1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Ryan Lortie <desrt@desrt.ca>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
35 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
38 #include "glib-unix.h"
42 #include "gthemedicon.h"
43 #include "gfileicon.h"
44 #include <glib/gstdio.h>
46 #include "giomodule-priv.h"
48 #include "gappinfoprivate.h"
49 #include "glocalfilemonitor.h"
52 #include "gdocumentportal.h"
56 * SECTION:gdesktopappinfo
57 * @title: GDesktopAppInfo
58 * @short_description: Application information from desktop files
59 * @include: gio/gdesktopappinfo.h
61 * #GDesktopAppInfo is an implementation of #GAppInfo based on
64 * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
65 * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
69 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
70 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
71 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
72 #define MIME_CACHE_GROUP "MIME Cache"
73 #define GENERIC_NAME_KEY "GenericName"
74 #define FULL_NAME_KEY "X-GNOME-FullName"
75 #define KEYWORDS_KEY "Keywords"
76 #define STARTUP_WM_CLASS_KEY "StartupWMClass"
83 static void g_desktop_app_info_iface_init (GAppInfoIface
*iface
);
84 static gboolean
g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
90 * Information about an installed application from a desktop file.
92 struct _GDesktopAppInfo
94 GObject parent_instance
;
116 char *startup_wm_class
;
123 guint startup_notify
: 1;
128 UPDATE_MIME_NONE
= 1 << 0,
129 UPDATE_MIME_SET_DEFAULT
= 1 << 1,
130 UPDATE_MIME_SET_NON_DEFAULT
= 1 << 2,
131 UPDATE_MIME_REMOVE
= 1 << 3,
132 UPDATE_MIME_SET_LAST_USED
= 1 << 4,
135 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo
, g_desktop_app_info
, G_TYPE_OBJECT
,
136 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO
, g_desktop_app_info_iface_init
))
138 /* DesktopFileDir implementation {{{1 */
143 gchar
*alternatively_watching
;
146 GFileMonitor
*monitor
;
147 GHashTable
*app_names
;
148 GHashTable
*mime_tweaks
;
149 GHashTable
*memory_index
;
150 GHashTable
*memory_implementations
;
153 static DesktopFileDir
*desktop_file_dirs
;
154 static guint n_desktop_file_dirs
;
155 static const guint desktop_file_dir_user_config_index
= 0;
156 static guint desktop_file_dir_user_data_index
;
157 static GMutex desktop_file_dir_lock
;
159 /* Monitor 'changed' signal handler {{{2 */
160 static void desktop_file_dir_reset (DesktopFileDir
*dir
);
163 * desktop_file_dir_get_alternative_dir:
164 * @dir: a #DesktopFileDir
166 * Gets the "alternative" directory to monitor in case the path
169 * If the path exists this will return NULL, otherwise it will return a
170 * parent directory of the path.
172 * This is used to avoid inotify on a non-existent directory (which
173 * results in polling).
175 * See https://bugzilla.gnome.org/show_bug.cgi?id=522314 for more info.
178 desktop_file_dir_get_alternative_dir (DesktopFileDir
*dir
)
182 /* If the directory itself exists then we need no alternative. */
183 if (g_access (dir
->path
, R_OK
| X_OK
) == 0)
186 /* Otherwise, try the parent directories until we find one. */
187 parent
= g_path_get_dirname (dir
->path
);
189 while (g_access (parent
, R_OK
| X_OK
) != 0)
193 parent
= g_path_get_dirname (tmp
);
195 /* If somehow we get to '/' or '.' then just stop... */
196 if (g_str_equal (parent
, tmp
))
209 desktop_file_dir_changed (GFileMonitor
*monitor
,
212 GFileMonitorEvent event_type
,
215 DesktopFileDir
*dir
= user_data
;
216 gboolean do_nothing
= FALSE
;
218 /* We are not interested in receiving notifications forever just
219 * because someone asked about one desktop file once.
221 * After we receive the first notification, reset the dir, destroying
222 * the monitor. We will take this as a hint, next time that we are
223 * asked, that we need to check if everything is up to date.
225 * If this is a notification for a parent directory (because the
226 * desktop directory didn't exist) then we shouldn't fire the signal
227 * unless something actually changed.
229 g_mutex_lock (&desktop_file_dir_lock
);
231 if (dir
->alternatively_watching
)
233 gchar
*alternative_dir
;
235 alternative_dir
= desktop_file_dir_get_alternative_dir (dir
);
236 do_nothing
= alternative_dir
&& g_str_equal (dir
->alternatively_watching
, alternative_dir
);
237 g_free (alternative_dir
);
241 desktop_file_dir_reset (dir
);
243 g_mutex_unlock (&desktop_file_dir_lock
);
245 /* Notify anyone else who may be interested */
247 g_app_info_monitor_fire ();
250 /* Internal utility functions {{{2 */
253 * desktop_file_dir_app_name_is_masked:
254 * @dir: a #DesktopFileDir
255 * @app_name: an application ID
257 * Checks if @app_name is masked for @dir.
259 * An application is masked if a similarly-named desktop file exists in
260 * a desktop file directory with higher precedence. Masked desktop
261 * files should be ignored.
264 desktop_file_dir_app_name_is_masked (DesktopFileDir
*dir
,
265 const gchar
*app_name
)
267 while (dir
> desktop_file_dirs
)
271 if (dir
->app_names
&& g_hash_table_contains (dir
->app_names
, app_name
))
278 static const gchar
* const *
279 get_lowercase_current_desktops (void)
281 static gchar
**result
;
283 if (g_once_init_enter (&result
))
288 envvar
= g_getenv ("XDG_CURRENT_DESKTOP");
294 tmp
= g_strsplit (envvar
, G_SEARCHPATH_SEPARATOR_S
, 0);
296 for (i
= 0; tmp
[i
]; i
++)
297 for (j
= 0; tmp
[i
][j
]; j
++)
298 tmp
[i
][j
] = g_ascii_tolower (tmp
[i
][j
]);
301 tmp
= g_new0 (gchar
*, 0 + 1);
303 g_once_init_leave (&result
, tmp
);
306 return (const gchar
**) result
;
309 static const gchar
* const *
310 get_current_desktops (const gchar
*value
)
312 static gchar
**result
;
314 if (g_once_init_enter (&result
))
319 value
= g_getenv ("XDG_CURRENT_DESKTOP");
324 tmp
= g_strsplit (value
, ":", 0);
326 g_once_init_leave (&result
, tmp
);
329 return (const gchar
**) result
;
333 * add_to_table_if_appropriate:
334 * @apps: a string to GDesktopAppInfo hash table
335 * @app_name: the name of the application
336 * @info: a #GDesktopAppInfo, or NULL
338 * If @info is non-%NULL and non-hidden, then add it to @apps, using
339 * @app_name as a key.
341 * If @info is non-%NULL then this function will consume the passed-in
345 add_to_table_if_appropriate (GHashTable
*apps
,
346 const gchar
*app_name
,
347 GDesktopAppInfo
*info
)
354 g_object_unref (info
);
358 g_free (info
->desktop_id
);
359 info
->desktop_id
= g_strdup (app_name
);
361 g_hash_table_insert (apps
, g_strdup (info
->desktop_id
), info
);
368 DESKTOP_KEY_GenericName
,
369 DESKTOP_KEY_Keywords
,
371 DESKTOP_KEY_X_GNOME_FullName
,
376 const gchar desktop_key_match_category
[N_DESKTOP_KEYS
] = {
377 /* Note: lower numbers are a better match.
379 * In case we want two keys to match at the same level, we can just
380 * use the same number for the two different keys.
382 [DESKTOP_KEY_Name
] = 1,
383 [DESKTOP_KEY_Exec
] = 2,
384 [DESKTOP_KEY_Keywords
] = 3,
385 [DESKTOP_KEY_GenericName
] = 4,
386 [DESKTOP_KEY_X_GNOME_FullName
] = 5,
387 [DESKTOP_KEY_Comment
] = 6
391 desktop_key_get_name (guint key_id
)
395 case DESKTOP_KEY_Comment
:
397 case DESKTOP_KEY_Exec
:
399 case DESKTOP_KEY_GenericName
:
400 return GENERIC_NAME_KEY
;
401 case DESKTOP_KEY_Keywords
:
403 case DESKTOP_KEY_Name
:
405 case DESKTOP_KEY_X_GNOME_FullName
:
406 return FULL_NAME_KEY
;
408 g_assert_not_reached ();
412 /* Search global state {{{2
414 * We only ever search under a global lock, so we can use (and reuse)
415 * some global data to reduce allocations made while searching.
417 * In short, we keep around arrays of results that we expand as needed
418 * (and never shrink).
420 * static_token_results: this is where we append the results for each
421 * token within a given desktop directory, as we handle it (which is
422 * a union of all matches for this term)
424 * static_search_results: this is where we build the complete results
425 * for a single directory (which is an intersection of the matches
426 * found for each term)
428 * static_total_results: this is where we build the complete results
429 * across all directories (which is a union of the matches found in
432 * The app_names that enter these tables are always pointer-unique (in
433 * the sense that string equality is the same as pointer equality).
434 * This can be guaranteed for two reasons:
436 * - we mask appids so that a given appid will only ever appear within
437 * the highest-precedence directory that contains it. We never
438 * return search results from a lower-level directory if a desktop
439 * file exists in a higher-level one.
441 * - within a given directory, the string is unique because it's the
442 * key in the hashtable of all app_ids for that directory.
444 * We perform a merging of the results in merge_token_results(). This
445 * works by ordering the two lists and moving through each of them (at
446 * the same time) looking for common elements, rejecting uncommon ones.
447 * "Order" here need not mean any particular thing, as long as it is
448 * some order. Because of the uniqueness of our strings, we can use
449 * pointer order. That's what's going on in compare_results() below.
453 const gchar
*app_name
;
457 static struct search_result
*static_token_results
;
458 static gint static_token_results_size
;
459 static gint static_token_results_allocated
;
460 static struct search_result
*static_search_results
;
461 static gint static_search_results_size
;
462 static gint static_search_results_allocated
;
463 static struct search_result
*static_total_results
;
464 static gint static_total_results_size
;
465 static gint static_total_results_allocated
;
467 /* And some functions for performing nice operations against it */
469 compare_results (gconstpointer a
,
472 const struct search_result
*ra
= a
;
473 const struct search_result
*rb
= b
;
475 if (ra
->app_name
< rb
->app_name
)
478 else if (ra
->app_name
> rb
->app_name
)
482 return ra
->category
- rb
->category
;
486 compare_categories (gconstpointer a
,
489 const struct search_result
*ra
= a
;
490 const struct search_result
*rb
= b
;
492 return ra
->category
- rb
->category
;
496 add_token_result (const gchar
*app_name
,
499 if G_UNLIKELY (static_token_results_size
== static_token_results_allocated
)
501 static_token_results_allocated
= MAX (16, static_token_results_allocated
* 2);
502 static_token_results
= g_renew (struct search_result
, static_token_results
, static_token_results_allocated
);
505 static_token_results
[static_token_results_size
].app_name
= app_name
;
506 static_token_results
[static_token_results_size
].category
= category
;
507 static_token_results_size
++;
511 merge_token_results (gboolean first
)
513 if (static_token_results_size
!= 0)
514 qsort (static_token_results
, static_token_results_size
, sizeof (struct search_result
), compare_results
);
516 /* If this is the first token then we are basically merging a list with
517 * itself -- we only perform de-duplication.
519 * If this is not the first token then we are doing a real merge.
523 const gchar
*last_name
= NULL
;
526 /* We must de-duplicate, but we do so by taking the best category
529 * The final list can be as large as the input here, so make sure
530 * we have enough room (even if it's too much room).
533 if G_UNLIKELY (static_search_results_allocated
< static_token_results_size
)
535 static_search_results_allocated
= static_token_results_allocated
;
536 static_search_results
= g_renew (struct search_result
,
537 static_search_results
,
538 static_search_results_allocated
);
541 for (i
= 0; i
< static_token_results_size
; i
++)
543 /* The list is sorted so that the best match for a given id
544 * will be at the front, so once we have copied an id, skip
545 * the rest of the entries for the same id.
547 if (static_token_results
[i
].app_name
== last_name
)
550 last_name
= static_token_results
[i
].app_name
;
552 static_search_results
[static_search_results_size
++] = static_token_results
[i
];
557 const gchar
*last_name
= NULL
;
561 /* We only ever remove items from the results list, so no need to
562 * resize to ensure that we have enough room.
564 for (i
= 0; i
< static_token_results_size
; i
++)
566 if (static_token_results
[i
].app_name
== last_name
)
569 last_name
= static_token_results
[i
].app_name
;
571 /* Now we only want to have a result in static_search_results
572 * if we already have it there *and* we have it in
573 * static_token_results as well. The category will be the
576 * Skip past the results in static_search_results that are not
577 * going to be matches.
579 while (k
< static_search_results_size
&&
580 static_search_results
[k
].app_name
< static_token_results
[i
].app_name
)
583 if (k
< static_search_results_size
&&
584 static_search_results
[k
].app_name
== static_token_results
[i
].app_name
)
588 * Category should be the worse of the two (ie:
589 * numerically larger).
591 static_search_results
[j
].app_name
= static_search_results
[k
].app_name
;
592 static_search_results
[j
].category
= MAX (static_search_results
[k
].category
,
593 static_token_results
[i
].category
);
598 static_search_results_size
= j
;
601 /* Clear it out for next time... */
602 static_token_results_size
= 0;
606 reset_total_search_results (void)
608 static_total_results_size
= 0;
612 sort_total_search_results (void)
614 if (static_total_results_size
!= 0)
615 qsort (static_total_results
, static_total_results_size
, sizeof (struct search_result
), compare_categories
);
619 merge_directory_results (void)
621 if G_UNLIKELY (static_total_results_size
+ static_search_results_size
> static_total_results_allocated
)
623 static_total_results_allocated
= MAX (16, static_total_results_allocated
);
624 while (static_total_results_allocated
< static_total_results_size
+ static_search_results_size
)
625 static_total_results_allocated
*= 2;
626 static_total_results
= g_renew (struct search_result
, static_total_results
, static_total_results_allocated
);
629 if (static_total_results
+ static_total_results_size
!= 0)
630 memcpy (static_total_results
+ static_total_results_size
,
631 static_search_results
,
632 static_search_results_size
* sizeof (struct search_result
));
634 static_total_results_size
+= static_search_results_size
;
636 /* Clear it out for next time... */
637 static_search_results_size
= 0;
640 /* Support for unindexed DesktopFileDirs {{{2 */
642 get_apps_from_dir (GHashTable
**apps
,
646 const char *basename
;
649 dir
= g_dir_open (dirname
, 0, NULL
);
654 while ((basename
= g_dir_read_name (dir
)) != NULL
)
658 filename
= g_build_filename (dirname
, basename
, NULL
);
660 if (g_str_has_suffix (basename
, ".desktop"))
664 app_name
= g_strconcat (prefix
, basename
, NULL
);
667 *apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
669 g_hash_table_insert (*apps
, app_name
, g_strdup (filename
));
671 else if (g_file_test (filename
, G_FILE_TEST_IS_DIR
))
675 subprefix
= g_strconcat (prefix
, basename
, "-", NULL
);
676 get_apps_from_dir (apps
, filename
, subprefix
);
691 } UnindexedMimeTweaks
;
694 free_mime_tweaks (gpointer data
)
696 UnindexedMimeTweaks
*tweaks
= data
;
698 g_strfreev (tweaks
->additions
);
699 g_strfreev (tweaks
->removals
);
700 g_strfreev (tweaks
->defaults
);
702 g_slice_free (UnindexedMimeTweaks
, tweaks
);
705 static UnindexedMimeTweaks
*
706 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir
*dir
,
707 const gchar
*mime_type
)
709 UnindexedMimeTweaks
*tweaks
;
710 gchar
*unaliased_type
;
712 unaliased_type
= _g_unix_content_type_unalias (mime_type
);
713 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, unaliased_type
);
717 tweaks
= g_slice_new0 (UnindexedMimeTweaks
);
718 g_hash_table_insert (dir
->mime_tweaks
, unaliased_type
, tweaks
);
721 g_free (unaliased_type
);
726 /* consumes 'to_add' */
728 expand_strv (gchar
***strv_ptr
,
730 gchar
* const *blacklist
)
732 guint strv_len
, add_len
;
743 strv_len
= g_strv_length (strv
);
744 add_len
= g_strv_length (to_add
);
745 strv
= g_renew (gchar
*, strv
, strv_len
+ add_len
+ 1);
747 for (i
= 0; to_add
[i
]; i
++)
749 /* Don't add blacklisted strings */
751 for (j
= 0; blacklist
[j
]; j
++)
752 if (g_str_equal (to_add
[i
], blacklist
[j
]))
755 /* Don't add duplicates already in the list */
756 for (j
= 0; j
< strv_len
; j
++)
757 if (g_str_equal (to_add
[i
], strv
[j
]))
760 strv
[strv_len
++] = to_add
[i
];
767 strv
[strv_len
] = NULL
;
774 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir
*dir
,
775 const gchar
*filename
,
776 const gchar
*added_group
,
777 gboolean tweaks_permitted
)
779 UnindexedMimeTweaks
*tweaks
;
780 char **desktop_file_ids
;
785 key_file
= g_key_file_new ();
786 if (!g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
))
788 g_key_file_free (key_file
);
792 mime_types
= g_key_file_get_keys (key_file
, added_group
, NULL
, NULL
);
794 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
796 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
797 "mimeapps.list file may add or remove associations.", filename
, added_group
);
798 g_strfreev (mime_types
);
802 if (mime_types
!= NULL
)
804 for (i
= 0; mime_types
[i
] != NULL
; i
++)
806 desktop_file_ids
= g_key_file_get_string_list (key_file
, added_group
, mime_types
[i
], NULL
, NULL
);
808 if (desktop_file_ids
)
810 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
811 expand_strv (&tweaks
->additions
, desktop_file_ids
, tweaks
->removals
);
815 g_strfreev (mime_types
);
818 mime_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
820 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
822 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
823 "mimeapps.list file may add or remove associations.", filename
, REMOVED_ASSOCIATIONS_GROUP
);
824 g_strfreev (mime_types
);
828 if (mime_types
!= NULL
)
830 for (i
= 0; mime_types
[i
] != NULL
; i
++)
832 desktop_file_ids
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
834 if (desktop_file_ids
)
836 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
837 expand_strv (&tweaks
->removals
, desktop_file_ids
, tweaks
->additions
);
841 g_strfreev (mime_types
);
844 mime_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
846 if (mime_types
!= NULL
)
848 for (i
= 0; mime_types
[i
] != NULL
; i
++)
850 desktop_file_ids
= g_key_file_get_string_list (key_file
, DEFAULT_APPLICATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
852 if (desktop_file_ids
)
854 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
855 expand_strv (&tweaks
->defaults
, desktop_file_ids
, NULL
);
859 g_strfreev (mime_types
);
862 g_key_file_free (key_file
);
866 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir
*dir
)
868 const gchar
* const *desktops
;
872 dir
->mime_tweaks
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, free_mime_tweaks
);
874 /* We process in order of precedence, using a blacklisting approach to
875 * avoid recording later instructions that conflict with ones we found
878 * We first start with the XDG_CURRENT_DESKTOP files, in precedence
881 desktops
= get_lowercase_current_desktops ();
882 for (i
= 0; desktops
[i
]; i
++)
884 filename
= g_strdup_printf ("%s/%s-mimeapps.list", dir
->path
, desktops
[i
]);
885 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
889 /* Next, the non-desktop-specific mimeapps.list */
890 filename
= g_strdup_printf ("%s/mimeapps.list", dir
->path
);
891 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, TRUE
);
894 /* The remaining files are only checked for in directories that might
895 * contain desktop files (ie: not the config dirs).
900 /* We have 'defaults.list' which was only ever understood by GLib. It
901 * exists widely, but it has never been part of any spec and it should
902 * be treated as deprecated. This will be removed in a future
905 filename
= g_strdup_printf ("%s/defaults.list", dir
->path
);
906 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
909 /* Finally, the mimeinfo.cache, which is just a cached copy of what we
910 * would find in the MimeTypes= lines of all of the desktop files.
912 filename
= g_strdup_printf ("%s/mimeinfo.cache", dir
->path
);
913 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, MIME_CACHE_GROUP
, TRUE
);
918 desktop_file_dir_unindexed_init (DesktopFileDir
*dir
)
921 get_apps_from_dir (&dir
->app_names
, dir
->path
, "");
923 desktop_file_dir_unindexed_read_mimeapps_lists (dir
);
926 static GDesktopAppInfo
*
927 desktop_file_dir_unindexed_get_app (DesktopFileDir
*dir
,
928 const gchar
*desktop_id
)
930 const gchar
*filename
;
932 filename
= g_hash_table_lookup (dir
->app_names
, desktop_id
);
937 return g_desktop_app_info_new_from_filename (filename
);
941 desktop_file_dir_unindexed_get_all (DesktopFileDir
*dir
,
948 if (dir
->app_names
== NULL
)
951 g_hash_table_iter_init (&iter
, dir
->app_names
);
952 while (g_hash_table_iter_next (&iter
, &app_name
, &filename
))
954 if (desktop_file_dir_app_name_is_masked (dir
, app_name
))
957 add_to_table_if_appropriate (apps
, app_name
, g_desktop_app_info_new_from_filename (filename
));
961 typedef struct _MemoryIndexEntry MemoryIndexEntry
;
962 typedef GHashTable MemoryIndex
;
964 struct _MemoryIndexEntry
966 const gchar
*app_name
; /* pointer to the hashtable key */
968 MemoryIndexEntry
*next
;
972 memory_index_entry_free (gpointer data
)
974 MemoryIndexEntry
*mie
= data
;
978 MemoryIndexEntry
*next
= mie
->next
;
980 g_slice_free (MemoryIndexEntry
, mie
);
986 memory_index_add_token (MemoryIndex
*mi
,
989 const gchar
*app_name
)
991 MemoryIndexEntry
*mie
, *first
;
993 mie
= g_slice_new (MemoryIndexEntry
);
994 mie
->app_name
= app_name
;
995 mie
->match_category
= match_category
;
997 first
= g_hash_table_lookup (mi
, token
);
1001 mie
->next
= first
->next
;
1007 g_hash_table_insert (mi
, g_strdup (token
), mie
);
1012 memory_index_add_string (MemoryIndex
*mi
,
1013 const gchar
*string
,
1014 gint match_category
,
1015 const gchar
*app_name
)
1017 gchar
**tokens
, **alternates
;
1020 tokens
= g_str_tokenize_and_fold (string
, NULL
, &alternates
);
1022 for (i
= 0; tokens
[i
]; i
++)
1023 memory_index_add_token (mi
, tokens
[i
], match_category
, app_name
);
1025 for (i
= 0; alternates
[i
]; i
++)
1026 memory_index_add_token (mi
, alternates
[i
], match_category
, app_name
);
1028 g_strfreev (alternates
);
1029 g_strfreev (tokens
);
1032 static MemoryIndex
*
1033 memory_index_new (void)
1035 return g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, memory_index_entry_free
);
1039 desktop_file_dir_unindexed_setup_search (DesktopFileDir
*dir
)
1041 GHashTableIter iter
;
1044 dir
->memory_index
= memory_index_new ();
1045 dir
->memory_implementations
= memory_index_new ();
1047 /* Nothing to search? */
1048 if (dir
->app_names
== NULL
)
1051 g_hash_table_iter_init (&iter
, dir
->app_names
);
1052 while (g_hash_table_iter_next (&iter
, &app
, &path
))
1056 if (desktop_file_dir_app_name_is_masked (dir
, app
))
1059 key_file
= g_key_file_new ();
1061 if (g_key_file_load_from_file (key_file
, path
, G_KEY_FILE_NONE
, NULL
) &&
1062 !g_key_file_get_boolean (key_file
, "Desktop Entry", "Hidden", NULL
))
1064 /* Index the interesting keys... */
1068 for (i
= 0; i
< G_N_ELEMENTS (desktop_key_match_category
); i
++)
1073 if (!desktop_key_match_category
[i
])
1076 raw
= g_key_file_get_locale_string (key_file
, "Desktop Entry", desktop_key_get_name (i
), NULL
, NULL
);
1079 if (i
== DESKTOP_KEY_Exec
&& raw
!= NULL
)
1081 /* Special handling: only match basename of first field */
1085 /* Remove extra arguments, if any */
1086 space
= raw
+ strcspn (raw
, " \t\n"); /* IFS */
1089 /* Skip the pathname, if any */
1090 if ((slash
= strrchr (raw
, '/')))
1095 memory_index_add_string (dir
->memory_index
, value
, desktop_key_match_category
[i
], app
);
1100 /* Make note of the Implements= line */
1101 implements
= g_key_file_get_string_list (key_file
, "Desktop Entry", "Implements", NULL
, NULL
);
1102 for (i
= 0; implements
&& implements
[i
]; i
++)
1103 memory_index_add_token (dir
->memory_implementations
, implements
[i
], 0, app
);
1104 g_strfreev (implements
);
1107 g_key_file_free (key_file
);
1112 desktop_file_dir_unindexed_search (DesktopFileDir
*dir
,
1113 const gchar
*search_token
)
1115 GHashTableIter iter
;
1116 gpointer key
, value
;
1118 if (!dir
->memory_index
)
1119 desktop_file_dir_unindexed_setup_search (dir
);
1121 g_hash_table_iter_init (&iter
, dir
->memory_index
);
1122 while (g_hash_table_iter_next (&iter
, &key
, &value
))
1124 MemoryIndexEntry
*mie
= value
;
1126 if (!g_str_has_prefix (key
, search_token
))
1131 add_token_result (mie
->app_name
, mie
->match_category
);
1138 array_contains (GPtrArray
*array
,
1143 for (i
= 0; i
< array
->len
; i
++)
1144 if (g_str_equal (array
->pdata
[i
], str
))
1151 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir
*dir
,
1152 const gchar
*mime_type
,
1154 GPtrArray
*blacklist
)
1156 UnindexedMimeTweaks
*tweaks
;
1159 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1164 if (tweaks
->additions
)
1166 for (i
= 0; tweaks
->additions
[i
]; i
++)
1168 gchar
*app_name
= tweaks
->additions
[i
];
1170 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1171 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1172 g_ptr_array_add (hits
, app_name
);
1176 if (tweaks
->removals
)
1178 for (i
= 0; tweaks
->removals
[i
]; i
++)
1180 gchar
*app_name
= tweaks
->removals
[i
];
1182 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1183 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1184 g_ptr_array_add (blacklist
, app_name
);
1190 desktop_file_dir_unindexed_default_lookup (DesktopFileDir
*dir
,
1191 const gchar
*mime_type
,
1194 UnindexedMimeTweaks
*tweaks
;
1197 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1199 if (!tweaks
|| !tweaks
->defaults
)
1202 for (i
= 0; tweaks
->defaults
[i
]; i
++)
1204 gchar
*app_name
= tweaks
->defaults
[i
];
1206 if (!array_contains (results
, app_name
))
1207 g_ptr_array_add (results
, app_name
);
1212 desktop_file_dir_unindexed_get_implementations (DesktopFileDir
*dir
,
1214 const gchar
*interface
)
1216 MemoryIndexEntry
*mie
;
1218 if (!dir
->memory_index
)
1219 desktop_file_dir_unindexed_setup_search (dir
);
1221 for (mie
= g_hash_table_lookup (dir
->memory_implementations
, interface
); mie
; mie
= mie
->next
)
1222 *results
= g_list_prepend (*results
, g_strdup (mie
->app_name
));
1225 /* DesktopFileDir "API" {{{2 */
1228 * desktop_file_dir_create:
1229 * @array: the #GArray to add a new item to
1230 * @data_dir: an XDG_DATA_DIR
1232 * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
1236 desktop_file_dir_create (GArray
*array
,
1237 const gchar
*data_dir
)
1239 DesktopFileDir dir
= { 0, };
1241 dir
.path
= g_build_filename (data_dir
, "applications", NULL
);
1243 g_array_append_val (array
, dir
);
1247 * desktop_file_dir_create:
1248 * @array: the #GArray to add a new item to
1249 * @config_dir: an XDG_CONFIG_DIR
1251 * Just the same as desktop_file_dir_create() except that it does not
1252 * add the "applications" directory. It also marks the directory as
1253 * config-only, which prevents us from attempting to find desktop files
1257 desktop_file_dir_create_for_config (GArray
*array
,
1258 const gchar
*config_dir
)
1260 DesktopFileDir dir
= { 0, };
1262 dir
.path
= g_strdup (config_dir
);
1263 dir
.is_config
= TRUE
;
1265 g_array_append_val (array
, dir
);
1269 * desktop_file_dir_reset:
1270 * @dir: a #DesktopFileDir
1272 * Cleans up @dir, releasing most resources that it was using.
1275 desktop_file_dir_reset (DesktopFileDir
*dir
)
1277 if (dir
->alternatively_watching
)
1279 g_free (dir
->alternatively_watching
);
1280 dir
->alternatively_watching
= NULL
;
1285 g_signal_handlers_disconnect_by_func (dir
->monitor
, desktop_file_dir_changed
, dir
);
1286 g_object_unref (dir
->monitor
);
1287 dir
->monitor
= NULL
;
1292 g_hash_table_unref (dir
->app_names
);
1293 dir
->app_names
= NULL
;
1296 if (dir
->memory_index
)
1298 g_hash_table_unref (dir
->memory_index
);
1299 dir
->memory_index
= NULL
;
1302 if (dir
->mime_tweaks
)
1304 g_hash_table_unref (dir
->mime_tweaks
);
1305 dir
->mime_tweaks
= NULL
;
1308 if (dir
->memory_implementations
)
1310 g_hash_table_unref (dir
->memory_implementations
);
1311 dir
->memory_implementations
= NULL
;
1314 dir
->is_setup
= FALSE
;
1318 * desktop_file_dir_init:
1319 * @dir: a #DesktopFileDir
1321 * Does initial setup for @dir
1323 * You should only call this if @dir is not already setup.
1326 desktop_file_dir_init (DesktopFileDir
*dir
)
1328 const gchar
*watch_dir
;
1330 g_assert (!dir
->is_setup
);
1332 g_assert (!dir
->alternatively_watching
);
1333 g_assert (!dir
->monitor
);
1335 dir
->alternatively_watching
= desktop_file_dir_get_alternative_dir (dir
);
1336 watch_dir
= dir
->alternatively_watching
? dir
->alternatively_watching
: dir
->path
;
1338 /* There is a very thin race here if the watch_dir has been _removed_
1339 * between when we checked for it and when we establish the watch.
1340 * Removes probably don't happen in usual operation, and even if it
1341 * does (and we catch the unlikely race), the only degradation is that
1342 * we will fall back to polling.
1344 dir
->monitor
= g_local_file_monitor_new_in_worker (watch_dir
, TRUE
, G_FILE_MONITOR_NONE
,
1345 desktop_file_dir_changed
, dir
, NULL
);
1347 desktop_file_dir_unindexed_init (dir
);
1349 dir
->is_setup
= TRUE
;
1353 * desktop_file_dir_get_app:
1354 * @dir: a DesktopFileDir
1355 * @desktop_id: the desktop ID to load
1357 * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1358 * within @dir, even if it is hidden.
1360 * This function does not check if @desktop_id would be masked by a
1361 * directory with higher precedence. The caller must do so.
1363 static GDesktopAppInfo
*
1364 desktop_file_dir_get_app (DesktopFileDir
*dir
,
1365 const gchar
*desktop_id
)
1367 if (!dir
->app_names
)
1370 return desktop_file_dir_unindexed_get_app (dir
, desktop_id
);
1374 * desktop_file_dir_get_all:
1375 * @dir: a DesktopFileDir
1376 * @apps: a #GHashTable<string, GDesktopAppInfo>
1378 * Loads all desktop files in @dir and adds them to @apps, careful to
1379 * ensure we don't add any files masked by a similarly-named file in a
1380 * higher-precedence directory.
1383 desktop_file_dir_get_all (DesktopFileDir
*dir
,
1386 desktop_file_dir_unindexed_get_all (dir
, apps
);
1390 * desktop_file_dir_mime_lookup:
1391 * @dir: a #DesktopFileDir
1392 * @mime_type: the mime type to look up
1393 * @hits: the array to store the hits
1394 * @blacklist: the array to store the blacklist
1396 * Does a lookup of a mimetype against one desktop file directory,
1397 * recording any hits and blacklisting and "Removed" associations (so
1398 * later directories don't record them as hits).
1400 * The items added to @hits are duplicated, but the ones in @blacklist
1401 * are weak pointers. This facilitates simply freeing the blacklist
1402 * (which is only used for internal bookkeeping) but using the pdata of
1403 * @hits as the result of the operation.
1406 desktop_file_dir_mime_lookup (DesktopFileDir
*dir
,
1407 const gchar
*mime_type
,
1409 GPtrArray
*blacklist
)
1411 desktop_file_dir_unindexed_mime_lookup (dir
, mime_type
, hits
, blacklist
);
1415 * desktop_file_dir_default_lookup:
1416 * @dir: a #DesktopFileDir
1417 * @mime_type: the mime type to look up
1418 * @results: an array to store the results in
1420 * Collects the "default" applications for a given mime type from @dir.
1423 desktop_file_dir_default_lookup (DesktopFileDir
*dir
,
1424 const gchar
*mime_type
,
1427 desktop_file_dir_unindexed_default_lookup (dir
, mime_type
, results
);
1431 * desktop_file_dir_search:
1432 * @dir: a #DesktopFileDir
1433 * @term: a normalised and casefolded search term
1435 * Finds the names of applications in @dir that match @term.
1438 desktop_file_dir_search (DesktopFileDir
*dir
,
1439 const gchar
*search_token
)
1441 desktop_file_dir_unindexed_search (dir
, search_token
);
1445 desktop_file_dir_get_implementations (DesktopFileDir
*dir
,
1447 const gchar
*interface
)
1449 desktop_file_dir_unindexed_get_implementations (dir
, results
, interface
);
1452 /* Lock/unlock and global setup API {{{2 */
1455 desktop_file_dirs_lock (void)
1459 g_mutex_lock (&desktop_file_dir_lock
);
1461 if (desktop_file_dirs
== NULL
)
1463 const char * const *dirs
;
1467 tmp
= g_array_new (FALSE
, FALSE
, sizeof (DesktopFileDir
));
1469 /* First, the configs. Highest priority: the user's ~/.config */
1470 desktop_file_dir_create_for_config (tmp
, g_get_user_config_dir ());
1472 /* Next, the system configs (/etc/xdg, and so on). */
1473 dirs
= g_get_system_config_dirs ();
1474 for (i
= 0; dirs
[i
]; i
++)
1475 desktop_file_dir_create_for_config (tmp
, dirs
[i
]);
1477 /* Now the data. Highest priority: the user's ~/.local/share/applications */
1478 desktop_file_dir_user_data_index
= tmp
->len
;
1479 desktop_file_dir_create (tmp
, g_get_user_data_dir ());
1481 /* Following that, XDG_DATA_DIRS/applications, in order */
1482 dirs
= g_get_system_data_dirs ();
1483 for (i
= 0; dirs
[i
]; i
++)
1484 desktop_file_dir_create (tmp
, dirs
[i
]);
1486 /* The list of directories will never change after this. */
1487 desktop_file_dirs
= (DesktopFileDir
*) tmp
->data
;
1488 n_desktop_file_dirs
= tmp
->len
;
1490 g_array_free (tmp
, FALSE
);
1493 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1494 if (!desktop_file_dirs
[i
].is_setup
)
1495 desktop_file_dir_init (&desktop_file_dirs
[i
]);
1499 desktop_file_dirs_unlock (void)
1501 g_mutex_unlock (&desktop_file_dir_lock
);
1505 desktop_file_dirs_invalidate_user_config (void)
1507 g_mutex_lock (&desktop_file_dir_lock
);
1509 if (n_desktop_file_dirs
)
1510 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_config_index
]);
1512 g_mutex_unlock (&desktop_file_dir_lock
);
1516 desktop_file_dirs_invalidate_user_data (void)
1518 g_mutex_lock (&desktop_file_dir_lock
);
1520 if (n_desktop_file_dirs
)
1521 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_data_index
]);
1523 g_mutex_unlock (&desktop_file_dir_lock
);
1526 /* GDesktopAppInfo implementation {{{1 */
1527 /* GObject implementation {{{2 */
1529 g_desktop_app_info_finalize (GObject
*object
)
1531 GDesktopAppInfo
*info
;
1533 info
= G_DESKTOP_APP_INFO (object
);
1535 g_free (info
->desktop_id
);
1536 g_free (info
->filename
);
1539 g_key_file_unref (info
->keyfile
);
1541 g_free (info
->name
);
1542 g_free (info
->generic_name
);
1543 g_free (info
->fullname
);
1544 g_free (info
->comment
);
1545 g_free (info
->icon_name
);
1547 g_object_unref (info
->icon
);
1548 g_strfreev (info
->keywords
);
1549 g_strfreev (info
->only_show_in
);
1550 g_strfreev (info
->not_show_in
);
1551 g_free (info
->try_exec
);
1552 g_free (info
->exec
);
1553 g_free (info
->binary
);
1554 g_free (info
->path
);
1555 g_free (info
->categories
);
1556 g_free (info
->startup_wm_class
);
1557 g_strfreev (info
->mime_types
);
1558 g_free (info
->app_id
);
1559 g_strfreev (info
->actions
);
1561 G_OBJECT_CLASS (g_desktop_app_info_parent_class
)->finalize (object
);
1565 g_desktop_app_info_set_property (GObject
*object
,
1567 const GValue
*value
,
1570 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1575 self
->filename
= g_value_dup_string (value
);
1579 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1585 g_desktop_app_info_get_property (GObject
*object
,
1590 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1595 g_value_set_string (value
, self
->filename
);
1598 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1604 g_desktop_app_info_class_init (GDesktopAppInfoClass
*klass
)
1606 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
1608 gobject_class
->get_property
= g_desktop_app_info_get_property
;
1609 gobject_class
->set_property
= g_desktop_app_info_set_property
;
1610 gobject_class
->finalize
= g_desktop_app_info_finalize
;
1613 * GDesktopAppInfo:filename:
1615 * The origin filename of this #GDesktopAppInfo
1617 g_object_class_install_property (gobject_class
,
1619 g_param_spec_string ("filename", "Filename", "", NULL
,
1620 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1624 g_desktop_app_info_init (GDesktopAppInfo
*local
)
1628 /* Construction... {{{2 */
1632 * @exec: an exec line
1634 * Returns the first word in an exec line (ie: the binary name).
1636 * If @exec is " progname --foo %F" then returns "progname".
1639 binary_from_exec (const char *exec
)
1641 const char *p
, *start
;
1647 while (*p
!= ' ' && *p
!= 0)
1650 return g_strndup (start
, p
- start
);
1654 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo
*info
,
1661 gboolean bus_activatable
;
1663 start_group
= g_key_file_get_start_group (key_file
);
1664 if (start_group
== NULL
|| strcmp (start_group
, G_KEY_FILE_DESKTOP_GROUP
) != 0)
1666 g_free (start_group
);
1669 g_free (start_group
);
1671 type
= g_key_file_get_string (key_file
,
1672 G_KEY_FILE_DESKTOP_GROUP
,
1673 G_KEY_FILE_DESKTOP_KEY_TYPE
,
1675 if (type
== NULL
|| strcmp (type
, G_KEY_FILE_DESKTOP_TYPE_APPLICATION
) != 0)
1682 try_exec
= g_key_file_get_string (key_file
,
1683 G_KEY_FILE_DESKTOP_GROUP
,
1684 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC
,
1686 if (try_exec
&& try_exec
[0] != '\0')
1689 t
= g_find_program_in_path (try_exec
);
1698 exec
= g_key_file_get_string (key_file
,
1699 G_KEY_FILE_DESKTOP_GROUP
,
1700 G_KEY_FILE_DESKTOP_KEY_EXEC
,
1702 if (exec
&& exec
[0] != '\0')
1706 if (!g_shell_parse_argv (exec
, &argc
, &argv
, NULL
))
1715 t
= g_find_program_in_path (argv
[0]);
1728 info
->name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NAME
, NULL
, NULL
);
1729 info
->generic_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, GENERIC_NAME_KEY
, NULL
, NULL
);
1730 info
->fullname
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, FULL_NAME_KEY
, NULL
, NULL
);
1731 info
->keywords
= g_key_file_get_locale_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, KEYWORDS_KEY
, NULL
, NULL
, NULL
);
1732 info
->comment
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_COMMENT
, NULL
, NULL
);
1733 info
->nodisplay
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, NULL
) != FALSE
;
1734 info
->icon_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ICON
, NULL
, NULL
);
1735 info
->only_show_in
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN
, NULL
, NULL
);
1736 info
->not_show_in
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN
, NULL
, NULL
);
1737 info
->try_exec
= try_exec
;
1739 info
->path
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_PATH
, NULL
);
1740 info
->terminal
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_TERMINAL
, NULL
) != FALSE
;
1741 info
->startup_notify
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY
, NULL
) != FALSE
;
1742 info
->no_fuse
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, "X-GIO-NoFuse", NULL
) != FALSE
;
1743 info
->hidden
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_HIDDEN
, NULL
) != FALSE
;
1744 info
->categories
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_CATEGORIES
, NULL
);
1745 info
->startup_wm_class
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, STARTUP_WM_CLASS_KEY
, NULL
);
1746 info
->mime_types
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE
, NULL
, NULL
);
1747 bus_activatable
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE
, NULL
);
1748 info
->actions
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ACTIONS
, NULL
, NULL
);
1750 /* Remove the special-case: no Actions= key just means 0 extra actions */
1751 if (info
->actions
== NULL
)
1752 info
->actions
= g_new0 (gchar
*, 0 + 1);
1755 if (info
->icon_name
)
1757 if (g_path_is_absolute (info
->icon_name
))
1761 file
= g_file_new_for_path (info
->icon_name
);
1762 info
->icon
= g_file_icon_new (file
);
1763 g_object_unref (file
);
1769 /* Work around a common mistake in desktop files */
1770 if ((p
= strrchr (info
->icon_name
, '.')) != NULL
&&
1771 (strcmp (p
, ".png") == 0 ||
1772 strcmp (p
, ".xpm") == 0 ||
1773 strcmp (p
, ".svg") == 0))
1776 info
->icon
= g_themed_icon_new (info
->icon_name
);
1781 info
->binary
= binary_from_exec (info
->exec
);
1783 if (info
->path
&& info
->path
[0] == '\0')
1785 g_free (info
->path
);
1789 /* Can only be DBusActivatable if we know the filename, which means
1790 * that this won't work for the load-from-keyfile case.
1792 if (bus_activatable
&& info
->filename
)
1797 basename
= g_path_get_basename (info
->filename
);
1798 last_dot
= strrchr (basename
, '.');
1800 if (last_dot
&& g_str_equal (last_dot
, ".desktop"))
1804 if (g_dbus_is_name (basename
) && basename
[0] != ':')
1805 info
->app_id
= g_strdup (basename
);
1811 info
->keyfile
= g_key_file_ref (key_file
);
1817 g_desktop_app_info_load_file (GDesktopAppInfo
*self
)
1820 gboolean retval
= FALSE
;
1822 g_return_val_if_fail (self
->filename
!= NULL
, FALSE
);
1824 self
->desktop_id
= g_path_get_basename (self
->filename
);
1826 key_file
= g_key_file_new ();
1828 if (g_key_file_load_from_file (key_file
, self
->filename
, G_KEY_FILE_NONE
, NULL
))
1829 retval
= g_desktop_app_info_load_from_keyfile (self
, key_file
);
1831 g_key_file_unref (key_file
);
1836 * g_desktop_app_info_new_from_keyfile:
1837 * @key_file: an opened #GKeyFile
1839 * Creates a new #GDesktopAppInfo.
1841 * Returns: a new #GDesktopAppInfo or %NULL on error.
1846 g_desktop_app_info_new_from_keyfile (GKeyFile
*key_file
)
1848 GDesktopAppInfo
*info
;
1850 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1851 info
->filename
= NULL
;
1852 if (!g_desktop_app_info_load_from_keyfile (info
, key_file
))
1854 g_object_unref (info
);
1861 * g_desktop_app_info_new_from_filename:
1862 * @filename: (type filename): the path of a desktop file, in the GLib
1865 * Creates a new #GDesktopAppInfo.
1867 * Returns: a new #GDesktopAppInfo or %NULL on error.
1870 g_desktop_app_info_new_from_filename (const char *filename
)
1872 GDesktopAppInfo
*info
= NULL
;
1874 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, "filename", filename
, NULL
);
1875 if (!g_desktop_app_info_load_file (info
))
1877 g_object_unref (info
);
1884 * g_desktop_app_info_new:
1885 * @desktop_id: the desktop file id
1887 * Creates a new #GDesktopAppInfo based on a desktop file id.
1889 * A desktop file id is the basename of the desktop file, including the
1890 * .desktop extension. GIO is looking for a desktop file with this name
1891 * in the `applications` subdirectories of the XDG
1892 * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1893 * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1894 * prefix-to-subdirectory mapping that is described in the
1895 * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1896 * (i.e. a desktop id of kde-foo.desktop will match
1897 * `/usr/share/applications/kde/foo.desktop`).
1899 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
1902 g_desktop_app_info_new (const char *desktop_id
)
1904 GDesktopAppInfo
*appinfo
= NULL
;
1907 desktop_file_dirs_lock ();
1909 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1911 appinfo
= desktop_file_dir_get_app (&desktop_file_dirs
[i
], desktop_id
);
1917 desktop_file_dirs_unlock ();
1919 if (appinfo
== NULL
)
1922 g_free (appinfo
->desktop_id
);
1923 appinfo
->desktop_id
= g_strdup (desktop_id
);
1925 if (g_desktop_app_info_get_is_hidden (appinfo
))
1927 g_object_unref (appinfo
);
1935 g_desktop_app_info_dup (GAppInfo
*appinfo
)
1937 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1938 GDesktopAppInfo
*new_info
;
1940 new_info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1942 new_info
->filename
= g_strdup (info
->filename
);
1943 new_info
->desktop_id
= g_strdup (info
->desktop_id
);
1946 new_info
->keyfile
= g_key_file_ref (info
->keyfile
);
1948 new_info
->name
= g_strdup (info
->name
);
1949 new_info
->generic_name
= g_strdup (info
->generic_name
);
1950 new_info
->fullname
= g_strdup (info
->fullname
);
1951 new_info
->keywords
= g_strdupv (info
->keywords
);
1952 new_info
->comment
= g_strdup (info
->comment
);
1953 new_info
->nodisplay
= info
->nodisplay
;
1954 new_info
->icon_name
= g_strdup (info
->icon_name
);
1956 new_info
->icon
= g_object_ref (info
->icon
);
1957 new_info
->only_show_in
= g_strdupv (info
->only_show_in
);
1958 new_info
->not_show_in
= g_strdupv (info
->not_show_in
);
1959 new_info
->try_exec
= g_strdup (info
->try_exec
);
1960 new_info
->exec
= g_strdup (info
->exec
);
1961 new_info
->binary
= g_strdup (info
->binary
);
1962 new_info
->path
= g_strdup (info
->path
);
1963 new_info
->app_id
= g_strdup (info
->app_id
);
1964 new_info
->hidden
= info
->hidden
;
1965 new_info
->terminal
= info
->terminal
;
1966 new_info
->startup_notify
= info
->startup_notify
;
1968 return G_APP_INFO (new_info
);
1971 /* GAppInfo interface implementation functions {{{2 */
1974 g_desktop_app_info_equal (GAppInfo
*appinfo1
,
1977 GDesktopAppInfo
*info1
= G_DESKTOP_APP_INFO (appinfo1
);
1978 GDesktopAppInfo
*info2
= G_DESKTOP_APP_INFO (appinfo2
);
1980 if (info1
->desktop_id
== NULL
||
1981 info2
->desktop_id
== NULL
)
1982 return info1
== info2
;
1984 return strcmp (info1
->desktop_id
, info2
->desktop_id
) == 0;
1988 g_desktop_app_info_get_id (GAppInfo
*appinfo
)
1990 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1992 return info
->desktop_id
;
1996 g_desktop_app_info_get_name (GAppInfo
*appinfo
)
1998 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2000 if (info
->name
== NULL
)
2001 return _("Unnamed");
2006 g_desktop_app_info_get_display_name (GAppInfo
*appinfo
)
2008 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2010 if (info
->fullname
== NULL
)
2011 return g_desktop_app_info_get_name (appinfo
);
2012 return info
->fullname
;
2016 * g_desktop_app_info_get_is_hidden:
2017 * @info: a #GDesktopAppInfo.
2019 * A desktop file is hidden if the Hidden key in it is
2022 * Returns: %TRUE if hidden, %FALSE otherwise.
2025 g_desktop_app_info_get_is_hidden (GDesktopAppInfo
*info
)
2027 return info
->hidden
;
2031 * g_desktop_app_info_get_filename:
2032 * @info: a #GDesktopAppInfo
2034 * When @info was created from a known filename, return it. In some
2035 * situations such as the #GDesktopAppInfo returned from
2036 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
2038 * Returns: (type filename): The full path to the file for @info,
2039 * or %NULL if not known.
2043 g_desktop_app_info_get_filename (GDesktopAppInfo
*info
)
2045 return info
->filename
;
2049 g_desktop_app_info_get_description (GAppInfo
*appinfo
)
2051 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2053 return info
->comment
;
2057 g_desktop_app_info_get_executable (GAppInfo
*appinfo
)
2059 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2061 return info
->binary
;
2065 g_desktop_app_info_get_commandline (GAppInfo
*appinfo
)
2067 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2073 g_desktop_app_info_get_icon (GAppInfo
*appinfo
)
2075 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2081 * g_desktop_app_info_get_categories:
2082 * @info: a #GDesktopAppInfo
2084 * Gets the categories from the desktop file.
2086 * Returns: The unparsed Categories key from the desktop file;
2087 * i.e. no attempt is made to split it by ';' or validate it.
2090 g_desktop_app_info_get_categories (GDesktopAppInfo
*info
)
2092 return info
->categories
;
2096 * g_desktop_app_info_get_keywords:
2097 * @info: a #GDesktopAppInfo
2099 * Gets the keywords from the desktop file.
2101 * Returns: (transfer none): The value of the Keywords key
2105 const char * const *
2106 g_desktop_app_info_get_keywords (GDesktopAppInfo
*info
)
2108 return (const char * const *)info
->keywords
;
2112 * g_desktop_app_info_get_generic_name:
2113 * @info: a #GDesktopAppInfo
2115 * Gets the generic name from the destkop file.
2117 * Returns: The value of the GenericName key
2120 g_desktop_app_info_get_generic_name (GDesktopAppInfo
*info
)
2122 return info
->generic_name
;
2126 * g_desktop_app_info_get_nodisplay:
2127 * @info: a #GDesktopAppInfo
2129 * Gets the value of the NoDisplay key, which helps determine if the
2130 * application info should be shown in menus. See
2131 * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2133 * Returns: The value of the NoDisplay key
2138 g_desktop_app_info_get_nodisplay (GDesktopAppInfo
*info
)
2140 return info
->nodisplay
;
2144 * g_desktop_app_info_get_show_in:
2145 * @info: a #GDesktopAppInfo
2146 * @desktop_env: (nullable): a string specifying a desktop name
2148 * Checks if the application info should be shown in menus that list available
2149 * applications for a specific name of the desktop, based on the
2150 * `OnlyShowIn` and `NotShowIn` keys.
2152 * @desktop_env should typically be given as %NULL, in which case the
2153 * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
2154 * to override the default mechanism then you may specify @desktop_env,
2155 * but this is not recommended.
2157 * Note that g_app_info_should_show() for @info will include this check (with
2158 * %NULL for @desktop_env) as well as additional checks.
2160 * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2161 * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2167 g_desktop_app_info_get_show_in (GDesktopAppInfo
*info
,
2168 const gchar
*desktop_env
)
2170 const gchar
*specified_envs
[] = { desktop_env
, NULL
};
2171 const gchar
* const *envs
;
2174 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
2177 envs
= specified_envs
;
2179 envs
= get_current_desktops (NULL
);
2181 for (i
= 0; envs
[i
]; i
++)
2185 if (info
->only_show_in
)
2186 for (j
= 0; info
->only_show_in
[j
]; j
++)
2187 if (g_str_equal (info
->only_show_in
[j
], envs
[i
]))
2190 if (info
->not_show_in
)
2191 for (j
= 0; info
->not_show_in
[j
]; j
++)
2192 if (g_str_equal (info
->not_show_in
[j
], envs
[i
]))
2196 return info
->only_show_in
== NULL
;
2199 /* Launching... {{{2 */
2202 expand_macro_single (char macro
, const char *uri
)
2205 char *result
= NULL
;
2209 file
= g_file_new_for_uri (uri
);
2215 result
= g_shell_quote (uri
);
2219 path
= g_file_get_path (file
);
2221 result
= g_shell_quote (path
);
2225 path
= g_file_get_path (file
);
2228 name
= g_path_get_dirname (path
);
2229 result
= g_shell_quote (name
);
2235 path
= g_file_get_path (file
);
2238 name
= g_path_get_basename (path
);
2239 result
= g_shell_quote (name
);
2245 g_object_unref (file
);
2252 expand_macro_uri (char macro
, const char *uri
, gboolean force_file_uri
, char force_file_uri_macro
)
2254 char *expanded
= NULL
;
2256 g_return_val_if_fail (uri
!= NULL
, NULL
);
2258 if (!force_file_uri
||
2259 /* Pass URI if it contains an anchor */
2260 strchr (uri
, '#') != NULL
)
2262 expanded
= expand_macro_single (macro
, uri
);
2266 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2267 if (expanded
== NULL
)
2268 expanded
= expand_macro_single (macro
, uri
);
2275 expand_macro (char macro
,
2277 GDesktopAppInfo
*info
,
2280 GList
*uris
= *uri_list
;
2281 char *expanded
= NULL
;
2282 gboolean force_file_uri
;
2283 char force_file_uri_macro
;
2286 g_return_if_fail (exec
!= NULL
);
2288 /* On %u and %U, pass POSIX file path pointing to the URI via
2289 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2290 * running or the URI doesn't have a POSIX file path via FUSE
2291 * we'll just pass the URI.
2293 force_file_uri_macro
= macro
;
2294 force_file_uri
= FALSE
;
2300 force_file_uri_macro
= 'f';
2301 force_file_uri
= TRUE
;
2304 force_file_uri_macro
= 'F';
2305 force_file_uri
= TRUE
;
2321 expanded
= expand_macro_uri (macro
, uri
,
2322 force_file_uri
, force_file_uri_macro
);
2325 g_string_append (exec
, expanded
);
2340 expanded
= expand_macro_uri (macro
, uri
,
2341 force_file_uri
, force_file_uri_macro
);
2344 g_string_append (exec
, expanded
);
2350 if (uris
!= NULL
&& expanded
)
2351 g_string_append_c (exec
, ' ');
2357 if (info
->icon_name
)
2359 g_string_append (exec
, "--icon ");
2360 expanded
= g_shell_quote (info
->icon_name
);
2361 g_string_append (exec
, expanded
);
2369 expanded
= g_shell_quote (info
->name
);
2370 g_string_append (exec
, expanded
);
2378 expanded
= g_shell_quote (info
->filename
);
2379 g_string_append (exec
, expanded
);
2384 case 'm': /* deprecated */
2388 g_string_append_c (exec
, '%');
2396 expand_application_parameters (GDesktopAppInfo
*info
,
2397 const gchar
*exec_line
,
2403 GList
*uri_list
= *uris
;
2404 const char *p
= exec_line
;
2405 GString
*expanded_exec
;
2408 if (exec_line
== NULL
)
2410 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2411 _("Desktop file didn’t specify Exec field"));
2415 expanded_exec
= g_string_new (NULL
);
2419 if (p
[0] == '%' && p
[1] != '\0')
2421 expand_macro (p
[1], expanded_exec
, info
, uris
);
2425 g_string_append_c (expanded_exec
, *p
);
2430 /* No file substitutions */
2431 if (uri_list
== *uris
&& uri_list
!= NULL
)
2433 /* If there is no macro default to %f. This is also what KDE does */
2434 g_string_append_c (expanded_exec
, ' ');
2435 expand_macro ('f', expanded_exec
, info
, uris
);
2438 res
= g_shell_parse_argv (expanded_exec
->str
, argc
, argv
, error
);
2439 g_string_free (expanded_exec
, TRUE
);
2444 prepend_terminal_to_vector (int *argc
,
2451 char **term_argv
= NULL
;
2456 g_return_val_if_fail (argc
!= NULL
, FALSE
);
2457 g_return_val_if_fail (argv
!= NULL
, FALSE
);
2465 /* compute size if not given */
2468 for (i
= 0; the_argv
[i
] != NULL
; i
++)
2474 term_argv
= g_new0 (char *, 3);
2476 check
= g_find_program_in_path ("gnome-terminal");
2479 term_argv
[0] = check
;
2480 /* Note that gnome-terminal takes -x and
2481 * as -e in gnome-terminal is broken we use that. */
2482 term_argv
[1] = g_strdup ("-x");
2487 check
= g_find_program_in_path ("nxterm");
2489 check
= g_find_program_in_path ("color-xterm");
2491 check
= g_find_program_in_path ("rxvt");
2493 check
= g_find_program_in_path ("dtterm");
2496 check
= g_strdup ("xterm");
2497 g_debug ("Couldn’t find a terminal: falling back to xterm");
2499 term_argv
[0] = check
;
2500 term_argv
[1] = g_strdup ("-e");
2503 real_argc
= term_argc
+ *argc
;
2504 real_argv
= g_new (char *, real_argc
+ 1);
2506 for (i
= 0; i
< term_argc
; i
++)
2507 real_argv
[i
] = term_argv
[i
];
2509 for (j
= 0; j
< *argc
; j
++, i
++)
2510 real_argv
[i
] = (char *)the_argv
[j
];
2512 real_argv
[i
] = NULL
;
2518 /* we use g_free here as we sucked all the inner strings
2519 * out from it into real_argv */
2524 #endif /* G_OS_WIN32 */
2528 create_files_for_uris (GList
*uris
)
2535 for (iter
= uris
; iter
; iter
= iter
->next
)
2537 GFile
*file
= g_file_new_for_uri ((char *)iter
->data
);
2538 res
= g_list_prepend (res
, file
);
2541 return g_list_reverse (res
);
2546 GSpawnChildSetupFunc user_setup
;
2547 gpointer user_setup_data
;
2553 child_setup (gpointer user_data
)
2555 ChildSetupData
*data
= user_data
;
2557 if (data
->pid_envvar
)
2559 pid_t pid
= getpid ();
2563 /* Write the pid into the space already reserved for it in the
2564 * environment array. We can't use sprintf because it might
2565 * malloc, so we do it by hand. It's simplest to write the pid
2566 * out backwards first, then copy it over.
2568 for (i
= 0; pid
; i
++, pid
/= 10)
2569 buf
[i
] = (pid
% 10) + '0';
2570 for (i
--; i
>= 0; i
--)
2571 *(data
->pid_envvar
++) = buf
[i
];
2572 *data
->pid_envvar
= '\0';
2575 if (data
->user_setup
)
2576 data
->user_setup (data
->user_setup_data
);
2580 notify_desktop_launch (GDBusConnection
*session_bus
,
2581 GDesktopAppInfo
*info
,
2583 const char *display
,
2588 GVariantBuilder uri_variant
;
2589 GVariantBuilder extras_variant
;
2591 const char *desktop_file_id
;
2592 const char *gio_desktop_file
;
2594 if (session_bus
== NULL
)
2597 g_variant_builder_init (&uri_variant
, G_VARIANT_TYPE ("as"));
2598 for (iter
= uris
; iter
; iter
= iter
->next
)
2599 g_variant_builder_add (&uri_variant
, "s", iter
->data
);
2601 g_variant_builder_init (&extras_variant
, G_VARIANT_TYPE ("a{sv}"));
2602 if (sn_id
!= NULL
&& g_utf8_validate (sn_id
, -1, NULL
))
2603 g_variant_builder_add (&extras_variant
, "{sv}",
2607 gio_desktop_file
= g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2608 if (gio_desktop_file
!= NULL
)
2609 g_variant_builder_add (&extras_variant
, "{sv}",
2610 "origin-desktop-file",
2611 g_variant_new_bytestring (gio_desktop_file
));
2612 if (g_get_prgname () != NULL
)
2613 g_variant_builder_add (&extras_variant
, "{sv}",
2615 g_variant_new_bytestring (g_get_prgname ()));
2616 g_variant_builder_add (&extras_variant
, "{sv}",
2619 (gint64
)getpid ()));
2622 desktop_file_id
= info
->filename
;
2623 else if (info
->desktop_id
)
2624 desktop_file_id
= info
->desktop_id
;
2626 desktop_file_id
= "";
2628 msg
= g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2629 "org.gtk.gio.DesktopAppInfo",
2631 g_dbus_message_set_body (msg
, g_variant_new ("(@aysxasa{sv})",
2632 g_variant_new_bytestring (desktop_file_id
),
2633 display
? display
: "",
2637 g_dbus_connection_send_message (session_bus
,
2641 g_object_unref (msg
);
2644 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2647 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo
*info
,
2648 GDBusConnection
*session_bus
,
2649 const gchar
*exec_line
,
2651 GAppLaunchContext
*launch_context
,
2652 GSpawnFlags spawn_flags
,
2653 GSpawnChildSetupFunc user_setup
,
2654 gpointer user_setup_data
,
2655 GDesktopAppLaunchCallback pid_callback
,
2656 gpointer pid_callback_data
,
2659 gboolean completed
= FALSE
;
2663 char **argv
, **envp
;
2665 ChildSetupData data
;
2667 g_return_val_if_fail (info
!= NULL
, FALSE
);
2672 envp
= g_app_launch_context_get_environment (launch_context
);
2674 envp
= g_get_environ ();
2676 /* The GList* passed to expand_application_parameters() will be modified
2677 * internally by expand_macro(), so we need to pass a copy of it instead,
2678 * and also use that copy to control the exit condition of the loop below.
2680 dup_uris
= g_list_copy (uris
);
2684 GList
*launched_uris
;
2688 old_uris
= dup_uris
;
2689 if (!expand_application_parameters (info
, exec_line
, &dup_uris
, &argc
, &argv
, error
))
2692 /* Get the subset of URIs we're launching with this process */
2693 launched_uris
= NULL
;
2694 for (iter
= old_uris
; iter
!= NULL
&& iter
!= dup_uris
; iter
= iter
->next
)
2695 launched_uris
= g_list_prepend (launched_uris
, iter
->data
);
2696 launched_uris
= g_list_reverse (launched_uris
);
2698 if (info
->terminal
&& !prepend_terminal_to_vector (&argc
, &argv
))
2700 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2701 _("Unable to find terminal required for application"));
2705 data
.user_setup
= user_setup
;
2706 data
.user_setup_data
= user_setup_data
;
2710 envp
= g_environ_setenv (envp
,
2711 "GIO_LAUNCHED_DESKTOP_FILE",
2714 envp
= g_environ_setenv (envp
,
2715 "GIO_LAUNCHED_DESKTOP_FILE_PID",
2716 "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
2718 data
.pid_envvar
= (char *)g_environ_getenv (envp
, "GIO_LAUNCHED_DESKTOP_FILE_PID");
2722 data
.pid_envvar
= NULL
;
2728 GList
*launched_files
= create_files_for_uris (launched_uris
);
2730 if (info
->startup_notify
)
2732 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
,
2736 envp
= g_environ_setenv (envp
, "DESKTOP_STARTUP_ID", sn_id
, TRUE
);
2739 g_list_free_full (launched_files
, g_object_unref
);
2742 if (!g_spawn_async (info
->path
,
2752 g_app_launch_context_launch_failed (launch_context
, sn_id
);
2755 g_list_free (launched_uris
);
2760 if (pid_callback
!= NULL
)
2761 pid_callback (info
, pid
, pid_callback_data
);
2763 if (launch_context
!= NULL
)
2765 GVariantBuilder builder
;
2766 GVariant
*platform_data
;
2768 g_variant_builder_init (&builder
, G_VARIANT_TYPE_ARRAY
);
2769 g_variant_builder_add (&builder
, "{sv}", "pid", g_variant_new_int32 (pid
));
2771 g_variant_builder_add (&builder
, "{sv}", "startup-notification-id", g_variant_new_string (sn_id
));
2772 platform_data
= g_variant_ref_sink (g_variant_builder_end (&builder
));
2773 g_signal_emit_by_name (launch_context
, "launched", info
, platform_data
);
2774 g_variant_unref (platform_data
);
2777 notify_desktop_launch (session_bus
,
2785 g_list_free (launched_uris
);
2790 while (dup_uris
!= NULL
);
2795 g_list_free (dup_uris
);
2803 object_path_from_appid (const gchar
*appid
)
2805 gchar
*appid_path
, *iter
;
2807 appid_path
= g_strconcat ("/", appid
, NULL
);
2808 for (iter
= appid_path
; *iter
; iter
++)
2821 g_desktop_app_info_make_platform_data (GDesktopAppInfo
*info
,
2823 GAppLaunchContext
*launch_context
)
2825 GVariantBuilder builder
;
2827 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
2831 GList
*launched_files
= create_files_for_uris (uris
);
2833 if (info
->startup_notify
)
2837 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
, G_APP_INFO (info
), launched_files
);
2839 g_variant_builder_add (&builder
, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id
));
2842 g_list_free_full (launched_files
, g_object_unref
);
2845 return g_variant_builder_end (&builder
);
2849 launch_uris_with_dbus (GDesktopAppInfo
*info
,
2850 GDBusConnection
*session_bus
,
2852 GAppLaunchContext
*launch_context
)
2854 GVariantBuilder builder
;
2857 g_variant_builder_init (&builder
, G_VARIANT_TYPE_TUPLE
);
2863 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
2864 for (iter
= uris
; iter
; iter
= iter
->next
)
2865 g_variant_builder_add (&builder
, "s", iter
->data
);
2866 g_variant_builder_close (&builder
);
2869 g_variant_builder_add_value (&builder
, g_desktop_app_info_make_platform_data (info
, uris
, launch_context
));
2871 /* This is non-blocking API. Similar to launching via fork()/exec()
2872 * we don't wait around to see if the program crashed during startup.
2873 * This is what startup-notification's job is...
2875 object_path
= object_path_from_appid (info
->app_id
);
2876 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
, "org.freedesktop.Application",
2877 uris
? "Open" : "Activate", g_variant_builder_end (&builder
),
2878 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
2879 g_free (object_path
);
2883 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo
*info
,
2884 GDBusConnection
*session_bus
,
2886 GAppLaunchContext
*launch_context
)
2888 GList
*ruris
= uris
;
2889 char *app_id
= NULL
;
2891 g_return_val_if_fail (info
!= NULL
, FALSE
);
2894 app_id
= g_desktop_app_info_get_string (info
, "X-Flatpak");
2895 if (app_id
&& *app_id
)
2897 ruris
= g_document_portal_add_documents (uris
, app_id
, NULL
);
2903 launch_uris_with_dbus (info
, session_bus
, ruris
, launch_context
);
2906 g_list_free_full (ruris
, g_free
);
2914 g_desktop_app_info_launch_uris_internal (GAppInfo
*appinfo
,
2916 GAppLaunchContext
*launch_context
,
2917 GSpawnFlags spawn_flags
,
2918 GSpawnChildSetupFunc user_setup
,
2919 gpointer user_setup_data
,
2920 GDesktopAppLaunchCallback pid_callback
,
2921 gpointer pid_callback_data
,
2924 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2925 GDBusConnection
*session_bus
;
2926 gboolean success
= TRUE
;
2928 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
2930 if (session_bus
&& info
->app_id
)
2931 g_desktop_app_info_launch_uris_with_dbus (info
, session_bus
, uris
, launch_context
);
2933 success
= g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, info
->exec
, uris
, launch_context
,
2934 spawn_flags
, user_setup
, user_setup_data
,
2935 pid_callback
, pid_callback_data
, error
);
2937 if (session_bus
!= NULL
)
2939 /* This asynchronous flush holds a reference until it completes,
2940 * which ensures that the following unref won't immediately kill
2941 * the connection if we were the initial owner.
2943 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
2944 g_object_unref (session_bus
);
2951 g_desktop_app_info_launch_uris (GAppInfo
*appinfo
,
2953 GAppLaunchContext
*launch_context
,
2956 return g_desktop_app_info_launch_uris_internal (appinfo
, uris
,
2958 _SPAWN_FLAGS_DEFAULT
,
2959 NULL
, NULL
, NULL
, NULL
,
2964 g_desktop_app_info_supports_uris (GAppInfo
*appinfo
)
2966 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2968 return info
->exec
&&
2969 ((strstr (info
->exec
, "%u") != NULL
) ||
2970 (strstr (info
->exec
, "%U") != NULL
));
2974 g_desktop_app_info_supports_files (GAppInfo
*appinfo
)
2976 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2978 return info
->exec
&&
2979 ((strstr (info
->exec
, "%f") != NULL
) ||
2980 (strstr (info
->exec
, "%F") != NULL
));
2984 g_desktop_app_info_launch (GAppInfo
*appinfo
,
2986 GAppLaunchContext
*launch_context
,
2996 uri
= g_file_get_uri (files
->data
);
2997 uris
= g_list_prepend (uris
, uri
);
2998 files
= files
->next
;
3001 uris
= g_list_reverse (uris
);
3003 res
= g_desktop_app_info_launch_uris (appinfo
, uris
, launch_context
, error
);
3005 g_list_free_full (uris
, g_free
);
3011 * g_desktop_app_info_launch_uris_as_manager:
3012 * @appinfo: a #GDesktopAppInfo
3013 * @uris: (element-type utf8): List of URIs
3014 * @launch_context: (nullable): a #GAppLaunchContext
3015 * @spawn_flags: #GSpawnFlags, used for each process
3016 * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once
3018 * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
3019 * @pid_callback: (scope call) (nullable): Callback for child processes
3020 * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
3021 * @error: return location for a #GError, or %NULL
3023 * This function performs the equivalent of g_app_info_launch_uris(),
3024 * but is intended primarily for operating system components that
3025 * launch applications. Ordinary applications should use
3026 * g_app_info_launch_uris().
3028 * If the application is launched via traditional UNIX fork()/exec()
3029 * then @spawn_flags, @user_setup and @user_setup_data are used for the
3030 * call to g_spawn_async(). Additionally, @pid_callback (with
3031 * @pid_callback_data) will be called to inform about the PID of the
3034 * If application launching occurs via some other mechanism (eg: D-Bus
3035 * activation) then @spawn_flags, @user_setup, @user_setup_data,
3036 * @pid_callback and @pid_callback_data are ignored.
3038 * Returns: %TRUE on successful launch, %FALSE otherwise.
3041 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
*appinfo
,
3043 GAppLaunchContext
*launch_context
,
3044 GSpawnFlags spawn_flags
,
3045 GSpawnChildSetupFunc user_setup
,
3046 gpointer user_setup_data
,
3047 GDesktopAppLaunchCallback pid_callback
,
3048 gpointer pid_callback_data
,
3051 return g_desktop_app_info_launch_uris_internal ((GAppInfo
*)appinfo
,
3062 /* OnlyShowIn API support {{{2 */
3065 * g_desktop_app_info_set_desktop_env:
3066 * @desktop_env: a string specifying what desktop this is
3068 * Sets the name of the desktop that the application is running in.
3069 * This is used by g_app_info_should_show() and
3070 * g_desktop_app_info_get_show_in() to evaluate the
3071 * `OnlyShowIn` and `NotShowIn`
3072 * desktop entry fields.
3074 * Should be called only once; subsequent calls are ignored.
3076 * Deprecated:2.42:do not use this API. Since 2.42 the value of the
3077 * `XDG_CURRENT_DESKTOP` environment variable will be used.
3080 g_desktop_app_info_set_desktop_env (const gchar
*desktop_env
)
3082 get_current_desktops (desktop_env
);
3086 g_desktop_app_info_should_show (GAppInfo
*appinfo
)
3088 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3090 if (info
->nodisplay
)
3093 return g_desktop_app_info_get_show_in (info
, NULL
);
3096 /* mime types/default apps support {{{2 */
3105 ensure_dir (DirType type
,
3108 char *path
, *display_name
;
3114 path
= g_build_filename (g_get_user_config_dir (), NULL
);
3118 path
= g_build_filename (g_get_user_data_dir (), "applications", NULL
);
3122 path
= g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL
);
3126 g_assert_not_reached ();
3130 if (g_mkdir_with_parents (path
, 0700) == 0)
3134 display_name
= g_filename_display_name (path
);
3135 if (type
== APP_DIR
)
3136 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3137 _("Can’t create user application configuration folder %s: %s"),
3138 display_name
, g_strerror (errsv
));
3140 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3141 _("Can’t create user MIME configuration folder %s: %s"),
3142 display_name
, g_strerror (errsv
));
3144 g_free (display_name
);
3151 update_mimeapps_list (const char *desktop_id
,
3152 const char *content_type
,
3153 UpdateMimeFlags flags
,
3156 char *dirname
, *filename
, *string
;
3158 gboolean load_succeeded
, res
;
3159 char **old_list
, **list
;
3160 gsize length
, data_size
;
3163 char **content_types
;
3165 /* Don't add both at start and end */
3166 g_assert (!((flags
& UPDATE_MIME_SET_DEFAULT
) &&
3167 (flags
& UPDATE_MIME_SET_NON_DEFAULT
)));
3169 dirname
= ensure_dir (CONF_DIR
, error
);
3173 filename
= g_build_filename (dirname
, "mimeapps.list", NULL
);
3176 key_file
= g_key_file_new ();
3177 load_succeeded
= g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
);
3178 if (!load_succeeded
||
3179 (!g_key_file_has_group (key_file
, ADDED_ASSOCIATIONS_GROUP
) &&
3180 !g_key_file_has_group (key_file
, REMOVED_ASSOCIATIONS_GROUP
) &&
3181 !g_key_file_has_group (key_file
, DEFAULT_APPLICATIONS_GROUP
)))
3183 g_key_file_free (key_file
);
3184 key_file
= g_key_file_new ();
3189 content_types
= g_new (char *, 2);
3190 content_types
[0] = g_strdup (content_type
);
3191 content_types
[1] = NULL
;
3195 content_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
3198 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3200 /* set as default, if requested so */
3201 string
= g_key_file_get_string (key_file
,
3202 DEFAULT_APPLICATIONS_GROUP
,
3206 if (g_strcmp0 (string
, desktop_id
) != 0 &&
3207 (flags
& UPDATE_MIME_SET_DEFAULT
))
3210 string
= g_strdup (desktop_id
);
3212 /* add in the non-default list too, if it's not already there */
3213 flags
|= UPDATE_MIME_SET_NON_DEFAULT
;
3216 if (string
== NULL
|| desktop_id
== NULL
)
3217 g_key_file_remove_key (key_file
,
3218 DEFAULT_APPLICATIONS_GROUP
,
3222 g_key_file_set_string (key_file
,
3223 DEFAULT_APPLICATIONS_GROUP
,
3232 /* reuse the list from above */
3236 g_strfreev (content_types
);
3237 content_types
= g_key_file_get_keys (key_file
, ADDED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3240 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3242 /* Add to the right place in the list */
3245 old_list
= g_key_file_get_string_list (key_file
, ADDED_ASSOCIATIONS_GROUP
,
3246 content_types
[k
], &length
, NULL
);
3248 list
= g_new (char *, 1 + length
+ 1);
3252 /* if we're adding a last-used hint, just put the application in front of the list */
3253 if (flags
& UPDATE_MIME_SET_LAST_USED
)
3255 /* avoid adding this again as non-default later */
3256 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3257 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3259 list
[i
++] = g_strdup (desktop_id
);
3264 for (j
= 0; old_list
[j
] != NULL
; j
++)
3266 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3268 /* rewrite other entries if they're different from the new one */
3269 list
[i
++] = g_strdup (old_list
[j
]);
3271 else if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3273 /* we encountered an old entry which is equal to the one we're adding as non-default,
3274 * don't change its position in the list.
3276 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3277 list
[i
++] = g_strdup (old_list
[j
]);
3282 /* add it at the end of the list */
3283 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3284 list
[i
++] = g_strdup (desktop_id
);
3288 g_strfreev (old_list
);
3290 if (list
[0] == NULL
|| desktop_id
== NULL
)
3291 g_key_file_remove_key (key_file
,
3292 ADDED_ASSOCIATIONS_GROUP
,
3296 g_key_file_set_string_list (key_file
,
3297 ADDED_ASSOCIATIONS_GROUP
,
3299 (const char * const *)list
, i
);
3306 /* reuse the list from above */
3310 g_strfreev (content_types
);
3311 content_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3314 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3316 /* Remove from removed associations group (unless remove) */
3319 old_list
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
,
3320 content_types
[k
], &length
, NULL
);
3322 list
= g_new (char *, 1 + length
+ 1);
3325 if (flags
& UPDATE_MIME_REMOVE
)
3326 list
[i
++] = g_strdup (desktop_id
);
3329 for (j
= 0; old_list
[j
] != NULL
; j
++)
3331 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3332 list
[i
++] = g_strdup (old_list
[j
]);
3337 g_strfreev (old_list
);
3339 if (list
[0] == NULL
|| desktop_id
== NULL
)
3340 g_key_file_remove_key (key_file
,
3341 REMOVED_ASSOCIATIONS_GROUP
,
3345 g_key_file_set_string_list (key_file
,
3346 REMOVED_ASSOCIATIONS_GROUP
,
3348 (const char * const *)list
, i
);
3353 g_strfreev (content_types
);
3355 data
= g_key_file_to_data (key_file
, &data_size
, error
);
3356 g_key_file_free (key_file
);
3358 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3360 desktop_file_dirs_invalidate_user_config ();
3369 g_desktop_app_info_set_as_last_used_for_type (GAppInfo
*appinfo
,
3370 const char *content_type
,
3373 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3375 if (!g_desktop_app_info_ensure_saved (info
, error
))
3378 if (!info
->desktop_id
)
3380 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3381 _("Application information lacks an identifier"));
3385 /* both add support for the content type and set as last used */
3386 return update_mimeapps_list (info
->desktop_id
, content_type
,
3387 UPDATE_MIME_SET_NON_DEFAULT
|
3388 UPDATE_MIME_SET_LAST_USED
,
3393 g_desktop_app_info_set_as_default_for_type (GAppInfo
*appinfo
,
3394 const char *content_type
,
3397 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3399 if (!g_desktop_app_info_ensure_saved (info
, error
))
3402 if (!info
->desktop_id
)
3404 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3405 _("Application information lacks an identifier"));
3409 return update_mimeapps_list (info
->desktop_id
, content_type
,
3410 UPDATE_MIME_SET_DEFAULT
,
3415 update_program_done (GPid pid
,
3419 /* Did the application exit correctly */
3420 if (g_spawn_check_exit_status (status
, NULL
))
3422 /* Here we could clean out any caches in use */
3427 run_update_command (char *command
,
3436 GError
*error
= NULL
;
3439 argv
[1] = g_build_filename (g_get_user_data_dir (), subdir
, NULL
);
3441 if (g_spawn_async ("/", argv
,
3443 G_SPAWN_SEARCH_PATH
|
3444 G_SPAWN_STDOUT_TO_DEV_NULL
|
3445 G_SPAWN_STDERR_TO_DEV_NULL
|
3446 G_SPAWN_DO_NOT_REAP_CHILD
,
3447 NULL
, NULL
, /* No setup function */
3450 g_child_watch_add (pid
, update_program_done
, NULL
);
3453 /* If we get an error at this point, it's quite likely the user doesn't
3454 * have an installed copy of either 'update-mime-database' or
3455 * 'update-desktop-database'. I don't think we want to popup an error
3456 * dialog at this point, so we just do a g_warning to give the user a
3457 * chance of debugging it.
3459 g_warning ("%s", error
->message
);
3460 g_error_free (error
);
3467 g_desktop_app_info_set_as_default_for_extension (GAppInfo
*appinfo
,
3468 const char *extension
,
3471 char *filename
, *basename
, *mimetype
;
3475 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo
), error
))
3478 dirname
= ensure_dir (MIMETYPE_DIR
, error
);
3482 basename
= g_strdup_printf ("user-extension-%s.xml", extension
);
3483 filename
= g_build_filename (dirname
, basename
, NULL
);
3487 mimetype
= g_strdup_printf ("application/x-extension-%s", extension
);
3489 if (!g_file_test (filename
, G_FILE_TEST_EXISTS
))
3494 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3495 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3496 " <mime-type type=\"%s\">\n"
3497 " <comment>%s document</comment>\n"
3498 " <glob pattern=\"*.%s\"/>\n"
3500 "</mime-info>\n", mimetype
, extension
, extension
);
3502 g_file_set_contents (filename
, contents
, -1, NULL
);
3505 run_update_command ("update-mime-database", "mime");
3509 res
= g_desktop_app_info_set_as_default_for_type (appinfo
,
3519 g_desktop_app_info_add_supports_type (GAppInfo
*appinfo
,
3520 const char *content_type
,
3523 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3525 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3528 return update_mimeapps_list (info
->desktop_id
, content_type
,
3529 UPDATE_MIME_SET_NON_DEFAULT
,
3534 g_desktop_app_info_can_remove_supports_type (GAppInfo
*appinfo
)
3540 g_desktop_app_info_remove_supports_type (GAppInfo
*appinfo
,
3541 const char *content_type
,
3544 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3546 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3549 return update_mimeapps_list (info
->desktop_id
, content_type
,
3554 static const char **
3555 g_desktop_app_info_get_supported_types (GAppInfo
*appinfo
)
3557 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3559 return (const char**) info
->mime_types
;
3562 /* Saving and deleting {{{2 */
3565 g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
3571 char *data
, *desktop_id
;
3576 if (info
->filename
!= NULL
)
3579 /* This is only used for object created with
3580 * g_app_info_create_from_commandline. All other
3581 * object should have a filename
3584 dirname
= ensure_dir (APP_DIR
, error
);
3588 key_file
= g_key_file_new ();
3590 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3591 "Encoding", "UTF-8");
3592 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3593 G_KEY_FILE_DESKTOP_KEY_VERSION
, "1.0");
3594 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3595 G_KEY_FILE_DESKTOP_KEY_TYPE
,
3596 G_KEY_FILE_DESKTOP_TYPE_APPLICATION
);
3598 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3599 G_KEY_FILE_DESKTOP_KEY_TERMINAL
, TRUE
);
3600 if (info
->nodisplay
)
3601 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3602 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3604 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3605 G_KEY_FILE_DESKTOP_KEY_EXEC
, info
->exec
);
3607 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3608 G_KEY_FILE_DESKTOP_KEY_NAME
, info
->name
);
3610 if (info
->generic_name
!= NULL
)
3611 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3612 GENERIC_NAME_KEY
, info
->generic_name
);
3614 if (info
->fullname
!= NULL
)
3615 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3616 FULL_NAME_KEY
, info
->fullname
);
3618 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3619 G_KEY_FILE_DESKTOP_KEY_COMMENT
, info
->comment
);
3621 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3622 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3624 data
= g_key_file_to_data (key_file
, &data_size
, NULL
);
3625 g_key_file_free (key_file
);
3627 desktop_id
= g_strdup_printf ("userapp-%s-XXXXXX.desktop", info
->name
);
3628 filename
= g_build_filename (dirname
, desktop_id
, NULL
);
3629 g_free (desktop_id
);
3632 fd
= g_mkstemp (filename
);
3637 display_name
= g_filename_display_name (filename
);
3638 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3639 _("Can’t create user desktop file %s"), display_name
);
3640 g_free (display_name
);
3646 desktop_id
= g_path_get_basename (filename
);
3648 /* FIXME - actually handle error */
3649 (void) g_close (fd
, NULL
);
3651 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3655 g_free (desktop_id
);
3660 info
->filename
= filename
;
3661 info
->desktop_id
= desktop_id
;
3663 run_update_command ("update-desktop-database", "applications");
3665 /* We just dropped a file in the user's desktop file directory. Save
3666 * the monitor the bother of having to notice it and invalidate
3669 * This means that calls directly following this will be able to see
3670 * the results immediately.
3672 desktop_file_dirs_invalidate_user_data ();
3678 g_desktop_app_info_can_delete (GAppInfo
*appinfo
)
3680 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3684 if (strstr (info
->filename
, "/userapp-"))
3685 return g_access (info
->filename
, W_OK
) == 0;
3692 g_desktop_app_info_delete (GAppInfo
*appinfo
)
3694 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3698 if (g_remove (info
->filename
) == 0)
3700 update_mimeapps_list (info
->desktop_id
, NULL
,
3704 g_free (info
->filename
);
3705 info
->filename
= NULL
;
3706 g_free (info
->desktop_id
);
3707 info
->desktop_id
= NULL
;
3716 /* Create for commandline {{{2 */
3718 * g_app_info_create_from_commandline:
3719 * @commandline: (type filename): the commandline to use
3720 * @application_name: (nullable): the application name, or %NULL to use @commandline
3721 * @flags: flags that can specify details of the created #GAppInfo
3722 * @error: a #GError location to store the error occurring, %NULL to ignore.
3724 * Creates a new #GAppInfo from the given information.
3726 * Note that for @commandline, the quoting rules of the Exec key of the
3727 * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3728 * are applied. For example, if the @commandline contains
3729 * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3730 * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
3732 * Returns: (transfer full): new #GAppInfo for given command.
3735 g_app_info_create_from_commandline (const char *commandline
,
3736 const char *application_name
,
3737 GAppInfoCreateFlags flags
,
3742 GDesktopAppInfo
*info
;
3744 g_return_val_if_fail (commandline
, NULL
);
3746 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
3748 info
->filename
= NULL
;
3749 info
->desktop_id
= NULL
;
3751 info
->terminal
= (flags
& G_APP_INFO_CREATE_NEEDS_TERMINAL
) != 0;
3752 info
->startup_notify
= (flags
& G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION
) != 0;
3753 info
->hidden
= FALSE
;
3754 if ((flags
& G_APP_INFO_CREATE_SUPPORTS_URIS
) != 0)
3755 info
->exec
= g_strconcat (commandline
, " %u", NULL
);
3757 info
->exec
= g_strconcat (commandline
, " %f", NULL
);
3758 info
->nodisplay
= TRUE
;
3759 info
->binary
= binary_from_exec (info
->exec
);
3761 if (application_name
)
3762 info
->name
= g_strdup (application_name
);
3765 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
3766 split
= g_strsplit (commandline
, " ", 2);
3767 basename
= split
[0] ? g_path_get_basename (split
[0]) : NULL
;
3769 info
->name
= basename
;
3770 if (info
->name
== NULL
)
3771 info
->name
= g_strdup ("custom");
3773 info
->comment
= g_strdup_printf (_("Custom definition for %s"), info
->name
);
3775 return G_APP_INFO (info
);
3778 /* GAppInfo interface init */
3781 g_desktop_app_info_iface_init (GAppInfoIface
*iface
)
3783 iface
->dup
= g_desktop_app_info_dup
;
3784 iface
->equal
= g_desktop_app_info_equal
;
3785 iface
->get_id
= g_desktop_app_info_get_id
;
3786 iface
->get_name
= g_desktop_app_info_get_name
;
3787 iface
->get_description
= g_desktop_app_info_get_description
;
3788 iface
->get_executable
= g_desktop_app_info_get_executable
;
3789 iface
->get_icon
= g_desktop_app_info_get_icon
;
3790 iface
->launch
= g_desktop_app_info_launch
;
3791 iface
->supports_uris
= g_desktop_app_info_supports_uris
;
3792 iface
->supports_files
= g_desktop_app_info_supports_files
;
3793 iface
->launch_uris
= g_desktop_app_info_launch_uris
;
3794 iface
->should_show
= g_desktop_app_info_should_show
;
3795 iface
->set_as_default_for_type
= g_desktop_app_info_set_as_default_for_type
;
3796 iface
->set_as_default_for_extension
= g_desktop_app_info_set_as_default_for_extension
;
3797 iface
->add_supports_type
= g_desktop_app_info_add_supports_type
;
3798 iface
->can_remove_supports_type
= g_desktop_app_info_can_remove_supports_type
;
3799 iface
->remove_supports_type
= g_desktop_app_info_remove_supports_type
;
3800 iface
->can_delete
= g_desktop_app_info_can_delete
;
3801 iface
->do_delete
= g_desktop_app_info_delete
;
3802 iface
->get_commandline
= g_desktop_app_info_get_commandline
;
3803 iface
->get_display_name
= g_desktop_app_info_get_display_name
;
3804 iface
->set_as_last_used_for_type
= g_desktop_app_info_set_as_last_used_for_type
;
3805 iface
->get_supported_types
= g_desktop_app_info_get_supported_types
;
3808 /* Recommended applications {{{2 */
3810 /* Converts content_type into a list of itself with all of its parent
3811 * types (if include_fallback is enabled) or just returns a single-item
3812 * list with the unaliased content type.
3815 get_list_of_mimetypes (const gchar
*content_type
,
3816 gboolean include_fallback
)
3821 array
= g_ptr_array_new ();
3822 unaliased
= _g_unix_content_type_unalias (content_type
);
3823 g_ptr_array_add (array
, unaliased
);
3825 if (include_fallback
)
3829 /* Iterate the array as we grow it, until we have nothing more to add */
3830 for (i
= 0; i
< array
->len
; i
++)
3832 gchar
**parents
= _g_unix_content_type_get_parents (g_ptr_array_index (array
, i
));
3835 for (j
= 0; parents
[j
]; j
++)
3836 /* Don't add duplicates */
3837 if (!array_contains (array
, parents
[j
]))
3838 g_ptr_array_add (array
, parents
[j
]);
3840 g_free (parents
[j
]);
3842 /* We already stole or freed each element. Free the container. */
3847 g_ptr_array_add (array
, NULL
);
3849 return (gchar
**) g_ptr_array_free (array
, FALSE
);
3853 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar
*content_type
,
3854 gboolean include_fallback
)
3856 GPtrArray
*hits
, *blacklist
;
3860 hits
= g_ptr_array_new ();
3861 blacklist
= g_ptr_array_new ();
3863 types
= get_list_of_mimetypes (content_type
, include_fallback
);
3865 desktop_file_dirs_lock ();
3867 for (i
= 0; types
[i
]; i
++)
3868 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
3869 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], hits
, blacklist
);
3871 /* We will keep the hits past unlocking, so we must dup them */
3872 for (i
= 0; i
< hits
->len
; i
++)
3873 hits
->pdata
[i
] = g_strdup (hits
->pdata
[i
]);
3875 desktop_file_dirs_unlock ();
3877 g_ptr_array_add (hits
, NULL
);
3879 g_ptr_array_free (blacklist
, TRUE
);
3882 return (gchar
**) g_ptr_array_free (hits
, FALSE
);
3886 * g_app_info_get_recommended_for_type:
3887 * @content_type: the content type to find a #GAppInfo for
3889 * Gets a list of recommended #GAppInfos for a given content type, i.e.
3890 * those applications which claim to support the given content type exactly,
3891 * and not by MIME type subclassing.
3892 * Note that the first application of the list is the last used one, i.e.
3893 * the last one for which g_app_info_set_as_last_used_for_type() has been
3896 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3897 * for given @content_type or %NULL on error.
3902 g_app_info_get_recommended_for_type (const gchar
*content_type
)
3904 gchar
**desktop_ids
;
3908 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3910 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3913 for (i
= 0; desktop_ids
[i
]; i
++)
3915 GDesktopAppInfo
*info
;
3917 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3919 infos
= g_list_prepend (infos
, info
);
3922 g_strfreev (desktop_ids
);
3924 return g_list_reverse (infos
);
3928 * g_app_info_get_fallback_for_type:
3929 * @content_type: the content type to find a #GAppInfo for
3931 * Gets a list of fallback #GAppInfos for a given content type, i.e.
3932 * those applications which claim to support the given content type
3933 * by MIME type subclassing and not directly.
3935 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3936 * for given @content_type or %NULL on error.
3941 g_app_info_get_fallback_for_type (const gchar
*content_type
)
3943 gchar
**recommended_ids
;
3948 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3950 recommended_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3951 all_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3954 for (i
= 0; all_ids
[i
]; i
++)
3956 GDesktopAppInfo
*info
;
3959 /* Don't return the ones on the recommended list */
3960 for (j
= 0; recommended_ids
[j
]; j
++)
3961 if (g_str_equal (all_ids
[i
], recommended_ids
[j
]))
3964 if (recommended_ids
[j
])
3967 info
= g_desktop_app_info_new (all_ids
[i
]);
3970 infos
= g_list_prepend (infos
, info
);
3973 g_strfreev (recommended_ids
);
3974 g_strfreev (all_ids
);
3976 return g_list_reverse (infos
);
3980 * g_app_info_get_all_for_type:
3981 * @content_type: the content type to find a #GAppInfo for
3983 * Gets a list of all #GAppInfos for a given content type,
3984 * including the recommended and fallback #GAppInfos. See
3985 * g_app_info_get_recommended_for_type() and
3986 * g_app_info_get_fallback_for_type().
3988 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3989 * for given @content_type or %NULL on error.
3992 g_app_info_get_all_for_type (const char *content_type
)
3994 gchar
**desktop_ids
;
3998 g_return_val_if_fail (content_type
!= NULL
, NULL
);
4000 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
4003 for (i
= 0; desktop_ids
[i
]; i
++)
4005 GDesktopAppInfo
*info
;
4007 info
= g_desktop_app_info_new (desktop_ids
[i
]);
4009 infos
= g_list_prepend (infos
, info
);
4012 g_strfreev (desktop_ids
);
4014 return g_list_reverse (infos
);
4018 * g_app_info_reset_type_associations:
4019 * @content_type: a content type
4021 * Removes all changes to the type associations done by
4022 * g_app_info_set_as_default_for_type(),
4023 * g_app_info_set_as_default_for_extension(),
4024 * g_app_info_add_supports_type() or
4025 * g_app_info_remove_supports_type().
4030 g_app_info_reset_type_associations (const char *content_type
)
4032 update_mimeapps_list (NULL
, content_type
,
4038 * g_app_info_get_default_for_type:
4039 * @content_type: the content type to find a #GAppInfo for
4040 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
4043 * Gets the default #GAppInfo for a given content type.
4045 * Returns: (transfer full): #GAppInfo for given @content_type or
4049 g_app_info_get_default_for_type (const char *content_type
,
4050 gboolean must_support_uris
)
4052 GPtrArray
*blacklist
;
4058 g_return_val_if_fail (content_type
!= NULL
, NULL
);
4060 types
= get_list_of_mimetypes (content_type
, TRUE
);
4062 blacklist
= g_ptr_array_new ();
4063 results
= g_ptr_array_new ();
4066 desktop_file_dirs_lock ();
4068 for (i
= 0; types
[i
]; i
++)
4070 /* Collect all the default apps for this type */
4071 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4072 desktop_file_dir_default_lookup (&desktop_file_dirs
[j
], types
[i
], results
);
4074 /* Consider the associations as well... */
4075 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4076 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], results
, blacklist
);
4078 /* (If any), see if one of those apps is installed... */
4079 for (j
= 0; j
< results
->len
; j
++)
4081 const gchar
*desktop_id
= g_ptr_array_index (results
, j
);
4083 for (k
= 0; k
< n_desktop_file_dirs
; k
++)
4085 info
= (GAppInfo
*) desktop_file_dir_get_app (&desktop_file_dirs
[k
], desktop_id
);
4089 if (!must_support_uris
|| g_app_info_supports_uris (info
))
4092 g_clear_object (&info
);
4097 /* Reset the list, ready to try again with the next (parent)
4098 * mimetype, but keep the blacklist in place.
4100 g_ptr_array_set_size (results
, 0);
4104 desktop_file_dirs_unlock ();
4106 g_ptr_array_unref (blacklist
);
4107 g_ptr_array_unref (results
);
4114 * g_app_info_get_default_for_uri_scheme:
4115 * @uri_scheme: a string containing a URI scheme.
4117 * Gets the default application for handling URIs with
4118 * the given URI scheme. A URI scheme is the initial part
4119 * of the URI, up to but not including the ':', e.g. "http",
4122 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4125 g_app_info_get_default_for_uri_scheme (const char *uri_scheme
)
4128 char *content_type
, *scheme_down
;
4130 scheme_down
= g_ascii_strdown (uri_scheme
, -1);
4131 content_type
= g_strdup_printf ("x-scheme-handler/%s", scheme_down
);
4132 g_free (scheme_down
);
4133 app_info
= g_app_info_get_default_for_type (content_type
, FALSE
);
4134 g_free (content_type
);
4139 /* "Get all" API {{{2 */
4142 * g_desktop_app_info_get_implementations:
4143 * @interface: the name of the interface
4145 * Gets all applications that implement @interface.
4147 * An application implements an interface if that interface is listed in
4148 * the Implements= line of the desktop file of the application.
4150 * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo
4156 g_desktop_app_info_get_implementations (const gchar
*interface
)
4158 GList
*result
= NULL
;
4162 desktop_file_dirs_lock ();
4164 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4165 desktop_file_dir_get_implementations (&desktop_file_dirs
[i
], &result
, interface
);
4167 desktop_file_dirs_unlock ();
4172 gchar
*name
= (*ptr
)->data
;
4173 GDesktopAppInfo
*app
;
4175 app
= g_desktop_app_info_new (name
);
4181 ptr
= &(*ptr
)->next
;
4184 *ptr
= g_list_delete_link (*ptr
, *ptr
);
4191 * g_desktop_app_info_search:
4192 * @search_string: the search string to use
4194 * Searches desktop files for ones that match @search_string.
4196 * The return value is an array of strvs. Each strv contains a list of
4197 * applications that matched @search_string with an equal score. The
4198 * outer list is sorted by score so that the first strv contains the
4199 * best-matching applications, and so on.
4200 * The algorithm for determining matches is undefined and may change at
4203 * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4204 * list of strvs. Free each item with g_strfreev() and free the outer
4205 * list with g_free().
4208 g_desktop_app_info_search (const gchar
*search_string
)
4210 gchar
**search_tokens
;
4211 gint last_category
= -1;
4213 gint n_categories
= 0;
4214 gint start_of_category
;
4217 search_tokens
= g_str_tokenize_and_fold (search_string
, NULL
, NULL
);
4219 desktop_file_dirs_lock ();
4221 reset_total_search_results ();
4223 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4225 for (j
= 0; search_tokens
[j
]; j
++)
4227 desktop_file_dir_search (&desktop_file_dirs
[i
], search_tokens
[j
]);
4228 merge_token_results (j
== 0);
4230 merge_directory_results ();
4233 sort_total_search_results ();
4235 /* Count the total number of unique categories */
4236 for (i
= 0; i
< static_total_results_size
; i
++)
4237 if (static_total_results
[i
].category
!= last_category
)
4239 last_category
= static_total_results
[i
].category
;
4243 results
= g_new (gchar
**, n_categories
+ 1);
4245 /* Start loading into the results list */
4246 start_of_category
= 0;
4247 for (i
= 0; i
< n_categories
; i
++)
4249 gint n_items_in_category
= 0;
4253 this_category
= static_total_results
[start_of_category
].category
;
4255 while (start_of_category
+ n_items_in_category
< static_total_results_size
&&
4256 static_total_results
[start_of_category
+ n_items_in_category
].category
== this_category
)
4257 n_items_in_category
++;
4259 results
[i
] = g_new (gchar
*, n_items_in_category
+ 1);
4260 for (j
= 0; j
< n_items_in_category
; j
++)
4261 results
[i
][j
] = g_strdup (static_total_results
[start_of_category
+ j
].app_name
);
4262 results
[i
][j
] = NULL
;
4264 start_of_category
+= n_items_in_category
;
4268 desktop_file_dirs_unlock ();
4270 g_strfreev (search_tokens
);
4276 * g_app_info_get_all:
4278 * Gets a list of all of the applications currently registered
4281 * For desktop files, this includes applications that have
4282 * `NoDisplay=true` set or are excluded from display by means
4283 * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4284 * The returned list does not include applications which have
4285 * the `Hidden` key set.
4287 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4290 g_app_info_get_all (void)
4293 GHashTableIter iter
;
4298 apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
4300 desktop_file_dirs_lock ();
4302 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4303 desktop_file_dir_get_all (&desktop_file_dirs
[i
], apps
);
4305 desktop_file_dirs_unlock ();
4308 g_hash_table_iter_init (&iter
, apps
);
4309 while (g_hash_table_iter_next (&iter
, NULL
, &value
))
4312 infos
= g_list_prepend (infos
, value
);
4315 g_hash_table_destroy (apps
);
4320 /* GDesktopAppInfoLookup interface {{{2 */
4323 * GDesktopAppInfoLookup:
4325 * #GDesktopAppInfoLookup is an opaque data structure and can only be accessed
4326 * using the following functions.
4329 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4331 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface
;
4332 G_DEFINE_INTERFACE (GDesktopAppInfoLookup
, g_desktop_app_info_lookup
, G_TYPE_OBJECT
)
4335 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface
*iface
)
4339 /* "Get for mime type" APIs {{{2 */
4342 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4343 * @lookup: a #GDesktopAppInfoLookup
4344 * @uri_scheme: a string containing a URI scheme.
4346 * Gets the default application for launching applications
4347 * using this URI scheme for a particular GDesktopAppInfoLookup
4350 * The GDesktopAppInfoLookup interface and this function is used
4351 * to implement g_app_info_get_default_for_uri_scheme() backends
4352 * in a GIO module. There is no reason for applications to use it
4353 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4355 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4357 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
4360 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup
*lookup
,
4361 const char *uri_scheme
)
4363 GDesktopAppInfoLookupIface
*iface
;
4365 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup
), NULL
);
4367 iface
= G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup
);
4369 return (* iface
->get_default_for_uri_scheme
) (lookup
, uri_scheme
);
4372 G_GNUC_END_IGNORE_DEPRECATIONS
4374 /* Misc getter APIs {{{2 */
4377 * g_desktop_app_info_get_startup_wm_class:
4378 * @info: a #GDesktopAppInfo that supports startup notify
4380 * Retrieves the StartupWMClass field from @info. This represents the
4381 * WM_CLASS property of the main window of the application, if launched
4384 * Returns: (transfer none): the startup WM class, or %NULL if none is set
4385 * in the desktop file.
4390 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo
*info
)
4392 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4394 return info
->startup_wm_class
;
4398 * g_desktop_app_info_get_string:
4399 * @info: a #GDesktopAppInfo
4400 * @key: the key to look up
4402 * Looks up a string value in the keyfile backing @info.
4404 * The @key is looked up in the "Desktop Entry" group.
4406 * Returns: a newly allocated string, or %NULL if the key
4412 g_desktop_app_info_get_string (GDesktopAppInfo
*info
,
4415 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4417 return g_key_file_get_string (info
->keyfile
,
4418 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4422 * g_desktop_app_info_get_locale_string:
4423 * @info: a #GDesktopAppInfo
4424 * @key: the key to look up
4426 * Looks up a localized string value in the keyfile backing @info
4427 * translated to the current locale.
4429 * The @key is looked up in the "Desktop Entry" group.
4431 * Returns: (nullable): a newly allocated string, or %NULL if the key
4437 g_desktop_app_info_get_locale_string (GDesktopAppInfo
*info
,
4440 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4441 g_return_val_if_fail (key
!= NULL
&& *key
!= '\0', NULL
);
4443 return g_key_file_get_locale_string (info
->keyfile
,
4444 G_KEY_FILE_DESKTOP_GROUP
,
4449 * g_desktop_app_info_get_boolean:
4450 * @info: a #GDesktopAppInfo
4451 * @key: the key to look up
4453 * Looks up a boolean value in the keyfile backing @info.
4455 * The @key is looked up in the "Desktop Entry" group.
4457 * Returns: the boolean value, or %FALSE if the key
4463 g_desktop_app_info_get_boolean (GDesktopAppInfo
*info
,
4466 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4468 return g_key_file_get_boolean (info
->keyfile
,
4469 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4473 * g_desktop_app_info_has_key:
4474 * @info: a #GDesktopAppInfo
4475 * @key: the key to look up
4477 * Returns whether @key exists in the "Desktop Entry" group
4478 * of the keyfile backing @info.
4480 * Returns: %TRUE if the @key exists
4485 g_desktop_app_info_has_key (GDesktopAppInfo
*info
,
4488 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4490 return g_key_file_has_key (info
->keyfile
,
4491 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4494 /* Desktop actions support {{{2 */
4497 * g_desktop_app_info_list_actions:
4498 * @info: a #GDesktopAppInfo
4500 * Returns the list of "additional application actions" supported on the
4501 * desktop file, as per the desktop file specification.
4503 * As per the specification, this is the list of actions that are
4504 * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4506 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4510 const gchar
* const *
4511 g_desktop_app_info_list_actions (GDesktopAppInfo
*info
)
4513 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4515 return (const gchar
**) info
->actions
;
4519 app_info_has_action (GDesktopAppInfo
*info
,
4520 const gchar
*action_name
)
4524 for (i
= 0; info
->actions
[i
]; i
++)
4525 if (g_str_equal (info
->actions
[i
], action_name
))
4532 * g_desktop_app_info_get_action_name:
4533 * @info: a #GDesktopAppInfo
4534 * @action_name: the name of the action as from
4535 * g_desktop_app_info_list_actions()
4537 * Gets the user-visible display name of the "additional application
4538 * action" specified by @action_name.
4540 * This corresponds to the "Name" key within the keyfile group for the
4543 * Returns: (transfer full): the locale-specific action name
4548 g_desktop_app_info_get_action_name (GDesktopAppInfo
*info
,
4549 const gchar
*action_name
)
4554 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4555 g_return_val_if_fail (action_name
!= NULL
, NULL
);
4556 g_return_val_if_fail (app_info_has_action (info
, action_name
), NULL
);
4558 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4559 result
= g_key_file_get_locale_string (info
->keyfile
, group_name
, "Name", NULL
, NULL
);
4560 g_free (group_name
);
4562 /* The spec says that the Name field must be given.
4564 * If it's not, let's follow the behaviour of our get_name()
4565 * implementation above and never return %NULL.
4568 result
= g_strdup (_("Unnamed"));
4574 * g_desktop_app_info_launch_action:
4575 * @info: a #GDesktopAppInfo
4576 * @action_name: the name of the action as from
4577 * g_desktop_app_info_list_actions()
4578 * @launch_context: (nullable): a #GAppLaunchContext
4580 * Activates the named application action.
4582 * You may only call this function on action names that were
4583 * returned from g_desktop_app_info_list_actions().
4585 * Note that if the main entry of the desktop file indicates that the
4586 * application supports startup notification, and @launch_context is
4587 * non-%NULL, then startup notification will be used when activating the
4588 * action (and as such, invocation of the action on the receiving side
4589 * must signal the end of startup notification when it is completed).
4590 * This is the expected behaviour of applications declaring additional
4591 * actions, as per the desktop file specification.
4593 * As with g_app_info_launch() there is no way to detect failures that
4594 * occur while using this function.
4599 g_desktop_app_info_launch_action (GDesktopAppInfo
*info
,
4600 const gchar
*action_name
,
4601 GAppLaunchContext
*launch_context
)
4603 GDBusConnection
*session_bus
;
4605 g_return_if_fail (G_IS_DESKTOP_APP_INFO (info
));
4606 g_return_if_fail (action_name
!= NULL
);
4607 g_return_if_fail (app_info_has_action (info
, action_name
));
4609 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
4611 if (session_bus
&& info
->app_id
)
4615 object_path
= object_path_from_appid (info
->app_id
);
4616 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
,
4617 "org.freedesktop.Application", "ActivateAction",
4618 g_variant_new ("(sav@a{sv})", action_name
, NULL
,
4619 g_desktop_app_info_make_platform_data (info
, NULL
, launch_context
)),
4620 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
4621 g_free (object_path
);
4628 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4629 exec_line
= g_key_file_get_string (info
->keyfile
, group_name
, "Exec", NULL
);
4630 g_free (group_name
);
4633 g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, exec_line
, NULL
, launch_context
,
4634 _SPAWN_FLAGS_DEFAULT
, NULL
, NULL
, NULL
, NULL
, NULL
);
4637 if (session_bus
!= NULL
)
4639 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
4640 g_object_unref (session_bus
);
4645 /* vim:set foldmethod=marker: */