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
390 /* Common prefix commands to ignore from Exec= lines */
391 const char * const exec_key_match_blacklist
[] = {
407 desktop_key_get_name (guint key_id
)
411 case DESKTOP_KEY_Comment
:
413 case DESKTOP_KEY_Exec
:
415 case DESKTOP_KEY_GenericName
:
416 return GENERIC_NAME_KEY
;
417 case DESKTOP_KEY_Keywords
:
419 case DESKTOP_KEY_Name
:
421 case DESKTOP_KEY_X_GNOME_FullName
:
422 return FULL_NAME_KEY
;
424 g_assert_not_reached ();
428 /* Search global state {{{2
430 * We only ever search under a global lock, so we can use (and reuse)
431 * some global data to reduce allocations made while searching.
433 * In short, we keep around arrays of results that we expand as needed
434 * (and never shrink).
436 * static_token_results: this is where we append the results for each
437 * token within a given desktop directory, as we handle it (which is
438 * a union of all matches for this term)
440 * static_search_results: this is where we build the complete results
441 * for a single directory (which is an intersection of the matches
442 * found for each term)
444 * static_total_results: this is where we build the complete results
445 * across all directories (which is a union of the matches found in
448 * The app_names that enter these tables are always pointer-unique (in
449 * the sense that string equality is the same as pointer equality).
450 * This can be guaranteed for two reasons:
452 * - we mask appids so that a given appid will only ever appear within
453 * the highest-precedence directory that contains it. We never
454 * return search results from a lower-level directory if a desktop
455 * file exists in a higher-level one.
457 * - within a given directory, the string is unique because it's the
458 * key in the hashtable of all app_ids for that directory.
460 * We perform a merging of the results in merge_token_results(). This
461 * works by ordering the two lists and moving through each of them (at
462 * the same time) looking for common elements, rejecting uncommon ones.
463 * "Order" here need not mean any particular thing, as long as it is
464 * some order. Because of the uniqueness of our strings, we can use
465 * pointer order. That's what's going on in compare_results() below.
469 const gchar
*app_name
;
473 static struct search_result
*static_token_results
;
474 static gint static_token_results_size
;
475 static gint static_token_results_allocated
;
476 static struct search_result
*static_search_results
;
477 static gint static_search_results_size
;
478 static gint static_search_results_allocated
;
479 static struct search_result
*static_total_results
;
480 static gint static_total_results_size
;
481 static gint static_total_results_allocated
;
483 /* And some functions for performing nice operations against it */
485 compare_results (gconstpointer a
,
488 const struct search_result
*ra
= a
;
489 const struct search_result
*rb
= b
;
491 if (ra
->app_name
< rb
->app_name
)
494 else if (ra
->app_name
> rb
->app_name
)
498 return ra
->category
- rb
->category
;
502 compare_categories (gconstpointer a
,
505 const struct search_result
*ra
= a
;
506 const struct search_result
*rb
= b
;
508 return ra
->category
- rb
->category
;
512 add_token_result (const gchar
*app_name
,
515 if G_UNLIKELY (static_token_results_size
== static_token_results_allocated
)
517 static_token_results_allocated
= MAX (16, static_token_results_allocated
* 2);
518 static_token_results
= g_renew (struct search_result
, static_token_results
, static_token_results_allocated
);
521 static_token_results
[static_token_results_size
].app_name
= app_name
;
522 static_token_results
[static_token_results_size
].category
= category
;
523 static_token_results_size
++;
527 merge_token_results (gboolean first
)
529 if (static_token_results_size
!= 0)
530 qsort (static_token_results
, static_token_results_size
, sizeof (struct search_result
), compare_results
);
532 /* If this is the first token then we are basically merging a list with
533 * itself -- we only perform de-duplication.
535 * If this is not the first token then we are doing a real merge.
539 const gchar
*last_name
= NULL
;
542 /* We must de-duplicate, but we do so by taking the best category
545 * The final list can be as large as the input here, so make sure
546 * we have enough room (even if it's too much room).
549 if G_UNLIKELY (static_search_results_allocated
< static_token_results_size
)
551 static_search_results_allocated
= static_token_results_allocated
;
552 static_search_results
= g_renew (struct search_result
,
553 static_search_results
,
554 static_search_results_allocated
);
557 for (i
= 0; i
< static_token_results_size
; i
++)
559 /* The list is sorted so that the best match for a given id
560 * will be at the front, so once we have copied an id, skip
561 * the rest of the entries for the same id.
563 if (static_token_results
[i
].app_name
== last_name
)
566 last_name
= static_token_results
[i
].app_name
;
568 static_search_results
[static_search_results_size
++] = static_token_results
[i
];
573 const gchar
*last_name
= NULL
;
577 /* We only ever remove items from the results list, so no need to
578 * resize to ensure that we have enough room.
580 for (i
= 0; i
< static_token_results_size
; i
++)
582 if (static_token_results
[i
].app_name
== last_name
)
585 last_name
= static_token_results
[i
].app_name
;
587 /* Now we only want to have a result in static_search_results
588 * if we already have it there *and* we have it in
589 * static_token_results as well. The category will be the
592 * Skip past the results in static_search_results that are not
593 * going to be matches.
595 while (k
< static_search_results_size
&&
596 static_search_results
[k
].app_name
< static_token_results
[i
].app_name
)
599 if (k
< static_search_results_size
&&
600 static_search_results
[k
].app_name
== static_token_results
[i
].app_name
)
604 * Category should be the worse of the two (ie:
605 * numerically larger).
607 static_search_results
[j
].app_name
= static_search_results
[k
].app_name
;
608 static_search_results
[j
].category
= MAX (static_search_results
[k
].category
,
609 static_token_results
[i
].category
);
614 static_search_results_size
= j
;
617 /* Clear it out for next time... */
618 static_token_results_size
= 0;
622 reset_total_search_results (void)
624 static_total_results_size
= 0;
628 sort_total_search_results (void)
630 if (static_total_results_size
!= 0)
631 qsort (static_total_results
, static_total_results_size
, sizeof (struct search_result
), compare_categories
);
635 merge_directory_results (void)
637 if G_UNLIKELY (static_total_results_size
+ static_search_results_size
> static_total_results_allocated
)
639 static_total_results_allocated
= MAX (16, static_total_results_allocated
);
640 while (static_total_results_allocated
< static_total_results_size
+ static_search_results_size
)
641 static_total_results_allocated
*= 2;
642 static_total_results
= g_renew (struct search_result
, static_total_results
, static_total_results_allocated
);
645 if (static_total_results
+ static_total_results_size
!= 0)
646 memcpy (static_total_results
+ static_total_results_size
,
647 static_search_results
,
648 static_search_results_size
* sizeof (struct search_result
));
650 static_total_results_size
+= static_search_results_size
;
652 /* Clear it out for next time... */
653 static_search_results_size
= 0;
656 /* Support for unindexed DesktopFileDirs {{{2 */
658 get_apps_from_dir (GHashTable
**apps
,
662 const char *basename
;
665 dir
= g_dir_open (dirname
, 0, NULL
);
670 while ((basename
= g_dir_read_name (dir
)) != NULL
)
674 filename
= g_build_filename (dirname
, basename
, NULL
);
676 if (g_str_has_suffix (basename
, ".desktop"))
680 app_name
= g_strconcat (prefix
, basename
, NULL
);
683 *apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
685 g_hash_table_insert (*apps
, app_name
, g_strdup (filename
));
687 else if (g_file_test (filename
, G_FILE_TEST_IS_DIR
))
691 subprefix
= g_strconcat (prefix
, basename
, "-", NULL
);
692 get_apps_from_dir (apps
, filename
, subprefix
);
707 } UnindexedMimeTweaks
;
710 free_mime_tweaks (gpointer data
)
712 UnindexedMimeTweaks
*tweaks
= data
;
714 g_strfreev (tweaks
->additions
);
715 g_strfreev (tweaks
->removals
);
716 g_strfreev (tweaks
->defaults
);
718 g_slice_free (UnindexedMimeTweaks
, tweaks
);
721 static UnindexedMimeTweaks
*
722 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir
*dir
,
723 const gchar
*mime_type
)
725 UnindexedMimeTweaks
*tweaks
;
726 gchar
*unaliased_type
;
728 unaliased_type
= _g_unix_content_type_unalias (mime_type
);
729 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, unaliased_type
);
733 tweaks
= g_slice_new0 (UnindexedMimeTweaks
);
734 g_hash_table_insert (dir
->mime_tweaks
, unaliased_type
, tweaks
);
737 g_free (unaliased_type
);
742 /* consumes 'to_add' */
744 expand_strv (gchar
***strv_ptr
,
746 gchar
* const *blacklist
)
748 guint strv_len
, add_len
;
759 strv_len
= g_strv_length (strv
);
760 add_len
= g_strv_length (to_add
);
761 strv
= g_renew (gchar
*, strv
, strv_len
+ add_len
+ 1);
763 for (i
= 0; to_add
[i
]; i
++)
765 /* Don't add blacklisted strings */
767 for (j
= 0; blacklist
[j
]; j
++)
768 if (g_str_equal (to_add
[i
], blacklist
[j
]))
771 /* Don't add duplicates already in the list */
772 for (j
= 0; j
< strv_len
; j
++)
773 if (g_str_equal (to_add
[i
], strv
[j
]))
776 strv
[strv_len
++] = to_add
[i
];
783 strv
[strv_len
] = NULL
;
790 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir
*dir
,
791 const gchar
*filename
,
792 const gchar
*added_group
,
793 gboolean tweaks_permitted
)
795 UnindexedMimeTweaks
*tweaks
;
796 char **desktop_file_ids
;
801 key_file
= g_key_file_new ();
802 if (!g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
))
804 g_key_file_free (key_file
);
808 mime_types
= g_key_file_get_keys (key_file
, added_group
, NULL
, NULL
);
810 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
812 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
813 "mimeapps.list file may add or remove associations.", filename
, added_group
);
814 g_strfreev (mime_types
);
818 if (mime_types
!= NULL
)
820 for (i
= 0; mime_types
[i
] != NULL
; i
++)
822 desktop_file_ids
= g_key_file_get_string_list (key_file
, added_group
, mime_types
[i
], NULL
, NULL
);
824 if (desktop_file_ids
)
826 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
827 expand_strv (&tweaks
->additions
, desktop_file_ids
, tweaks
->removals
);
831 g_strfreev (mime_types
);
834 mime_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
836 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
838 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
839 "mimeapps.list file may add or remove associations.", filename
, REMOVED_ASSOCIATIONS_GROUP
);
840 g_strfreev (mime_types
);
844 if (mime_types
!= NULL
)
846 for (i
= 0; mime_types
[i
] != NULL
; i
++)
848 desktop_file_ids
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
850 if (desktop_file_ids
)
852 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
853 expand_strv (&tweaks
->removals
, desktop_file_ids
, tweaks
->additions
);
857 g_strfreev (mime_types
);
860 mime_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
862 if (mime_types
!= NULL
)
864 for (i
= 0; mime_types
[i
] != NULL
; i
++)
866 desktop_file_ids
= g_key_file_get_string_list (key_file
, DEFAULT_APPLICATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
868 if (desktop_file_ids
)
870 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
871 expand_strv (&tweaks
->defaults
, desktop_file_ids
, NULL
);
875 g_strfreev (mime_types
);
878 g_key_file_free (key_file
);
882 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir
*dir
)
884 const gchar
* const *desktops
;
888 dir
->mime_tweaks
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, free_mime_tweaks
);
890 /* We process in order of precedence, using a blacklisting approach to
891 * avoid recording later instructions that conflict with ones we found
894 * We first start with the XDG_CURRENT_DESKTOP files, in precedence
897 desktops
= get_lowercase_current_desktops ();
898 for (i
= 0; desktops
[i
]; i
++)
900 filename
= g_strdup_printf ("%s/%s-mimeapps.list", dir
->path
, desktops
[i
]);
901 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
905 /* Next, the non-desktop-specific mimeapps.list */
906 filename
= g_strdup_printf ("%s/mimeapps.list", dir
->path
);
907 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, TRUE
);
910 /* The remaining files are only checked for in directories that might
911 * contain desktop files (ie: not the config dirs).
916 /* We have 'defaults.list' which was only ever understood by GLib. It
917 * exists widely, but it has never been part of any spec and it should
918 * be treated as deprecated. This will be removed in a future
921 filename
= g_strdup_printf ("%s/defaults.list", dir
->path
);
922 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
925 /* Finally, the mimeinfo.cache, which is just a cached copy of what we
926 * would find in the MimeTypes= lines of all of the desktop files.
928 filename
= g_strdup_printf ("%s/mimeinfo.cache", dir
->path
);
929 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, MIME_CACHE_GROUP
, TRUE
);
934 desktop_file_dir_unindexed_init (DesktopFileDir
*dir
)
937 get_apps_from_dir (&dir
->app_names
, dir
->path
, "");
939 desktop_file_dir_unindexed_read_mimeapps_lists (dir
);
942 static GDesktopAppInfo
*
943 desktop_file_dir_unindexed_get_app (DesktopFileDir
*dir
,
944 const gchar
*desktop_id
)
946 const gchar
*filename
;
948 filename
= g_hash_table_lookup (dir
->app_names
, desktop_id
);
953 return g_desktop_app_info_new_from_filename (filename
);
957 desktop_file_dir_unindexed_get_all (DesktopFileDir
*dir
,
964 if (dir
->app_names
== NULL
)
967 g_hash_table_iter_init (&iter
, dir
->app_names
);
968 while (g_hash_table_iter_next (&iter
, &app_name
, &filename
))
970 if (desktop_file_dir_app_name_is_masked (dir
, app_name
))
973 add_to_table_if_appropriate (apps
, app_name
, g_desktop_app_info_new_from_filename (filename
));
977 typedef struct _MemoryIndexEntry MemoryIndexEntry
;
978 typedef GHashTable MemoryIndex
;
980 struct _MemoryIndexEntry
982 const gchar
*app_name
; /* pointer to the hashtable key */
984 MemoryIndexEntry
*next
;
988 memory_index_entry_free (gpointer data
)
990 MemoryIndexEntry
*mie
= data
;
994 MemoryIndexEntry
*next
= mie
->next
;
996 g_slice_free (MemoryIndexEntry
, mie
);
1002 memory_index_add_token (MemoryIndex
*mi
,
1004 gint match_category
,
1005 const gchar
*app_name
)
1007 MemoryIndexEntry
*mie
, *first
;
1009 mie
= g_slice_new (MemoryIndexEntry
);
1010 mie
->app_name
= app_name
;
1011 mie
->match_category
= match_category
;
1013 first
= g_hash_table_lookup (mi
, token
);
1017 mie
->next
= first
->next
;
1023 g_hash_table_insert (mi
, g_strdup (token
), mie
);
1028 memory_index_add_string (MemoryIndex
*mi
,
1029 const gchar
*string
,
1030 gint match_category
,
1031 const gchar
*app_name
)
1033 gchar
**tokens
, **alternates
;
1036 tokens
= g_str_tokenize_and_fold (string
, NULL
, &alternates
);
1038 for (i
= 0; tokens
[i
]; i
++)
1039 memory_index_add_token (mi
, tokens
[i
], match_category
, app_name
);
1041 for (i
= 0; alternates
[i
]; i
++)
1042 memory_index_add_token (mi
, alternates
[i
], match_category
, app_name
);
1044 g_strfreev (alternates
);
1045 g_strfreev (tokens
);
1048 static MemoryIndex
*
1049 memory_index_new (void)
1051 return g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, memory_index_entry_free
);
1055 desktop_file_dir_unindexed_setup_search (DesktopFileDir
*dir
)
1057 GHashTableIter iter
;
1060 dir
->memory_index
= memory_index_new ();
1061 dir
->memory_implementations
= memory_index_new ();
1063 /* Nothing to search? */
1064 if (dir
->app_names
== NULL
)
1067 g_hash_table_iter_init (&iter
, dir
->app_names
);
1068 while (g_hash_table_iter_next (&iter
, &app
, &path
))
1072 if (desktop_file_dir_app_name_is_masked (dir
, app
))
1075 key_file
= g_key_file_new ();
1077 if (g_key_file_load_from_file (key_file
, path
, G_KEY_FILE_NONE
, NULL
) &&
1078 !g_key_file_get_boolean (key_file
, "Desktop Entry", "Hidden", NULL
))
1080 /* Index the interesting keys... */
1084 for (i
= 0; i
< G_N_ELEMENTS (desktop_key_match_category
); i
++)
1089 if (!desktop_key_match_category
[i
])
1092 raw
= g_key_file_get_locale_string (key_file
, "Desktop Entry", desktop_key_get_name (i
), NULL
, NULL
);
1095 if (i
== DESKTOP_KEY_Exec
&& raw
!= NULL
)
1097 /* Special handling: only match basename of first field */
1101 /* Remove extra arguments, if any */
1102 space
= raw
+ strcspn (raw
, " \t\n"); /* IFS */
1105 /* Skip the pathname, if any */
1106 if ((slash
= strrchr (raw
, '/')))
1109 /* Don't match on blacklisted binaries like interpreters */
1110 if (g_strv_contains (exec_key_match_blacklist
, value
))
1115 memory_index_add_string (dir
->memory_index
, value
, desktop_key_match_category
[i
], app
);
1120 /* Make note of the Implements= line */
1121 implements
= g_key_file_get_string_list (key_file
, "Desktop Entry", "Implements", NULL
, NULL
);
1122 for (i
= 0; implements
&& implements
[i
]; i
++)
1123 memory_index_add_token (dir
->memory_implementations
, implements
[i
], 0, app
);
1124 g_strfreev (implements
);
1127 g_key_file_free (key_file
);
1132 desktop_file_dir_unindexed_search (DesktopFileDir
*dir
,
1133 const gchar
*search_token
)
1135 GHashTableIter iter
;
1136 gpointer key
, value
;
1138 if (!dir
->memory_index
)
1139 desktop_file_dir_unindexed_setup_search (dir
);
1141 g_hash_table_iter_init (&iter
, dir
->memory_index
);
1142 while (g_hash_table_iter_next (&iter
, &key
, &value
))
1144 MemoryIndexEntry
*mie
= value
;
1146 if (!g_str_has_prefix (key
, search_token
))
1151 add_token_result (mie
->app_name
, mie
->match_category
);
1158 array_contains (GPtrArray
*array
,
1163 for (i
= 0; i
< array
->len
; i
++)
1164 if (g_str_equal (array
->pdata
[i
], str
))
1171 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir
*dir
,
1172 const gchar
*mime_type
,
1174 GPtrArray
*blacklist
)
1176 UnindexedMimeTweaks
*tweaks
;
1179 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1184 if (tweaks
->additions
)
1186 for (i
= 0; tweaks
->additions
[i
]; i
++)
1188 gchar
*app_name
= tweaks
->additions
[i
];
1190 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1191 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1192 g_ptr_array_add (hits
, app_name
);
1196 if (tweaks
->removals
)
1198 for (i
= 0; tweaks
->removals
[i
]; i
++)
1200 gchar
*app_name
= tweaks
->removals
[i
];
1202 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1203 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1204 g_ptr_array_add (blacklist
, app_name
);
1210 desktop_file_dir_unindexed_default_lookup (DesktopFileDir
*dir
,
1211 const gchar
*mime_type
,
1214 UnindexedMimeTweaks
*tweaks
;
1217 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1219 if (!tweaks
|| !tweaks
->defaults
)
1222 for (i
= 0; tweaks
->defaults
[i
]; i
++)
1224 gchar
*app_name
= tweaks
->defaults
[i
];
1226 if (!array_contains (results
, app_name
))
1227 g_ptr_array_add (results
, app_name
);
1232 desktop_file_dir_unindexed_get_implementations (DesktopFileDir
*dir
,
1234 const gchar
*interface
)
1236 MemoryIndexEntry
*mie
;
1238 if (!dir
->memory_index
)
1239 desktop_file_dir_unindexed_setup_search (dir
);
1241 for (mie
= g_hash_table_lookup (dir
->memory_implementations
, interface
); mie
; mie
= mie
->next
)
1242 *results
= g_list_prepend (*results
, g_strdup (mie
->app_name
));
1245 /* DesktopFileDir "API" {{{2 */
1248 * desktop_file_dir_create:
1249 * @array: the #GArray to add a new item to
1250 * @data_dir: an XDG_DATA_DIR
1252 * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
1256 desktop_file_dir_create (GArray
*array
,
1257 const gchar
*data_dir
)
1259 DesktopFileDir dir
= { 0, };
1261 dir
.path
= g_build_filename (data_dir
, "applications", NULL
);
1263 g_array_append_val (array
, dir
);
1267 * desktop_file_dir_create:
1268 * @array: the #GArray to add a new item to
1269 * @config_dir: an XDG_CONFIG_DIR
1271 * Just the same as desktop_file_dir_create() except that it does not
1272 * add the "applications" directory. It also marks the directory as
1273 * config-only, which prevents us from attempting to find desktop files
1277 desktop_file_dir_create_for_config (GArray
*array
,
1278 const gchar
*config_dir
)
1280 DesktopFileDir dir
= { 0, };
1282 dir
.path
= g_strdup (config_dir
);
1283 dir
.is_config
= TRUE
;
1285 g_array_append_val (array
, dir
);
1289 * desktop_file_dir_reset:
1290 * @dir: a #DesktopFileDir
1292 * Cleans up @dir, releasing most resources that it was using.
1295 desktop_file_dir_reset (DesktopFileDir
*dir
)
1297 if (dir
->alternatively_watching
)
1299 g_free (dir
->alternatively_watching
);
1300 dir
->alternatively_watching
= NULL
;
1305 g_signal_handlers_disconnect_by_func (dir
->monitor
, desktop_file_dir_changed
, dir
);
1306 g_object_unref (dir
->monitor
);
1307 dir
->monitor
= NULL
;
1312 g_hash_table_unref (dir
->app_names
);
1313 dir
->app_names
= NULL
;
1316 if (dir
->memory_index
)
1318 g_hash_table_unref (dir
->memory_index
);
1319 dir
->memory_index
= NULL
;
1322 if (dir
->mime_tweaks
)
1324 g_hash_table_unref (dir
->mime_tweaks
);
1325 dir
->mime_tweaks
= NULL
;
1328 if (dir
->memory_implementations
)
1330 g_hash_table_unref (dir
->memory_implementations
);
1331 dir
->memory_implementations
= NULL
;
1334 dir
->is_setup
= FALSE
;
1338 * desktop_file_dir_init:
1339 * @dir: a #DesktopFileDir
1341 * Does initial setup for @dir
1343 * You should only call this if @dir is not already setup.
1346 desktop_file_dir_init (DesktopFileDir
*dir
)
1348 const gchar
*watch_dir
;
1350 g_assert (!dir
->is_setup
);
1352 g_assert (!dir
->alternatively_watching
);
1353 g_assert (!dir
->monitor
);
1355 dir
->alternatively_watching
= desktop_file_dir_get_alternative_dir (dir
);
1356 watch_dir
= dir
->alternatively_watching
? dir
->alternatively_watching
: dir
->path
;
1358 /* There is a very thin race here if the watch_dir has been _removed_
1359 * between when we checked for it and when we establish the watch.
1360 * Removes probably don't happen in usual operation, and even if it
1361 * does (and we catch the unlikely race), the only degradation is that
1362 * we will fall back to polling.
1364 dir
->monitor
= g_local_file_monitor_new_in_worker (watch_dir
, TRUE
, G_FILE_MONITOR_NONE
,
1365 desktop_file_dir_changed
, dir
, NULL
);
1367 desktop_file_dir_unindexed_init (dir
);
1369 dir
->is_setup
= TRUE
;
1373 * desktop_file_dir_get_app:
1374 * @dir: a DesktopFileDir
1375 * @desktop_id: the desktop ID to load
1377 * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1378 * within @dir, even if it is hidden.
1380 * This function does not check if @desktop_id would be masked by a
1381 * directory with higher precedence. The caller must do so.
1383 static GDesktopAppInfo
*
1384 desktop_file_dir_get_app (DesktopFileDir
*dir
,
1385 const gchar
*desktop_id
)
1387 if (!dir
->app_names
)
1390 return desktop_file_dir_unindexed_get_app (dir
, desktop_id
);
1394 * desktop_file_dir_get_all:
1395 * @dir: a DesktopFileDir
1396 * @apps: a #GHashTable<string, GDesktopAppInfo>
1398 * Loads all desktop files in @dir and adds them to @apps, careful to
1399 * ensure we don't add any files masked by a similarly-named file in a
1400 * higher-precedence directory.
1403 desktop_file_dir_get_all (DesktopFileDir
*dir
,
1406 desktop_file_dir_unindexed_get_all (dir
, apps
);
1410 * desktop_file_dir_mime_lookup:
1411 * @dir: a #DesktopFileDir
1412 * @mime_type: the mime type to look up
1413 * @hits: the array to store the hits
1414 * @blacklist: the array to store the blacklist
1416 * Does a lookup of a mimetype against one desktop file directory,
1417 * recording any hits and blacklisting and "Removed" associations (so
1418 * later directories don't record them as hits).
1420 * The items added to @hits are duplicated, but the ones in @blacklist
1421 * are weak pointers. This facilitates simply freeing the blacklist
1422 * (which is only used for internal bookkeeping) but using the pdata of
1423 * @hits as the result of the operation.
1426 desktop_file_dir_mime_lookup (DesktopFileDir
*dir
,
1427 const gchar
*mime_type
,
1429 GPtrArray
*blacklist
)
1431 desktop_file_dir_unindexed_mime_lookup (dir
, mime_type
, hits
, blacklist
);
1435 * desktop_file_dir_default_lookup:
1436 * @dir: a #DesktopFileDir
1437 * @mime_type: the mime type to look up
1438 * @results: an array to store the results in
1440 * Collects the "default" applications for a given mime type from @dir.
1443 desktop_file_dir_default_lookup (DesktopFileDir
*dir
,
1444 const gchar
*mime_type
,
1447 desktop_file_dir_unindexed_default_lookup (dir
, mime_type
, results
);
1451 * desktop_file_dir_search:
1452 * @dir: a #DesktopFileDir
1453 * @term: a normalised and casefolded search term
1455 * Finds the names of applications in @dir that match @term.
1458 desktop_file_dir_search (DesktopFileDir
*dir
,
1459 const gchar
*search_token
)
1461 desktop_file_dir_unindexed_search (dir
, search_token
);
1465 desktop_file_dir_get_implementations (DesktopFileDir
*dir
,
1467 const gchar
*interface
)
1469 desktop_file_dir_unindexed_get_implementations (dir
, results
, interface
);
1472 /* Lock/unlock and global setup API {{{2 */
1475 desktop_file_dirs_lock (void)
1479 g_mutex_lock (&desktop_file_dir_lock
);
1481 if (desktop_file_dirs
== NULL
)
1483 const char * const *dirs
;
1487 tmp
= g_array_new (FALSE
, FALSE
, sizeof (DesktopFileDir
));
1489 /* First, the configs. Highest priority: the user's ~/.config */
1490 desktop_file_dir_create_for_config (tmp
, g_get_user_config_dir ());
1492 /* Next, the system configs (/etc/xdg, and so on). */
1493 dirs
= g_get_system_config_dirs ();
1494 for (i
= 0; dirs
[i
]; i
++)
1495 desktop_file_dir_create_for_config (tmp
, dirs
[i
]);
1497 /* Now the data. Highest priority: the user's ~/.local/share/applications */
1498 desktop_file_dir_user_data_index
= tmp
->len
;
1499 desktop_file_dir_create (tmp
, g_get_user_data_dir ());
1501 /* Following that, XDG_DATA_DIRS/applications, in order */
1502 dirs
= g_get_system_data_dirs ();
1503 for (i
= 0; dirs
[i
]; i
++)
1504 desktop_file_dir_create (tmp
, dirs
[i
]);
1506 /* The list of directories will never change after this. */
1507 desktop_file_dirs
= (DesktopFileDir
*) tmp
->data
;
1508 n_desktop_file_dirs
= tmp
->len
;
1510 g_array_free (tmp
, FALSE
);
1513 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1514 if (!desktop_file_dirs
[i
].is_setup
)
1515 desktop_file_dir_init (&desktop_file_dirs
[i
]);
1519 desktop_file_dirs_unlock (void)
1521 g_mutex_unlock (&desktop_file_dir_lock
);
1525 desktop_file_dirs_invalidate_user_config (void)
1527 g_mutex_lock (&desktop_file_dir_lock
);
1529 if (n_desktop_file_dirs
)
1530 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_config_index
]);
1532 g_mutex_unlock (&desktop_file_dir_lock
);
1536 desktop_file_dirs_invalidate_user_data (void)
1538 g_mutex_lock (&desktop_file_dir_lock
);
1540 if (n_desktop_file_dirs
)
1541 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_data_index
]);
1543 g_mutex_unlock (&desktop_file_dir_lock
);
1546 /* GDesktopAppInfo implementation {{{1 */
1547 /* GObject implementation {{{2 */
1549 g_desktop_app_info_finalize (GObject
*object
)
1551 GDesktopAppInfo
*info
;
1553 info
= G_DESKTOP_APP_INFO (object
);
1555 g_free (info
->desktop_id
);
1556 g_free (info
->filename
);
1559 g_key_file_unref (info
->keyfile
);
1561 g_free (info
->name
);
1562 g_free (info
->generic_name
);
1563 g_free (info
->fullname
);
1564 g_free (info
->comment
);
1565 g_free (info
->icon_name
);
1567 g_object_unref (info
->icon
);
1568 g_strfreev (info
->keywords
);
1569 g_strfreev (info
->only_show_in
);
1570 g_strfreev (info
->not_show_in
);
1571 g_free (info
->try_exec
);
1572 g_free (info
->exec
);
1573 g_free (info
->binary
);
1574 g_free (info
->path
);
1575 g_free (info
->categories
);
1576 g_free (info
->startup_wm_class
);
1577 g_strfreev (info
->mime_types
);
1578 g_free (info
->app_id
);
1579 g_strfreev (info
->actions
);
1581 G_OBJECT_CLASS (g_desktop_app_info_parent_class
)->finalize (object
);
1585 g_desktop_app_info_set_property (GObject
*object
,
1587 const GValue
*value
,
1590 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1595 self
->filename
= g_value_dup_string (value
);
1599 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1605 g_desktop_app_info_get_property (GObject
*object
,
1610 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1615 g_value_set_string (value
, self
->filename
);
1618 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1624 g_desktop_app_info_class_init (GDesktopAppInfoClass
*klass
)
1626 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
1628 gobject_class
->get_property
= g_desktop_app_info_get_property
;
1629 gobject_class
->set_property
= g_desktop_app_info_set_property
;
1630 gobject_class
->finalize
= g_desktop_app_info_finalize
;
1633 * GDesktopAppInfo:filename:
1635 * The origin filename of this #GDesktopAppInfo
1637 g_object_class_install_property (gobject_class
,
1639 g_param_spec_string ("filename", "Filename", "", NULL
,
1640 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1644 g_desktop_app_info_init (GDesktopAppInfo
*local
)
1648 /* Construction... {{{2 */
1652 * @exec: an exec line
1654 * Returns the first word in an exec line (ie: the binary name).
1656 * If @exec is " progname --foo %F" then returns "progname".
1659 binary_from_exec (const char *exec
)
1661 const char *p
, *start
;
1667 while (*p
!= ' ' && *p
!= 0)
1670 return g_strndup (start
, p
- start
);
1674 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo
*info
,
1681 gboolean bus_activatable
;
1683 start_group
= g_key_file_get_start_group (key_file
);
1684 if (start_group
== NULL
|| strcmp (start_group
, G_KEY_FILE_DESKTOP_GROUP
) != 0)
1686 g_free (start_group
);
1689 g_free (start_group
);
1691 type
= g_key_file_get_string (key_file
,
1692 G_KEY_FILE_DESKTOP_GROUP
,
1693 G_KEY_FILE_DESKTOP_KEY_TYPE
,
1695 if (type
== NULL
|| strcmp (type
, G_KEY_FILE_DESKTOP_TYPE_APPLICATION
) != 0)
1702 try_exec
= g_key_file_get_string (key_file
,
1703 G_KEY_FILE_DESKTOP_GROUP
,
1704 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC
,
1706 if (try_exec
&& try_exec
[0] != '\0')
1709 t
= g_find_program_in_path (try_exec
);
1718 exec
= g_key_file_get_string (key_file
,
1719 G_KEY_FILE_DESKTOP_GROUP
,
1720 G_KEY_FILE_DESKTOP_KEY_EXEC
,
1722 if (exec
&& exec
[0] != '\0')
1726 if (!g_shell_parse_argv (exec
, &argc
, &argv
, NULL
))
1735 t
= g_find_program_in_path (argv
[0]);
1748 info
->name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NAME
, NULL
, NULL
);
1749 info
->generic_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, GENERIC_NAME_KEY
, NULL
, NULL
);
1750 info
->fullname
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, FULL_NAME_KEY
, NULL
, NULL
);
1751 info
->keywords
= g_key_file_get_locale_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, KEYWORDS_KEY
, NULL
, NULL
, NULL
);
1752 info
->comment
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_COMMENT
, NULL
, NULL
);
1753 info
->nodisplay
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, NULL
) != FALSE
;
1754 info
->icon_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ICON
, NULL
, NULL
);
1755 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
);
1756 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
);
1757 info
->try_exec
= try_exec
;
1759 info
->path
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_PATH
, NULL
);
1760 info
->terminal
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_TERMINAL
, NULL
) != FALSE
;
1761 info
->startup_notify
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY
, NULL
) != FALSE
;
1762 info
->no_fuse
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, "X-GIO-NoFuse", NULL
) != FALSE
;
1763 info
->hidden
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_HIDDEN
, NULL
) != FALSE
;
1764 info
->categories
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_CATEGORIES
, NULL
);
1765 info
->startup_wm_class
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, STARTUP_WM_CLASS_KEY
, NULL
);
1766 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
);
1767 bus_activatable
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE
, NULL
);
1768 info
->actions
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ACTIONS
, NULL
, NULL
);
1770 /* Remove the special-case: no Actions= key just means 0 extra actions */
1771 if (info
->actions
== NULL
)
1772 info
->actions
= g_new0 (gchar
*, 0 + 1);
1775 if (info
->icon_name
)
1777 if (g_path_is_absolute (info
->icon_name
))
1781 file
= g_file_new_for_path (info
->icon_name
);
1782 info
->icon
= g_file_icon_new (file
);
1783 g_object_unref (file
);
1789 /* Work around a common mistake in desktop files */
1790 if ((p
= strrchr (info
->icon_name
, '.')) != NULL
&&
1791 (strcmp (p
, ".png") == 0 ||
1792 strcmp (p
, ".xpm") == 0 ||
1793 strcmp (p
, ".svg") == 0))
1796 info
->icon
= g_themed_icon_new (info
->icon_name
);
1801 info
->binary
= binary_from_exec (info
->exec
);
1803 if (info
->path
&& info
->path
[0] == '\0')
1805 g_free (info
->path
);
1809 /* Can only be DBusActivatable if we know the filename, which means
1810 * that this won't work for the load-from-keyfile case.
1812 if (bus_activatable
&& info
->filename
)
1817 basename
= g_path_get_basename (info
->filename
);
1818 last_dot
= strrchr (basename
, '.');
1820 if (last_dot
&& g_str_equal (last_dot
, ".desktop"))
1824 if (g_dbus_is_name (basename
) && basename
[0] != ':')
1825 info
->app_id
= g_strdup (basename
);
1831 info
->keyfile
= g_key_file_ref (key_file
);
1837 g_desktop_app_info_load_file (GDesktopAppInfo
*self
)
1840 gboolean retval
= FALSE
;
1842 g_return_val_if_fail (self
->filename
!= NULL
, FALSE
);
1844 self
->desktop_id
= g_path_get_basename (self
->filename
);
1846 key_file
= g_key_file_new ();
1848 if (g_key_file_load_from_file (key_file
, self
->filename
, G_KEY_FILE_NONE
, NULL
))
1849 retval
= g_desktop_app_info_load_from_keyfile (self
, key_file
);
1851 g_key_file_unref (key_file
);
1856 * g_desktop_app_info_new_from_keyfile:
1857 * @key_file: an opened #GKeyFile
1859 * Creates a new #GDesktopAppInfo.
1861 * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error.
1866 g_desktop_app_info_new_from_keyfile (GKeyFile
*key_file
)
1868 GDesktopAppInfo
*info
;
1870 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1871 info
->filename
= NULL
;
1872 if (!g_desktop_app_info_load_from_keyfile (info
, key_file
))
1874 g_object_unref (info
);
1881 * g_desktop_app_info_new_from_filename:
1882 * @filename: (type filename): the path of a desktop file, in the GLib
1885 * Creates a new #GDesktopAppInfo.
1887 * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error.
1890 g_desktop_app_info_new_from_filename (const char *filename
)
1892 GDesktopAppInfo
*info
= NULL
;
1894 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, "filename", filename
, NULL
);
1895 if (!g_desktop_app_info_load_file (info
))
1897 g_object_unref (info
);
1904 * g_desktop_app_info_new:
1905 * @desktop_id: the desktop file id
1907 * Creates a new #GDesktopAppInfo based on a desktop file id.
1909 * A desktop file id is the basename of the desktop file, including the
1910 * .desktop extension. GIO is looking for a desktop file with this name
1911 * in the `applications` subdirectories of the XDG
1912 * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1913 * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1914 * prefix-to-subdirectory mapping that is described in the
1915 * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1916 * (i.e. a desktop id of kde-foo.desktop will match
1917 * `/usr/share/applications/kde/foo.desktop`).
1919 * Returns: (nullable): a new #GDesktopAppInfo, or %NULL if no desktop
1920 * file with that id exists.
1923 g_desktop_app_info_new (const char *desktop_id
)
1925 GDesktopAppInfo
*appinfo
= NULL
;
1928 desktop_file_dirs_lock ();
1930 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1932 appinfo
= desktop_file_dir_get_app (&desktop_file_dirs
[i
], desktop_id
);
1938 desktop_file_dirs_unlock ();
1940 if (appinfo
== NULL
)
1943 g_free (appinfo
->desktop_id
);
1944 appinfo
->desktop_id
= g_strdup (desktop_id
);
1946 if (g_desktop_app_info_get_is_hidden (appinfo
))
1948 g_object_unref (appinfo
);
1956 g_desktop_app_info_dup (GAppInfo
*appinfo
)
1958 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1959 GDesktopAppInfo
*new_info
;
1961 new_info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1963 new_info
->filename
= g_strdup (info
->filename
);
1964 new_info
->desktop_id
= g_strdup (info
->desktop_id
);
1967 new_info
->keyfile
= g_key_file_ref (info
->keyfile
);
1969 new_info
->name
= g_strdup (info
->name
);
1970 new_info
->generic_name
= g_strdup (info
->generic_name
);
1971 new_info
->fullname
= g_strdup (info
->fullname
);
1972 new_info
->keywords
= g_strdupv (info
->keywords
);
1973 new_info
->comment
= g_strdup (info
->comment
);
1974 new_info
->nodisplay
= info
->nodisplay
;
1975 new_info
->icon_name
= g_strdup (info
->icon_name
);
1977 new_info
->icon
= g_object_ref (info
->icon
);
1978 new_info
->only_show_in
= g_strdupv (info
->only_show_in
);
1979 new_info
->not_show_in
= g_strdupv (info
->not_show_in
);
1980 new_info
->try_exec
= g_strdup (info
->try_exec
);
1981 new_info
->exec
= g_strdup (info
->exec
);
1982 new_info
->binary
= g_strdup (info
->binary
);
1983 new_info
->path
= g_strdup (info
->path
);
1984 new_info
->app_id
= g_strdup (info
->app_id
);
1985 new_info
->hidden
= info
->hidden
;
1986 new_info
->terminal
= info
->terminal
;
1987 new_info
->startup_notify
= info
->startup_notify
;
1989 return G_APP_INFO (new_info
);
1992 /* GAppInfo interface implementation functions {{{2 */
1995 g_desktop_app_info_equal (GAppInfo
*appinfo1
,
1998 GDesktopAppInfo
*info1
= G_DESKTOP_APP_INFO (appinfo1
);
1999 GDesktopAppInfo
*info2
= G_DESKTOP_APP_INFO (appinfo2
);
2001 if (info1
->desktop_id
== NULL
||
2002 info2
->desktop_id
== NULL
)
2003 return info1
== info2
;
2005 return strcmp (info1
->desktop_id
, info2
->desktop_id
) == 0;
2009 g_desktop_app_info_get_id (GAppInfo
*appinfo
)
2011 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2013 return info
->desktop_id
;
2017 g_desktop_app_info_get_name (GAppInfo
*appinfo
)
2019 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2021 if (info
->name
== NULL
)
2022 return _("Unnamed");
2027 g_desktop_app_info_get_display_name (GAppInfo
*appinfo
)
2029 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2031 if (info
->fullname
== NULL
)
2032 return g_desktop_app_info_get_name (appinfo
);
2033 return info
->fullname
;
2037 * g_desktop_app_info_get_is_hidden:
2038 * @info: a #GDesktopAppInfo.
2040 * A desktop file is hidden if the Hidden key in it is
2043 * Returns: %TRUE if hidden, %FALSE otherwise.
2046 g_desktop_app_info_get_is_hidden (GDesktopAppInfo
*info
)
2048 return info
->hidden
;
2052 * g_desktop_app_info_get_filename:
2053 * @info: a #GDesktopAppInfo
2055 * When @info was created from a known filename, return it. In some
2056 * situations such as the #GDesktopAppInfo returned from
2057 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
2059 * Returns: (type filename): The full path to the file for @info,
2060 * or %NULL if not known.
2064 g_desktop_app_info_get_filename (GDesktopAppInfo
*info
)
2066 return info
->filename
;
2070 g_desktop_app_info_get_description (GAppInfo
*appinfo
)
2072 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2074 return info
->comment
;
2078 g_desktop_app_info_get_executable (GAppInfo
*appinfo
)
2080 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2082 return info
->binary
;
2086 g_desktop_app_info_get_commandline (GAppInfo
*appinfo
)
2088 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2094 g_desktop_app_info_get_icon (GAppInfo
*appinfo
)
2096 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2102 * g_desktop_app_info_get_categories:
2103 * @info: a #GDesktopAppInfo
2105 * Gets the categories from the desktop file.
2107 * Returns: The unparsed Categories key from the desktop file;
2108 * i.e. no attempt is made to split it by ';' or validate it.
2111 g_desktop_app_info_get_categories (GDesktopAppInfo
*info
)
2113 return info
->categories
;
2117 * g_desktop_app_info_get_keywords:
2118 * @info: a #GDesktopAppInfo
2120 * Gets the keywords from the desktop file.
2122 * Returns: (transfer none): The value of the Keywords key
2126 const char * const *
2127 g_desktop_app_info_get_keywords (GDesktopAppInfo
*info
)
2129 return (const char * const *)info
->keywords
;
2133 * g_desktop_app_info_get_generic_name:
2134 * @info: a #GDesktopAppInfo
2136 * Gets the generic name from the destkop file.
2138 * Returns: The value of the GenericName key
2141 g_desktop_app_info_get_generic_name (GDesktopAppInfo
*info
)
2143 return info
->generic_name
;
2147 * g_desktop_app_info_get_nodisplay:
2148 * @info: a #GDesktopAppInfo
2150 * Gets the value of the NoDisplay key, which helps determine if the
2151 * application info should be shown in menus. See
2152 * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2154 * Returns: The value of the NoDisplay key
2159 g_desktop_app_info_get_nodisplay (GDesktopAppInfo
*info
)
2161 return info
->nodisplay
;
2165 * g_desktop_app_info_get_show_in:
2166 * @info: a #GDesktopAppInfo
2167 * @desktop_env: (nullable): a string specifying a desktop name
2169 * Checks if the application info should be shown in menus that list available
2170 * applications for a specific name of the desktop, based on the
2171 * `OnlyShowIn` and `NotShowIn` keys.
2173 * @desktop_env should typically be given as %NULL, in which case the
2174 * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
2175 * to override the default mechanism then you may specify @desktop_env,
2176 * but this is not recommended.
2178 * Note that g_app_info_should_show() for @info will include this check (with
2179 * %NULL for @desktop_env) as well as additional checks.
2181 * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2182 * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2188 g_desktop_app_info_get_show_in (GDesktopAppInfo
*info
,
2189 const gchar
*desktop_env
)
2191 const gchar
*specified_envs
[] = { desktop_env
, NULL
};
2192 const gchar
* const *envs
;
2195 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
2198 envs
= specified_envs
;
2200 envs
= get_current_desktops (NULL
);
2202 for (i
= 0; envs
[i
]; i
++)
2206 if (info
->only_show_in
)
2207 for (j
= 0; info
->only_show_in
[j
]; j
++)
2208 if (g_str_equal (info
->only_show_in
[j
], envs
[i
]))
2211 if (info
->not_show_in
)
2212 for (j
= 0; info
->not_show_in
[j
]; j
++)
2213 if (g_str_equal (info
->not_show_in
[j
], envs
[i
]))
2217 return info
->only_show_in
== NULL
;
2220 /* Launching... {{{2 */
2223 expand_macro_single (char macro
, const char *uri
)
2226 char *result
= NULL
;
2230 file
= g_file_new_for_uri (uri
);
2236 result
= g_shell_quote (uri
);
2240 path
= g_file_get_path (file
);
2242 result
= g_shell_quote (path
);
2246 path
= g_file_get_path (file
);
2249 name
= g_path_get_dirname (path
);
2250 result
= g_shell_quote (name
);
2256 path
= g_file_get_path (file
);
2259 name
= g_path_get_basename (path
);
2260 result
= g_shell_quote (name
);
2266 g_object_unref (file
);
2273 expand_macro_uri (char macro
, const char *uri
, gboolean force_file_uri
, char force_file_uri_macro
)
2275 char *expanded
= NULL
;
2277 g_return_val_if_fail (uri
!= NULL
, NULL
);
2279 if (!force_file_uri
||
2280 /* Pass URI if it contains an anchor */
2281 strchr (uri
, '#') != NULL
)
2283 expanded
= expand_macro_single (macro
, uri
);
2287 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2288 if (expanded
== NULL
)
2289 expanded
= expand_macro_single (macro
, uri
);
2296 expand_macro (char macro
,
2298 GDesktopAppInfo
*info
,
2301 GList
*uris
= *uri_list
;
2302 char *expanded
= NULL
;
2303 gboolean force_file_uri
;
2304 char force_file_uri_macro
;
2307 g_return_if_fail (exec
!= NULL
);
2309 /* On %u and %U, pass POSIX file path pointing to the URI via
2310 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2311 * running or the URI doesn't have a POSIX file path via FUSE
2312 * we'll just pass the URI.
2314 force_file_uri_macro
= macro
;
2315 force_file_uri
= FALSE
;
2321 force_file_uri_macro
= 'f';
2322 force_file_uri
= TRUE
;
2325 force_file_uri_macro
= 'F';
2326 force_file_uri
= TRUE
;
2342 expanded
= expand_macro_uri (macro
, uri
,
2343 force_file_uri
, force_file_uri_macro
);
2346 g_string_append (exec
, expanded
);
2361 expanded
= expand_macro_uri (macro
, uri
,
2362 force_file_uri
, force_file_uri_macro
);
2365 g_string_append (exec
, expanded
);
2371 if (uris
!= NULL
&& expanded
)
2372 g_string_append_c (exec
, ' ');
2378 if (info
->icon_name
)
2380 g_string_append (exec
, "--icon ");
2381 expanded
= g_shell_quote (info
->icon_name
);
2382 g_string_append (exec
, expanded
);
2390 expanded
= g_shell_quote (info
->name
);
2391 g_string_append (exec
, expanded
);
2399 expanded
= g_shell_quote (info
->filename
);
2400 g_string_append (exec
, expanded
);
2405 case 'm': /* deprecated */
2409 g_string_append_c (exec
, '%');
2417 expand_application_parameters (GDesktopAppInfo
*info
,
2418 const gchar
*exec_line
,
2424 GList
*uri_list
= *uris
;
2425 const char *p
= exec_line
;
2426 GString
*expanded_exec
;
2429 if (exec_line
== NULL
)
2431 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2432 _("Desktop file didn’t specify Exec field"));
2436 expanded_exec
= g_string_new (NULL
);
2440 if (p
[0] == '%' && p
[1] != '\0')
2442 expand_macro (p
[1], expanded_exec
, info
, uris
);
2446 g_string_append_c (expanded_exec
, *p
);
2451 /* No file substitutions */
2452 if (uri_list
== *uris
&& uri_list
!= NULL
)
2454 /* If there is no macro default to %f. This is also what KDE does */
2455 g_string_append_c (expanded_exec
, ' ');
2456 expand_macro ('f', expanded_exec
, info
, uris
);
2459 res
= g_shell_parse_argv (expanded_exec
->str
, argc
, argv
, error
);
2460 g_string_free (expanded_exec
, TRUE
);
2465 prepend_terminal_to_vector (int *argc
,
2472 char **term_argv
= NULL
;
2477 g_return_val_if_fail (argc
!= NULL
, FALSE
);
2478 g_return_val_if_fail (argv
!= NULL
, FALSE
);
2486 /* compute size if not given */
2489 for (i
= 0; the_argv
[i
] != NULL
; i
++)
2495 term_argv
= g_new0 (char *, 3);
2497 check
= g_find_program_in_path ("gnome-terminal");
2500 term_argv
[0] = check
;
2501 /* Note that gnome-terminal takes -x and
2502 * as -e in gnome-terminal is broken we use that. */
2503 term_argv
[1] = g_strdup ("-x");
2508 check
= g_find_program_in_path ("nxterm");
2510 check
= g_find_program_in_path ("color-xterm");
2512 check
= g_find_program_in_path ("rxvt");
2514 check
= g_find_program_in_path ("dtterm");
2517 check
= g_strdup ("xterm");
2518 g_debug ("Couldn’t find a terminal: falling back to xterm");
2520 term_argv
[0] = check
;
2521 term_argv
[1] = g_strdup ("-e");
2524 real_argc
= term_argc
+ *argc
;
2525 real_argv
= g_new (char *, real_argc
+ 1);
2527 for (i
= 0; i
< term_argc
; i
++)
2528 real_argv
[i
] = term_argv
[i
];
2530 for (j
= 0; j
< *argc
; j
++, i
++)
2531 real_argv
[i
] = (char *)the_argv
[j
];
2533 real_argv
[i
] = NULL
;
2539 /* we use g_free here as we sucked all the inner strings
2540 * out from it into real_argv */
2545 #endif /* G_OS_WIN32 */
2549 create_files_for_uris (GList
*uris
)
2556 for (iter
= uris
; iter
; iter
= iter
->next
)
2558 GFile
*file
= g_file_new_for_uri ((char *)iter
->data
);
2559 res
= g_list_prepend (res
, file
);
2562 return g_list_reverse (res
);
2567 GSpawnChildSetupFunc user_setup
;
2568 gpointer user_setup_data
;
2574 child_setup (gpointer user_data
)
2576 ChildSetupData
*data
= user_data
;
2578 if (data
->pid_envvar
)
2580 pid_t pid
= getpid ();
2584 /* Write the pid into the space already reserved for it in the
2585 * environment array. We can't use sprintf because it might
2586 * malloc, so we do it by hand. It's simplest to write the pid
2587 * out backwards first, then copy it over.
2589 for (i
= 0; pid
; i
++, pid
/= 10)
2590 buf
[i
] = (pid
% 10) + '0';
2591 for (i
--; i
>= 0; i
--)
2592 *(data
->pid_envvar
++) = buf
[i
];
2593 *data
->pid_envvar
= '\0';
2596 if (data
->user_setup
)
2597 data
->user_setup (data
->user_setup_data
);
2601 notify_desktop_launch (GDBusConnection
*session_bus
,
2602 GDesktopAppInfo
*info
,
2604 const char *display
,
2609 GVariantBuilder uri_variant
;
2610 GVariantBuilder extras_variant
;
2612 const char *desktop_file_id
;
2613 const char *gio_desktop_file
;
2615 if (session_bus
== NULL
)
2618 g_variant_builder_init (&uri_variant
, G_VARIANT_TYPE ("as"));
2619 for (iter
= uris
; iter
; iter
= iter
->next
)
2620 g_variant_builder_add (&uri_variant
, "s", iter
->data
);
2622 g_variant_builder_init (&extras_variant
, G_VARIANT_TYPE ("a{sv}"));
2623 if (sn_id
!= NULL
&& g_utf8_validate (sn_id
, -1, NULL
))
2624 g_variant_builder_add (&extras_variant
, "{sv}",
2628 gio_desktop_file
= g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2629 if (gio_desktop_file
!= NULL
)
2630 g_variant_builder_add (&extras_variant
, "{sv}",
2631 "origin-desktop-file",
2632 g_variant_new_bytestring (gio_desktop_file
));
2633 if (g_get_prgname () != NULL
)
2634 g_variant_builder_add (&extras_variant
, "{sv}",
2636 g_variant_new_bytestring (g_get_prgname ()));
2637 g_variant_builder_add (&extras_variant
, "{sv}",
2640 (gint64
)getpid ()));
2643 desktop_file_id
= info
->filename
;
2644 else if (info
->desktop_id
)
2645 desktop_file_id
= info
->desktop_id
;
2647 desktop_file_id
= "";
2649 msg
= g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2650 "org.gtk.gio.DesktopAppInfo",
2652 g_dbus_message_set_body (msg
, g_variant_new ("(@aysxasa{sv})",
2653 g_variant_new_bytestring (desktop_file_id
),
2654 display
? display
: "",
2658 g_dbus_connection_send_message (session_bus
,
2662 g_object_unref (msg
);
2665 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2668 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo
*info
,
2669 GDBusConnection
*session_bus
,
2670 const gchar
*exec_line
,
2672 GAppLaunchContext
*launch_context
,
2673 GSpawnFlags spawn_flags
,
2674 GSpawnChildSetupFunc user_setup
,
2675 gpointer user_setup_data
,
2676 GDesktopAppLaunchCallback pid_callback
,
2677 gpointer pid_callback_data
,
2680 gboolean completed
= FALSE
;
2684 char **argv
, **envp
;
2686 ChildSetupData data
;
2688 g_return_val_if_fail (info
!= NULL
, FALSE
);
2693 envp
= g_app_launch_context_get_environment (launch_context
);
2695 envp
= g_get_environ ();
2697 /* The GList* passed to expand_application_parameters() will be modified
2698 * internally by expand_macro(), so we need to pass a copy of it instead,
2699 * and also use that copy to control the exit condition of the loop below.
2701 dup_uris
= g_list_copy (uris
);
2705 GList
*launched_uris
;
2709 old_uris
= dup_uris
;
2710 if (!expand_application_parameters (info
, exec_line
, &dup_uris
, &argc
, &argv
, error
))
2713 /* Get the subset of URIs we're launching with this process */
2714 launched_uris
= NULL
;
2715 for (iter
= old_uris
; iter
!= NULL
&& iter
!= dup_uris
; iter
= iter
->next
)
2716 launched_uris
= g_list_prepend (launched_uris
, iter
->data
);
2717 launched_uris
= g_list_reverse (launched_uris
);
2719 if (info
->terminal
&& !prepend_terminal_to_vector (&argc
, &argv
))
2721 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2722 _("Unable to find terminal required for application"));
2726 data
.user_setup
= user_setup
;
2727 data
.user_setup_data
= user_setup_data
;
2731 envp
= g_environ_setenv (envp
,
2732 "GIO_LAUNCHED_DESKTOP_FILE",
2735 envp
= g_environ_setenv (envp
,
2736 "GIO_LAUNCHED_DESKTOP_FILE_PID",
2737 "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
2739 data
.pid_envvar
= (char *)g_environ_getenv (envp
, "GIO_LAUNCHED_DESKTOP_FILE_PID");
2743 data
.pid_envvar
= NULL
;
2749 GList
*launched_files
= create_files_for_uris (launched_uris
);
2751 if (info
->startup_notify
)
2753 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
,
2757 envp
= g_environ_setenv (envp
, "DESKTOP_STARTUP_ID", sn_id
, TRUE
);
2760 g_list_free_full (launched_files
, g_object_unref
);
2763 if (!g_spawn_async (info
->path
,
2773 g_app_launch_context_launch_failed (launch_context
, sn_id
);
2776 g_list_free (launched_uris
);
2781 if (pid_callback
!= NULL
)
2782 pid_callback (info
, pid
, pid_callback_data
);
2784 if (launch_context
!= NULL
)
2786 GVariantBuilder builder
;
2787 GVariant
*platform_data
;
2789 g_variant_builder_init (&builder
, G_VARIANT_TYPE_ARRAY
);
2790 g_variant_builder_add (&builder
, "{sv}", "pid", g_variant_new_int32 (pid
));
2792 g_variant_builder_add (&builder
, "{sv}", "startup-notification-id", g_variant_new_string (sn_id
));
2793 platform_data
= g_variant_ref_sink (g_variant_builder_end (&builder
));
2794 g_signal_emit_by_name (launch_context
, "launched", info
, platform_data
);
2795 g_variant_unref (platform_data
);
2798 notify_desktop_launch (session_bus
,
2806 g_list_free (launched_uris
);
2811 while (dup_uris
!= NULL
);
2816 g_list_free (dup_uris
);
2824 object_path_from_appid (const gchar
*appid
)
2826 gchar
*appid_path
, *iter
;
2828 appid_path
= g_strconcat ("/", appid
, NULL
);
2829 for (iter
= appid_path
; *iter
; iter
++)
2842 g_desktop_app_info_make_platform_data (GDesktopAppInfo
*info
,
2844 GAppLaunchContext
*launch_context
)
2846 GVariantBuilder builder
;
2848 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
2852 GList
*launched_files
= create_files_for_uris (uris
);
2854 if (info
->startup_notify
)
2858 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
, G_APP_INFO (info
), launched_files
);
2860 g_variant_builder_add (&builder
, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id
));
2863 g_list_free_full (launched_files
, g_object_unref
);
2866 return g_variant_builder_end (&builder
);
2870 launch_uris_with_dbus (GDesktopAppInfo
*info
,
2871 GDBusConnection
*session_bus
,
2873 GAppLaunchContext
*launch_context
)
2875 GVariantBuilder builder
;
2878 g_variant_builder_init (&builder
, G_VARIANT_TYPE_TUPLE
);
2884 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
2885 for (iter
= uris
; iter
; iter
= iter
->next
)
2886 g_variant_builder_add (&builder
, "s", iter
->data
);
2887 g_variant_builder_close (&builder
);
2890 g_variant_builder_add_value (&builder
, g_desktop_app_info_make_platform_data (info
, uris
, launch_context
));
2892 /* This is non-blocking API. Similar to launching via fork()/exec()
2893 * we don't wait around to see if the program crashed during startup.
2894 * This is what startup-notification's job is...
2896 object_path
= object_path_from_appid (info
->app_id
);
2897 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
, "org.freedesktop.Application",
2898 uris
? "Open" : "Activate", g_variant_builder_end (&builder
),
2899 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
2900 g_free (object_path
);
2904 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo
*info
,
2905 GDBusConnection
*session_bus
,
2907 GAppLaunchContext
*launch_context
)
2909 GList
*ruris
= uris
;
2910 char *app_id
= NULL
;
2912 g_return_val_if_fail (info
!= NULL
, FALSE
);
2915 app_id
= g_desktop_app_info_get_string (info
, "X-Flatpak");
2916 if (app_id
&& *app_id
)
2918 ruris
= g_document_portal_add_documents (uris
, app_id
, NULL
);
2924 launch_uris_with_dbus (info
, session_bus
, ruris
, launch_context
);
2927 g_list_free_full (ruris
, g_free
);
2935 g_desktop_app_info_launch_uris_internal (GAppInfo
*appinfo
,
2937 GAppLaunchContext
*launch_context
,
2938 GSpawnFlags spawn_flags
,
2939 GSpawnChildSetupFunc user_setup
,
2940 gpointer user_setup_data
,
2941 GDesktopAppLaunchCallback pid_callback
,
2942 gpointer pid_callback_data
,
2945 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2946 GDBusConnection
*session_bus
;
2947 gboolean success
= TRUE
;
2949 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
2951 if (session_bus
&& info
->app_id
)
2952 g_desktop_app_info_launch_uris_with_dbus (info
, session_bus
, uris
, launch_context
);
2954 success
= g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, info
->exec
, uris
, launch_context
,
2955 spawn_flags
, user_setup
, user_setup_data
,
2956 pid_callback
, pid_callback_data
, error
);
2958 if (session_bus
!= NULL
)
2960 /* This asynchronous flush holds a reference until it completes,
2961 * which ensures that the following unref won't immediately kill
2962 * the connection if we were the initial owner.
2964 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
2965 g_object_unref (session_bus
);
2972 g_desktop_app_info_launch_uris (GAppInfo
*appinfo
,
2974 GAppLaunchContext
*launch_context
,
2977 return g_desktop_app_info_launch_uris_internal (appinfo
, uris
,
2979 _SPAWN_FLAGS_DEFAULT
,
2980 NULL
, NULL
, NULL
, NULL
,
2985 g_desktop_app_info_supports_uris (GAppInfo
*appinfo
)
2987 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2989 return info
->exec
&&
2990 ((strstr (info
->exec
, "%u") != NULL
) ||
2991 (strstr (info
->exec
, "%U") != NULL
));
2995 g_desktop_app_info_supports_files (GAppInfo
*appinfo
)
2997 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2999 return info
->exec
&&
3000 ((strstr (info
->exec
, "%f") != NULL
) ||
3001 (strstr (info
->exec
, "%F") != NULL
));
3005 g_desktop_app_info_launch (GAppInfo
*appinfo
,
3007 GAppLaunchContext
*launch_context
,
3017 uri
= g_file_get_uri (files
->data
);
3018 uris
= g_list_prepend (uris
, uri
);
3019 files
= files
->next
;
3022 uris
= g_list_reverse (uris
);
3024 res
= g_desktop_app_info_launch_uris (appinfo
, uris
, launch_context
, error
);
3026 g_list_free_full (uris
, g_free
);
3032 * g_desktop_app_info_launch_uris_as_manager:
3033 * @appinfo: a #GDesktopAppInfo
3034 * @uris: (element-type utf8): List of URIs
3035 * @launch_context: (nullable): a #GAppLaunchContext
3036 * @spawn_flags: #GSpawnFlags, used for each process
3037 * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once
3039 * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
3040 * @pid_callback: (scope call) (nullable): Callback for child processes
3041 * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
3042 * @error: return location for a #GError, or %NULL
3044 * This function performs the equivalent of g_app_info_launch_uris(),
3045 * but is intended primarily for operating system components that
3046 * launch applications. Ordinary applications should use
3047 * g_app_info_launch_uris().
3049 * If the application is launched via traditional UNIX fork()/exec()
3050 * then @spawn_flags, @user_setup and @user_setup_data are used for the
3051 * call to g_spawn_async(). Additionally, @pid_callback (with
3052 * @pid_callback_data) will be called to inform about the PID of the
3055 * If application launching occurs via some other mechanism (eg: D-Bus
3056 * activation) then @spawn_flags, @user_setup, @user_setup_data,
3057 * @pid_callback and @pid_callback_data are ignored.
3059 * Returns: %TRUE on successful launch, %FALSE otherwise.
3062 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
*appinfo
,
3064 GAppLaunchContext
*launch_context
,
3065 GSpawnFlags spawn_flags
,
3066 GSpawnChildSetupFunc user_setup
,
3067 gpointer user_setup_data
,
3068 GDesktopAppLaunchCallback pid_callback
,
3069 gpointer pid_callback_data
,
3072 return g_desktop_app_info_launch_uris_internal ((GAppInfo
*)appinfo
,
3083 /* OnlyShowIn API support {{{2 */
3086 * g_desktop_app_info_set_desktop_env:
3087 * @desktop_env: a string specifying what desktop this is
3089 * Sets the name of the desktop that the application is running in.
3090 * This is used by g_app_info_should_show() and
3091 * g_desktop_app_info_get_show_in() to evaluate the
3092 * `OnlyShowIn` and `NotShowIn`
3093 * desktop entry fields.
3095 * Should be called only once; subsequent calls are ignored.
3097 * Deprecated:2.42:do not use this API. Since 2.42 the value of the
3098 * `XDG_CURRENT_DESKTOP` environment variable will be used.
3101 g_desktop_app_info_set_desktop_env (const gchar
*desktop_env
)
3103 get_current_desktops (desktop_env
);
3107 g_desktop_app_info_should_show (GAppInfo
*appinfo
)
3109 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3111 if (info
->nodisplay
)
3114 return g_desktop_app_info_get_show_in (info
, NULL
);
3117 /* mime types/default apps support {{{2 */
3126 ensure_dir (DirType type
,
3129 char *path
, *display_name
;
3135 path
= g_build_filename (g_get_user_config_dir (), NULL
);
3139 path
= g_build_filename (g_get_user_data_dir (), "applications", NULL
);
3143 path
= g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL
);
3147 g_assert_not_reached ();
3151 if (g_mkdir_with_parents (path
, 0700) == 0)
3155 display_name
= g_filename_display_name (path
);
3156 if (type
== APP_DIR
)
3157 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3158 _("Can’t create user application configuration folder %s: %s"),
3159 display_name
, g_strerror (errsv
));
3161 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3162 _("Can’t create user MIME configuration folder %s: %s"),
3163 display_name
, g_strerror (errsv
));
3165 g_free (display_name
);
3172 update_mimeapps_list (const char *desktop_id
,
3173 const char *content_type
,
3174 UpdateMimeFlags flags
,
3177 char *dirname
, *filename
, *string
;
3179 gboolean load_succeeded
, res
;
3180 char **old_list
, **list
;
3181 gsize length
, data_size
;
3184 char **content_types
;
3186 /* Don't add both at start and end */
3187 g_assert (!((flags
& UPDATE_MIME_SET_DEFAULT
) &&
3188 (flags
& UPDATE_MIME_SET_NON_DEFAULT
)));
3190 dirname
= ensure_dir (CONF_DIR
, error
);
3194 filename
= g_build_filename (dirname
, "mimeapps.list", NULL
);
3197 key_file
= g_key_file_new ();
3198 load_succeeded
= g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
);
3199 if (!load_succeeded
||
3200 (!g_key_file_has_group (key_file
, ADDED_ASSOCIATIONS_GROUP
) &&
3201 !g_key_file_has_group (key_file
, REMOVED_ASSOCIATIONS_GROUP
) &&
3202 !g_key_file_has_group (key_file
, DEFAULT_APPLICATIONS_GROUP
)))
3204 g_key_file_free (key_file
);
3205 key_file
= g_key_file_new ();
3210 content_types
= g_new (char *, 2);
3211 content_types
[0] = g_strdup (content_type
);
3212 content_types
[1] = NULL
;
3216 content_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
3219 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3221 /* set as default, if requested so */
3222 string
= g_key_file_get_string (key_file
,
3223 DEFAULT_APPLICATIONS_GROUP
,
3227 if (g_strcmp0 (string
, desktop_id
) != 0 &&
3228 (flags
& UPDATE_MIME_SET_DEFAULT
))
3231 string
= g_strdup (desktop_id
);
3233 /* add in the non-default list too, if it's not already there */
3234 flags
|= UPDATE_MIME_SET_NON_DEFAULT
;
3237 if (string
== NULL
|| desktop_id
== NULL
)
3238 g_key_file_remove_key (key_file
,
3239 DEFAULT_APPLICATIONS_GROUP
,
3243 g_key_file_set_string (key_file
,
3244 DEFAULT_APPLICATIONS_GROUP
,
3253 /* reuse the list from above */
3257 g_strfreev (content_types
);
3258 content_types
= g_key_file_get_keys (key_file
, ADDED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3261 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3263 /* Add to the right place in the list */
3266 old_list
= g_key_file_get_string_list (key_file
, ADDED_ASSOCIATIONS_GROUP
,
3267 content_types
[k
], &length
, NULL
);
3269 list
= g_new (char *, 1 + length
+ 1);
3273 /* if we're adding a last-used hint, just put the application in front of the list */
3274 if (flags
& UPDATE_MIME_SET_LAST_USED
)
3276 /* avoid adding this again as non-default later */
3277 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3278 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3280 list
[i
++] = g_strdup (desktop_id
);
3285 for (j
= 0; old_list
[j
] != NULL
; j
++)
3287 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3289 /* rewrite other entries if they're different from the new one */
3290 list
[i
++] = g_strdup (old_list
[j
]);
3292 else if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3294 /* we encountered an old entry which is equal to the one we're adding as non-default,
3295 * don't change its position in the list.
3297 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3298 list
[i
++] = g_strdup (old_list
[j
]);
3303 /* add it at the end of the list */
3304 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3305 list
[i
++] = g_strdup (desktop_id
);
3309 g_strfreev (old_list
);
3311 if (list
[0] == NULL
|| desktop_id
== NULL
)
3312 g_key_file_remove_key (key_file
,
3313 ADDED_ASSOCIATIONS_GROUP
,
3317 g_key_file_set_string_list (key_file
,
3318 ADDED_ASSOCIATIONS_GROUP
,
3320 (const char * const *)list
, i
);
3327 /* reuse the list from above */
3331 g_strfreev (content_types
);
3332 content_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3335 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3337 /* Remove from removed associations group (unless remove) */
3340 old_list
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
,
3341 content_types
[k
], &length
, NULL
);
3343 list
= g_new (char *, 1 + length
+ 1);
3346 if (flags
& UPDATE_MIME_REMOVE
)
3347 list
[i
++] = g_strdup (desktop_id
);
3350 for (j
= 0; old_list
[j
] != NULL
; j
++)
3352 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3353 list
[i
++] = g_strdup (old_list
[j
]);
3358 g_strfreev (old_list
);
3360 if (list
[0] == NULL
|| desktop_id
== NULL
)
3361 g_key_file_remove_key (key_file
,
3362 REMOVED_ASSOCIATIONS_GROUP
,
3366 g_key_file_set_string_list (key_file
,
3367 REMOVED_ASSOCIATIONS_GROUP
,
3369 (const char * const *)list
, i
);
3374 g_strfreev (content_types
);
3376 data
= g_key_file_to_data (key_file
, &data_size
, error
);
3377 g_key_file_free (key_file
);
3379 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3381 desktop_file_dirs_invalidate_user_config ();
3390 g_desktop_app_info_set_as_last_used_for_type (GAppInfo
*appinfo
,
3391 const char *content_type
,
3394 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3396 if (!g_desktop_app_info_ensure_saved (info
, error
))
3399 if (!info
->desktop_id
)
3401 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3402 _("Application information lacks an identifier"));
3406 /* both add support for the content type and set as last used */
3407 return update_mimeapps_list (info
->desktop_id
, content_type
,
3408 UPDATE_MIME_SET_NON_DEFAULT
|
3409 UPDATE_MIME_SET_LAST_USED
,
3414 g_desktop_app_info_set_as_default_for_type (GAppInfo
*appinfo
,
3415 const char *content_type
,
3418 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3420 if (!g_desktop_app_info_ensure_saved (info
, error
))
3423 if (!info
->desktop_id
)
3425 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3426 _("Application information lacks an identifier"));
3430 return update_mimeapps_list (info
->desktop_id
, content_type
,
3431 UPDATE_MIME_SET_DEFAULT
,
3436 update_program_done (GPid pid
,
3440 /* Did the application exit correctly */
3441 if (g_spawn_check_exit_status (status
, NULL
))
3443 /* Here we could clean out any caches in use */
3448 run_update_command (char *command
,
3457 GError
*error
= NULL
;
3460 argv
[1] = g_build_filename (g_get_user_data_dir (), subdir
, NULL
);
3462 if (g_spawn_async ("/", argv
,
3464 G_SPAWN_SEARCH_PATH
|
3465 G_SPAWN_STDOUT_TO_DEV_NULL
|
3466 G_SPAWN_STDERR_TO_DEV_NULL
|
3467 G_SPAWN_DO_NOT_REAP_CHILD
,
3468 NULL
, NULL
, /* No setup function */
3471 g_child_watch_add (pid
, update_program_done
, NULL
);
3474 /* If we get an error at this point, it's quite likely the user doesn't
3475 * have an installed copy of either 'update-mime-database' or
3476 * 'update-desktop-database'. I don't think we want to popup an error
3477 * dialog at this point, so we just do a g_warning to give the user a
3478 * chance of debugging it.
3480 g_warning ("%s", error
->message
);
3481 g_error_free (error
);
3488 g_desktop_app_info_set_as_default_for_extension (GAppInfo
*appinfo
,
3489 const char *extension
,
3492 char *filename
, *basename
, *mimetype
;
3496 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo
), error
))
3499 dirname
= ensure_dir (MIMETYPE_DIR
, error
);
3503 basename
= g_strdup_printf ("user-extension-%s.xml", extension
);
3504 filename
= g_build_filename (dirname
, basename
, NULL
);
3508 mimetype
= g_strdup_printf ("application/x-extension-%s", extension
);
3510 if (!g_file_test (filename
, G_FILE_TEST_EXISTS
))
3515 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3516 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3517 " <mime-type type=\"%s\">\n"
3518 " <comment>%s document</comment>\n"
3519 " <glob pattern=\"*.%s\"/>\n"
3521 "</mime-info>\n", mimetype
, extension
, extension
);
3523 g_file_set_contents (filename
, contents
, -1, NULL
);
3526 run_update_command ("update-mime-database", "mime");
3530 res
= g_desktop_app_info_set_as_default_for_type (appinfo
,
3540 g_desktop_app_info_add_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
,
3550 UPDATE_MIME_SET_NON_DEFAULT
,
3555 g_desktop_app_info_can_remove_supports_type (GAppInfo
*appinfo
)
3561 g_desktop_app_info_remove_supports_type (GAppInfo
*appinfo
,
3562 const char *content_type
,
3565 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3567 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3570 return update_mimeapps_list (info
->desktop_id
, content_type
,
3575 static const char **
3576 g_desktop_app_info_get_supported_types (GAppInfo
*appinfo
)
3578 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3580 return (const char**) info
->mime_types
;
3583 /* Saving and deleting {{{2 */
3586 g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
3592 char *data
, *desktop_id
;
3597 if (info
->filename
!= NULL
)
3600 /* This is only used for object created with
3601 * g_app_info_create_from_commandline. All other
3602 * object should have a filename
3605 dirname
= ensure_dir (APP_DIR
, error
);
3609 key_file
= g_key_file_new ();
3611 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3612 "Encoding", "UTF-8");
3613 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3614 G_KEY_FILE_DESKTOP_KEY_VERSION
, "1.0");
3615 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3616 G_KEY_FILE_DESKTOP_KEY_TYPE
,
3617 G_KEY_FILE_DESKTOP_TYPE_APPLICATION
);
3619 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3620 G_KEY_FILE_DESKTOP_KEY_TERMINAL
, TRUE
);
3621 if (info
->nodisplay
)
3622 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3623 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3625 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3626 G_KEY_FILE_DESKTOP_KEY_EXEC
, info
->exec
);
3628 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3629 G_KEY_FILE_DESKTOP_KEY_NAME
, info
->name
);
3631 if (info
->generic_name
!= NULL
)
3632 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3633 GENERIC_NAME_KEY
, info
->generic_name
);
3635 if (info
->fullname
!= NULL
)
3636 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3637 FULL_NAME_KEY
, info
->fullname
);
3639 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3640 G_KEY_FILE_DESKTOP_KEY_COMMENT
, info
->comment
);
3642 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3643 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3645 data
= g_key_file_to_data (key_file
, &data_size
, NULL
);
3646 g_key_file_free (key_file
);
3648 desktop_id
= g_strdup_printf ("userapp-%s-XXXXXX.desktop", info
->name
);
3649 filename
= g_build_filename (dirname
, desktop_id
, NULL
);
3650 g_free (desktop_id
);
3653 fd
= g_mkstemp (filename
);
3658 display_name
= g_filename_display_name (filename
);
3659 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3660 _("Can’t create user desktop file %s"), display_name
);
3661 g_free (display_name
);
3667 desktop_id
= g_path_get_basename (filename
);
3669 /* FIXME - actually handle error */
3670 (void) g_close (fd
, NULL
);
3672 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3676 g_free (desktop_id
);
3681 info
->filename
= filename
;
3682 info
->desktop_id
= desktop_id
;
3684 run_update_command ("update-desktop-database", "applications");
3686 /* We just dropped a file in the user's desktop file directory. Save
3687 * the monitor the bother of having to notice it and invalidate
3690 * This means that calls directly following this will be able to see
3691 * the results immediately.
3693 desktop_file_dirs_invalidate_user_data ();
3699 g_desktop_app_info_can_delete (GAppInfo
*appinfo
)
3701 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3705 if (strstr (info
->filename
, "/userapp-"))
3706 return g_access (info
->filename
, W_OK
) == 0;
3713 g_desktop_app_info_delete (GAppInfo
*appinfo
)
3715 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3719 if (g_remove (info
->filename
) == 0)
3721 update_mimeapps_list (info
->desktop_id
, NULL
,
3725 g_free (info
->filename
);
3726 info
->filename
= NULL
;
3727 g_free (info
->desktop_id
);
3728 info
->desktop_id
= NULL
;
3737 /* Create for commandline {{{2 */
3739 * g_app_info_create_from_commandline:
3740 * @commandline: (type filename): the commandline to use
3741 * @application_name: (nullable): the application name, or %NULL to use @commandline
3742 * @flags: flags that can specify details of the created #GAppInfo
3743 * @error: a #GError location to store the error occurring, %NULL to ignore.
3745 * Creates a new #GAppInfo from the given information.
3747 * Note that for @commandline, the quoting rules of the Exec key of the
3748 * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3749 * are applied. For example, if the @commandline contains
3750 * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3751 * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
3753 * Returns: (transfer full): new #GAppInfo for given command.
3756 g_app_info_create_from_commandline (const char *commandline
,
3757 const char *application_name
,
3758 GAppInfoCreateFlags flags
,
3763 GDesktopAppInfo
*info
;
3765 g_return_val_if_fail (commandline
, NULL
);
3767 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
3769 info
->filename
= NULL
;
3770 info
->desktop_id
= NULL
;
3772 info
->terminal
= (flags
& G_APP_INFO_CREATE_NEEDS_TERMINAL
) != 0;
3773 info
->startup_notify
= (flags
& G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION
) != 0;
3774 info
->hidden
= FALSE
;
3775 if ((flags
& G_APP_INFO_CREATE_SUPPORTS_URIS
) != 0)
3776 info
->exec
= g_strconcat (commandline
, " %u", NULL
);
3778 info
->exec
= g_strconcat (commandline
, " %f", NULL
);
3779 info
->nodisplay
= TRUE
;
3780 info
->binary
= binary_from_exec (info
->exec
);
3782 if (application_name
)
3783 info
->name
= g_strdup (application_name
);
3786 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
3787 split
= g_strsplit (commandline
, " ", 2);
3788 basename
= split
[0] ? g_path_get_basename (split
[0]) : NULL
;
3790 info
->name
= basename
;
3791 if (info
->name
== NULL
)
3792 info
->name
= g_strdup ("custom");
3794 info
->comment
= g_strdup_printf (_("Custom definition for %s"), info
->name
);
3796 return G_APP_INFO (info
);
3799 /* GAppInfo interface init */
3802 g_desktop_app_info_iface_init (GAppInfoIface
*iface
)
3804 iface
->dup
= g_desktop_app_info_dup
;
3805 iface
->equal
= g_desktop_app_info_equal
;
3806 iface
->get_id
= g_desktop_app_info_get_id
;
3807 iface
->get_name
= g_desktop_app_info_get_name
;
3808 iface
->get_description
= g_desktop_app_info_get_description
;
3809 iface
->get_executable
= g_desktop_app_info_get_executable
;
3810 iface
->get_icon
= g_desktop_app_info_get_icon
;
3811 iface
->launch
= g_desktop_app_info_launch
;
3812 iface
->supports_uris
= g_desktop_app_info_supports_uris
;
3813 iface
->supports_files
= g_desktop_app_info_supports_files
;
3814 iface
->launch_uris
= g_desktop_app_info_launch_uris
;
3815 iface
->should_show
= g_desktop_app_info_should_show
;
3816 iface
->set_as_default_for_type
= g_desktop_app_info_set_as_default_for_type
;
3817 iface
->set_as_default_for_extension
= g_desktop_app_info_set_as_default_for_extension
;
3818 iface
->add_supports_type
= g_desktop_app_info_add_supports_type
;
3819 iface
->can_remove_supports_type
= g_desktop_app_info_can_remove_supports_type
;
3820 iface
->remove_supports_type
= g_desktop_app_info_remove_supports_type
;
3821 iface
->can_delete
= g_desktop_app_info_can_delete
;
3822 iface
->do_delete
= g_desktop_app_info_delete
;
3823 iface
->get_commandline
= g_desktop_app_info_get_commandline
;
3824 iface
->get_display_name
= g_desktop_app_info_get_display_name
;
3825 iface
->set_as_last_used_for_type
= g_desktop_app_info_set_as_last_used_for_type
;
3826 iface
->get_supported_types
= g_desktop_app_info_get_supported_types
;
3829 /* Recommended applications {{{2 */
3831 /* Converts content_type into a list of itself with all of its parent
3832 * types (if include_fallback is enabled) or just returns a single-item
3833 * list with the unaliased content type.
3836 get_list_of_mimetypes (const gchar
*content_type
,
3837 gboolean include_fallback
)
3842 array
= g_ptr_array_new ();
3843 unaliased
= _g_unix_content_type_unalias (content_type
);
3844 g_ptr_array_add (array
, unaliased
);
3846 if (include_fallback
)
3850 /* Iterate the array as we grow it, until we have nothing more to add */
3851 for (i
= 0; i
< array
->len
; i
++)
3853 gchar
**parents
= _g_unix_content_type_get_parents (g_ptr_array_index (array
, i
));
3856 for (j
= 0; parents
[j
]; j
++)
3857 /* Don't add duplicates */
3858 if (!array_contains (array
, parents
[j
]))
3859 g_ptr_array_add (array
, parents
[j
]);
3861 g_free (parents
[j
]);
3863 /* We already stole or freed each element. Free the container. */
3868 g_ptr_array_add (array
, NULL
);
3870 return (gchar
**) g_ptr_array_free (array
, FALSE
);
3874 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar
*content_type
,
3875 gboolean include_fallback
)
3877 GPtrArray
*hits
, *blacklist
;
3881 hits
= g_ptr_array_new ();
3882 blacklist
= g_ptr_array_new ();
3884 types
= get_list_of_mimetypes (content_type
, include_fallback
);
3886 desktop_file_dirs_lock ();
3888 for (i
= 0; types
[i
]; i
++)
3889 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
3890 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], hits
, blacklist
);
3892 /* We will keep the hits past unlocking, so we must dup them */
3893 for (i
= 0; i
< hits
->len
; i
++)
3894 hits
->pdata
[i
] = g_strdup (hits
->pdata
[i
]);
3896 desktop_file_dirs_unlock ();
3898 g_ptr_array_add (hits
, NULL
);
3900 g_ptr_array_free (blacklist
, TRUE
);
3903 return (gchar
**) g_ptr_array_free (hits
, FALSE
);
3907 * g_app_info_get_recommended_for_type:
3908 * @content_type: the content type to find a #GAppInfo for
3910 * Gets a list of recommended #GAppInfos for a given content type, i.e.
3911 * those applications which claim to support the given content type exactly,
3912 * and not by MIME type subclassing.
3913 * Note that the first application of the list is the last used one, i.e.
3914 * the last one for which g_app_info_set_as_last_used_for_type() has been
3917 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3918 * for given @content_type or %NULL on error.
3923 g_app_info_get_recommended_for_type (const gchar
*content_type
)
3925 gchar
**desktop_ids
;
3929 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3931 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3934 for (i
= 0; desktop_ids
[i
]; i
++)
3936 GDesktopAppInfo
*info
;
3938 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3940 infos
= g_list_prepend (infos
, info
);
3943 g_strfreev (desktop_ids
);
3945 return g_list_reverse (infos
);
3949 * g_app_info_get_fallback_for_type:
3950 * @content_type: the content type to find a #GAppInfo for
3952 * Gets a list of fallback #GAppInfos for a given content type, i.e.
3953 * those applications which claim to support the given content type
3954 * by MIME type subclassing and not directly.
3956 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3957 * for given @content_type or %NULL on error.
3962 g_app_info_get_fallback_for_type (const gchar
*content_type
)
3964 gchar
**recommended_ids
;
3969 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3971 recommended_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3972 all_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3975 for (i
= 0; all_ids
[i
]; i
++)
3977 GDesktopAppInfo
*info
;
3980 /* Don't return the ones on the recommended list */
3981 for (j
= 0; recommended_ids
[j
]; j
++)
3982 if (g_str_equal (all_ids
[i
], recommended_ids
[j
]))
3985 if (recommended_ids
[j
])
3988 info
= g_desktop_app_info_new (all_ids
[i
]);
3991 infos
= g_list_prepend (infos
, info
);
3994 g_strfreev (recommended_ids
);
3995 g_strfreev (all_ids
);
3997 return g_list_reverse (infos
);
4001 * g_app_info_get_all_for_type:
4002 * @content_type: the content type to find a #GAppInfo for
4004 * Gets a list of all #GAppInfos for a given content type,
4005 * including the recommended and fallback #GAppInfos. See
4006 * g_app_info_get_recommended_for_type() and
4007 * g_app_info_get_fallback_for_type().
4009 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
4010 * for given @content_type or %NULL on error.
4013 g_app_info_get_all_for_type (const char *content_type
)
4015 gchar
**desktop_ids
;
4019 g_return_val_if_fail (content_type
!= NULL
, NULL
);
4021 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
4024 for (i
= 0; desktop_ids
[i
]; i
++)
4026 GDesktopAppInfo
*info
;
4028 info
= g_desktop_app_info_new (desktop_ids
[i
]);
4030 infos
= g_list_prepend (infos
, info
);
4033 g_strfreev (desktop_ids
);
4035 return g_list_reverse (infos
);
4039 * g_app_info_reset_type_associations:
4040 * @content_type: a content type
4042 * Removes all changes to the type associations done by
4043 * g_app_info_set_as_default_for_type(),
4044 * g_app_info_set_as_default_for_extension(),
4045 * g_app_info_add_supports_type() or
4046 * g_app_info_remove_supports_type().
4051 g_app_info_reset_type_associations (const char *content_type
)
4053 update_mimeapps_list (NULL
, content_type
,
4059 * g_app_info_get_default_for_type:
4060 * @content_type: the content type to find a #GAppInfo for
4061 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
4064 * Gets the default #GAppInfo for a given content type.
4066 * Returns: (transfer full): #GAppInfo for given @content_type or
4070 g_app_info_get_default_for_type (const char *content_type
,
4071 gboolean must_support_uris
)
4073 GPtrArray
*blacklist
;
4079 g_return_val_if_fail (content_type
!= NULL
, NULL
);
4081 types
= get_list_of_mimetypes (content_type
, TRUE
);
4083 blacklist
= g_ptr_array_new ();
4084 results
= g_ptr_array_new ();
4087 desktop_file_dirs_lock ();
4089 for (i
= 0; types
[i
]; i
++)
4091 /* Collect all the default apps for this type */
4092 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4093 desktop_file_dir_default_lookup (&desktop_file_dirs
[j
], types
[i
], results
);
4095 /* Consider the associations as well... */
4096 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4097 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], results
, blacklist
);
4099 /* (If any), see if one of those apps is installed... */
4100 for (j
= 0; j
< results
->len
; j
++)
4102 const gchar
*desktop_id
= g_ptr_array_index (results
, j
);
4104 for (k
= 0; k
< n_desktop_file_dirs
; k
++)
4106 info
= (GAppInfo
*) desktop_file_dir_get_app (&desktop_file_dirs
[k
], desktop_id
);
4110 if (!must_support_uris
|| g_app_info_supports_uris (info
))
4113 g_clear_object (&info
);
4118 /* Reset the list, ready to try again with the next (parent)
4119 * mimetype, but keep the blacklist in place.
4121 g_ptr_array_set_size (results
, 0);
4125 desktop_file_dirs_unlock ();
4127 g_ptr_array_unref (blacklist
);
4128 g_ptr_array_unref (results
);
4135 * g_app_info_get_default_for_uri_scheme:
4136 * @uri_scheme: a string containing a URI scheme.
4138 * Gets the default application for handling URIs with
4139 * the given URI scheme. A URI scheme is the initial part
4140 * of the URI, up to but not including the ':', e.g. "http",
4143 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4146 g_app_info_get_default_for_uri_scheme (const char *uri_scheme
)
4149 char *content_type
, *scheme_down
;
4151 scheme_down
= g_ascii_strdown (uri_scheme
, -1);
4152 content_type
= g_strdup_printf ("x-scheme-handler/%s", scheme_down
);
4153 g_free (scheme_down
);
4154 app_info
= g_app_info_get_default_for_type (content_type
, FALSE
);
4155 g_free (content_type
);
4160 /* "Get all" API {{{2 */
4163 * g_desktop_app_info_get_implementations:
4164 * @interface: the name of the interface
4166 * Gets all applications that implement @interface.
4168 * An application implements an interface if that interface is listed in
4169 * the Implements= line of the desktop file of the application.
4171 * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo
4177 g_desktop_app_info_get_implementations (const gchar
*interface
)
4179 GList
*result
= NULL
;
4183 desktop_file_dirs_lock ();
4185 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4186 desktop_file_dir_get_implementations (&desktop_file_dirs
[i
], &result
, interface
);
4188 desktop_file_dirs_unlock ();
4193 gchar
*name
= (*ptr
)->data
;
4194 GDesktopAppInfo
*app
;
4196 app
= g_desktop_app_info_new (name
);
4202 ptr
= &(*ptr
)->next
;
4205 *ptr
= g_list_delete_link (*ptr
, *ptr
);
4212 * g_desktop_app_info_search:
4213 * @search_string: the search string to use
4215 * Searches desktop files for ones that match @search_string.
4217 * The return value is an array of strvs. Each strv contains a list of
4218 * applications that matched @search_string with an equal score. The
4219 * outer list is sorted by score so that the first strv contains the
4220 * best-matching applications, and so on.
4221 * The algorithm for determining matches is undefined and may change at
4224 * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4225 * list of strvs. Free each item with g_strfreev() and free the outer
4226 * list with g_free().
4229 g_desktop_app_info_search (const gchar
*search_string
)
4231 gchar
**search_tokens
;
4232 gint last_category
= -1;
4234 gint n_categories
= 0;
4235 gint start_of_category
;
4238 search_tokens
= g_str_tokenize_and_fold (search_string
, NULL
, NULL
);
4240 desktop_file_dirs_lock ();
4242 reset_total_search_results ();
4244 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4246 for (j
= 0; search_tokens
[j
]; j
++)
4248 desktop_file_dir_search (&desktop_file_dirs
[i
], search_tokens
[j
]);
4249 merge_token_results (j
== 0);
4251 merge_directory_results ();
4254 sort_total_search_results ();
4256 /* Count the total number of unique categories */
4257 for (i
= 0; i
< static_total_results_size
; i
++)
4258 if (static_total_results
[i
].category
!= last_category
)
4260 last_category
= static_total_results
[i
].category
;
4264 results
= g_new (gchar
**, n_categories
+ 1);
4266 /* Start loading into the results list */
4267 start_of_category
= 0;
4268 for (i
= 0; i
< n_categories
; i
++)
4270 gint n_items_in_category
= 0;
4274 this_category
= static_total_results
[start_of_category
].category
;
4276 while (start_of_category
+ n_items_in_category
< static_total_results_size
&&
4277 static_total_results
[start_of_category
+ n_items_in_category
].category
== this_category
)
4278 n_items_in_category
++;
4280 results
[i
] = g_new (gchar
*, n_items_in_category
+ 1);
4281 for (j
= 0; j
< n_items_in_category
; j
++)
4282 results
[i
][j
] = g_strdup (static_total_results
[start_of_category
+ j
].app_name
);
4283 results
[i
][j
] = NULL
;
4285 start_of_category
+= n_items_in_category
;
4289 desktop_file_dirs_unlock ();
4291 g_strfreev (search_tokens
);
4297 * g_app_info_get_all:
4299 * Gets a list of all of the applications currently registered
4302 * For desktop files, this includes applications that have
4303 * `NoDisplay=true` set or are excluded from display by means
4304 * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4305 * The returned list does not include applications which have
4306 * the `Hidden` key set.
4308 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4311 g_app_info_get_all (void)
4314 GHashTableIter iter
;
4319 apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
4321 desktop_file_dirs_lock ();
4323 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4324 desktop_file_dir_get_all (&desktop_file_dirs
[i
], apps
);
4326 desktop_file_dirs_unlock ();
4329 g_hash_table_iter_init (&iter
, apps
);
4330 while (g_hash_table_iter_next (&iter
, NULL
, &value
))
4333 infos
= g_list_prepend (infos
, value
);
4336 g_hash_table_destroy (apps
);
4341 /* GDesktopAppInfoLookup interface {{{2 */
4344 * GDesktopAppInfoLookup:
4346 * #GDesktopAppInfoLookup is an opaque data structure and can only be accessed
4347 * using the following functions.
4350 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4352 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface
;
4353 G_DEFINE_INTERFACE (GDesktopAppInfoLookup
, g_desktop_app_info_lookup
, G_TYPE_OBJECT
)
4356 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface
*iface
)
4360 /* "Get for mime type" APIs {{{2 */
4363 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4364 * @lookup: a #GDesktopAppInfoLookup
4365 * @uri_scheme: a string containing a URI scheme.
4367 * Gets the default application for launching applications
4368 * using this URI scheme for a particular GDesktopAppInfoLookup
4371 * The GDesktopAppInfoLookup interface and this function is used
4372 * to implement g_app_info_get_default_for_uri_scheme() backends
4373 * in a GIO module. There is no reason for applications to use it
4374 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4376 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4378 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
4381 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup
*lookup
,
4382 const char *uri_scheme
)
4384 GDesktopAppInfoLookupIface
*iface
;
4386 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup
), NULL
);
4388 iface
= G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup
);
4390 return (* iface
->get_default_for_uri_scheme
) (lookup
, uri_scheme
);
4393 G_GNUC_END_IGNORE_DEPRECATIONS
4395 /* Misc getter APIs {{{2 */
4398 * g_desktop_app_info_get_startup_wm_class:
4399 * @info: a #GDesktopAppInfo that supports startup notify
4401 * Retrieves the StartupWMClass field from @info. This represents the
4402 * WM_CLASS property of the main window of the application, if launched
4405 * Returns: (transfer none): the startup WM class, or %NULL if none is set
4406 * in the desktop file.
4411 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo
*info
)
4413 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4415 return info
->startup_wm_class
;
4419 * g_desktop_app_info_get_string:
4420 * @info: a #GDesktopAppInfo
4421 * @key: the key to look up
4423 * Looks up a string value in the keyfile backing @info.
4425 * The @key is looked up in the "Desktop Entry" group.
4427 * Returns: a newly allocated string, or %NULL if the key
4433 g_desktop_app_info_get_string (GDesktopAppInfo
*info
,
4436 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4438 return g_key_file_get_string (info
->keyfile
,
4439 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4443 * g_desktop_app_info_get_locale_string:
4444 * @info: a #GDesktopAppInfo
4445 * @key: the key to look up
4447 * Looks up a localized string value in the keyfile backing @info
4448 * translated to the current locale.
4450 * The @key is looked up in the "Desktop Entry" group.
4452 * Returns: (nullable): a newly allocated string, or %NULL if the key
4458 g_desktop_app_info_get_locale_string (GDesktopAppInfo
*info
,
4461 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4462 g_return_val_if_fail (key
!= NULL
&& *key
!= '\0', NULL
);
4464 return g_key_file_get_locale_string (info
->keyfile
,
4465 G_KEY_FILE_DESKTOP_GROUP
,
4470 * g_desktop_app_info_get_boolean:
4471 * @info: a #GDesktopAppInfo
4472 * @key: the key to look up
4474 * Looks up a boolean value in the keyfile backing @info.
4476 * The @key is looked up in the "Desktop Entry" group.
4478 * Returns: the boolean value, or %FALSE if the key
4484 g_desktop_app_info_get_boolean (GDesktopAppInfo
*info
,
4487 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4489 return g_key_file_get_boolean (info
->keyfile
,
4490 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4494 * g_desktop_app_info_has_key:
4495 * @info: a #GDesktopAppInfo
4496 * @key: the key to look up
4498 * Returns whether @key exists in the "Desktop Entry" group
4499 * of the keyfile backing @info.
4501 * Returns: %TRUE if the @key exists
4506 g_desktop_app_info_has_key (GDesktopAppInfo
*info
,
4509 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4511 return g_key_file_has_key (info
->keyfile
,
4512 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4515 /* Desktop actions support {{{2 */
4518 * g_desktop_app_info_list_actions:
4519 * @info: a #GDesktopAppInfo
4521 * Returns the list of "additional application actions" supported on the
4522 * desktop file, as per the desktop file specification.
4524 * As per the specification, this is the list of actions that are
4525 * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4527 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4531 const gchar
* const *
4532 g_desktop_app_info_list_actions (GDesktopAppInfo
*info
)
4534 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4536 return (const gchar
**) info
->actions
;
4540 app_info_has_action (GDesktopAppInfo
*info
,
4541 const gchar
*action_name
)
4545 for (i
= 0; info
->actions
[i
]; i
++)
4546 if (g_str_equal (info
->actions
[i
], action_name
))
4553 * g_desktop_app_info_get_action_name:
4554 * @info: a #GDesktopAppInfo
4555 * @action_name: the name of the action as from
4556 * g_desktop_app_info_list_actions()
4558 * Gets the user-visible display name of the "additional application
4559 * action" specified by @action_name.
4561 * This corresponds to the "Name" key within the keyfile group for the
4564 * Returns: (transfer full): the locale-specific action name
4569 g_desktop_app_info_get_action_name (GDesktopAppInfo
*info
,
4570 const gchar
*action_name
)
4575 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4576 g_return_val_if_fail (action_name
!= NULL
, NULL
);
4577 g_return_val_if_fail (app_info_has_action (info
, action_name
), NULL
);
4579 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4580 result
= g_key_file_get_locale_string (info
->keyfile
, group_name
, "Name", NULL
, NULL
);
4581 g_free (group_name
);
4583 /* The spec says that the Name field must be given.
4585 * If it's not, let's follow the behaviour of our get_name()
4586 * implementation above and never return %NULL.
4589 result
= g_strdup (_("Unnamed"));
4595 * g_desktop_app_info_launch_action:
4596 * @info: a #GDesktopAppInfo
4597 * @action_name: the name of the action as from
4598 * g_desktop_app_info_list_actions()
4599 * @launch_context: (nullable): a #GAppLaunchContext
4601 * Activates the named application action.
4603 * You may only call this function on action names that were
4604 * returned from g_desktop_app_info_list_actions().
4606 * Note that if the main entry of the desktop file indicates that the
4607 * application supports startup notification, and @launch_context is
4608 * non-%NULL, then startup notification will be used when activating the
4609 * action (and as such, invocation of the action on the receiving side
4610 * must signal the end of startup notification when it is completed).
4611 * This is the expected behaviour of applications declaring additional
4612 * actions, as per the desktop file specification.
4614 * As with g_app_info_launch() there is no way to detect failures that
4615 * occur while using this function.
4620 g_desktop_app_info_launch_action (GDesktopAppInfo
*info
,
4621 const gchar
*action_name
,
4622 GAppLaunchContext
*launch_context
)
4624 GDBusConnection
*session_bus
;
4626 g_return_if_fail (G_IS_DESKTOP_APP_INFO (info
));
4627 g_return_if_fail (action_name
!= NULL
);
4628 g_return_if_fail (app_info_has_action (info
, action_name
));
4630 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
4632 if (session_bus
&& info
->app_id
)
4636 object_path
= object_path_from_appid (info
->app_id
);
4637 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
,
4638 "org.freedesktop.Application", "ActivateAction",
4639 g_variant_new ("(sav@a{sv})", action_name
, NULL
,
4640 g_desktop_app_info_make_platform_data (info
, NULL
, launch_context
)),
4641 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
4642 g_free (object_path
);
4649 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4650 exec_line
= g_key_file_get_string (info
->keyfile
, group_name
, "Exec", NULL
);
4651 g_free (group_name
);
4654 g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, exec_line
, NULL
, launch_context
,
4655 _SPAWN_FLAGS_DEFAULT
, NULL
, NULL
, NULL
, NULL
, NULL
);
4658 if (session_bus
!= NULL
)
4660 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
4661 g_object_unref (session_bus
);
4666 /* vim:set foldmethod=marker: */