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 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 "glocaldirectorymonitor.h"
53 * SECTION:gdesktopappinfo
54 * @title: GDesktopAppInfo
55 * @short_description: Application information from desktop files
56 * @include: gio/gdesktopappinfo.h
58 * #GDesktopAppInfo is an implementation of #GAppInfo based on
61 * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
62 * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
66 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
67 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
68 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
69 #define MIME_CACHE_GROUP "MIME Cache"
70 #define GENERIC_NAME_KEY "GenericName"
71 #define FULL_NAME_KEY "X-GNOME-FullName"
72 #define KEYWORDS_KEY "Keywords"
73 #define STARTUP_WM_CLASS_KEY "StartupWMClass"
80 static void g_desktop_app_info_iface_init (GAppInfoIface
*iface
);
81 static gboolean
g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
87 * Information about an installed application from a desktop file.
89 struct _GDesktopAppInfo
91 GObject parent_instance
;
113 char *startup_wm_class
;
120 guint startup_notify
: 1;
125 UPDATE_MIME_NONE
= 1 << 0,
126 UPDATE_MIME_SET_DEFAULT
= 1 << 1,
127 UPDATE_MIME_SET_NON_DEFAULT
= 1 << 2,
128 UPDATE_MIME_REMOVE
= 1 << 3,
129 UPDATE_MIME_SET_LAST_USED
= 1 << 4,
132 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo
, g_desktop_app_info
, G_TYPE_OBJECT
,
133 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO
, g_desktop_app_info_iface_init
))
135 /* DesktopFileDir implementation {{{1 */
142 GLocalDirectoryMonitor
*monitor
;
143 GHashTable
*app_names
;
144 GHashTable
*mime_tweaks
;
145 GHashTable
*memory_index
;
146 GHashTable
*memory_implementations
;
149 static DesktopFileDir
*desktop_file_dirs
;
150 static guint n_desktop_file_dirs
;
151 static const guint desktop_file_dir_user_config_index
= 0;
152 static guint desktop_file_dir_user_data_index
;
153 static GMutex desktop_file_dir_lock
;
155 /* Monitor 'changed' signal handler {{{2 */
156 static void desktop_file_dir_reset (DesktopFileDir
*dir
);
159 desktop_file_dir_changed (GFileMonitor
*monitor
,
162 GFileMonitorEvent event_type
,
165 DesktopFileDir
*dir
= user_data
;
167 /* We are not interested in receiving notifications forever just
168 * because someone asked about one desktop file once.
170 * After we receive the first notification, reset the dir, destroying
171 * the monitor. We will take this as a hint, next time that we are
172 * asked, that we need to check if everything is up to date.
174 g_mutex_lock (&desktop_file_dir_lock
);
176 desktop_file_dir_reset (dir
);
178 g_mutex_unlock (&desktop_file_dir_lock
);
180 /* Notify anyone else who may be interested */
181 g_app_info_monitor_fire ();
184 /* Internal utility functions {{{2 */
187 * desktop_file_dir_app_name_is_masked:
188 * @dir: a #DesktopFileDir
189 * @app_name: an application ID
191 * Checks if @app_name is masked for @dir.
193 * An application is masked if a similarly-named desktop file exists in
194 * a desktop file directory with higher precedence. Masked desktop
195 * files should be ignored.
198 desktop_file_dir_app_name_is_masked (DesktopFileDir
*dir
,
199 const gchar
*app_name
)
201 while (dir
> desktop_file_dirs
)
205 if (dir
->app_names
&& g_hash_table_contains (dir
->app_names
, app_name
))
212 static const gchar
* const *
213 get_lowercase_current_desktops (void)
215 static gchar
**result
;
217 if (g_once_init_enter (&result
))
222 envvar
= g_getenv ("XDG_CURRENT_DESKTOP");
228 tmp
= g_strsplit (envvar
, G_SEARCHPATH_SEPARATOR_S
, 0);
230 for (i
= 0; tmp
[i
]; i
++)
231 for (j
= 0; tmp
[i
][j
]; j
++)
232 tmp
[i
][j
] = g_ascii_tolower (tmp
[i
][j
]);
235 tmp
= g_new0 (gchar
*, 0 + 1);
237 g_once_init_leave (&result
, tmp
);
240 return (const gchar
**) result
;
243 static const gchar
* const *
244 get_current_desktops (const gchar
*value
)
246 static gchar
**result
;
248 if (g_once_init_enter (&result
))
253 value
= g_getenv ("XDG_CURRENT_DESKTOP");
258 tmp
= g_strsplit (value
, ":", 0);
260 g_once_init_leave (&result
, tmp
);
263 return (const gchar
**) result
;
267 * add_to_table_if_appropriate:
268 * @apps: a string to GDesktopAppInfo hash table
269 * @app_name: the name of the application
270 * @info: a #GDesktopAppInfo, or NULL
272 * If @info is non-%NULL and non-hidden, then add it to @apps, using
273 * @app_name as a key.
275 * If @info is non-%NULL then this function will consume the passed-in
279 add_to_table_if_appropriate (GHashTable
*apps
,
280 const gchar
*app_name
,
281 GDesktopAppInfo
*info
)
288 g_object_unref (info
);
292 g_free (info
->desktop_id
);
293 info
->desktop_id
= g_strdup (app_name
);
295 g_hash_table_insert (apps
, g_strdup (info
->desktop_id
), info
);
302 DESKTOP_KEY_GenericName
,
303 DESKTOP_KEY_Keywords
,
305 DESKTOP_KEY_X_GNOME_FullName
,
310 const gchar desktop_key_match_category
[N_DESKTOP_KEYS
] = {
311 /* Note: lower numbers are a better match.
313 * In case we want two keys to match at the same level, we can just
314 * use the same number for the two different keys.
316 [DESKTOP_KEY_Name
] = 1,
317 [DESKTOP_KEY_Exec
] = 2,
318 [DESKTOP_KEY_Keywords
] = 3,
319 [DESKTOP_KEY_GenericName
] = 4,
320 [DESKTOP_KEY_X_GNOME_FullName
] = 5,
321 [DESKTOP_KEY_Comment
] = 6
325 desktop_key_get_name (guint key_id
)
329 case DESKTOP_KEY_Comment
:
331 case DESKTOP_KEY_Exec
:
333 case DESKTOP_KEY_GenericName
:
334 return "GenericName";
335 case DESKTOP_KEY_Keywords
:
337 case DESKTOP_KEY_Name
:
339 case DESKTOP_KEY_X_GNOME_FullName
:
340 return "X-GNOME-FullName";
342 g_assert_not_reached ();
346 /* Search global state {{{2
348 * We only ever search under a global lock, so we can use (and reuse)
349 * some global data to reduce allocations made while searching.
351 * In short, we keep around arrays of results that we expand as needed
352 * (and never shrink).
354 * static_token_results: this is where we append the results for each
355 * token within a given desktop directory, as we handle it (which is
356 * a union of all matches for this term)
358 * static_search_results: this is where we build the complete results
359 * for a single directory (which is an intersection of the matches
360 * found for each term)
362 * static_total_results: this is where we build the complete results
363 * across all directories (which is a union of the matches found in
366 * The app_names that enter these tables are always pointer-unique (in
367 * the sense that string equality is the same as pointer equality).
368 * This can be guaranteed for two reasons:
370 * - we mask appids so that a given appid will only ever appear within
371 * the highest-precedence directory that contains it. We never
372 * return search results from a lower-level directory if a desktop
373 * file exists in a higher-level one.
375 * - within a given directory, the string is unique because it's the
376 * key in the hashtable of all app_ids for that directory.
378 * We perform a merging of the results in merge_token_results(). This
379 * works by ordering the two lists and moving through each of them (at
380 * the same time) looking for common elements, rejecting uncommon ones.
381 * "Order" here need not mean any particular thing, as long as it is
382 * some order. Because of the uniqueness of our strings, we can use
383 * pointer order. That's what's going on in compare_results() below.
387 const gchar
*app_name
;
391 static struct search_result
*static_token_results
;
392 static gint static_token_results_size
;
393 static gint static_token_results_allocated
;
394 static struct search_result
*static_search_results
;
395 static gint static_search_results_size
;
396 static gint static_search_results_allocated
;
397 static struct search_result
*static_total_results
;
398 static gint static_total_results_size
;
399 static gint static_total_results_allocated
;
401 /* And some functions for performing nice operations against it */
403 compare_results (gconstpointer a
,
406 const struct search_result
*ra
= a
;
407 const struct search_result
*rb
= b
;
409 if (ra
->app_name
< rb
->app_name
)
412 else if (ra
->app_name
> rb
->app_name
)
416 return ra
->category
- rb
->category
;
420 compare_categories (gconstpointer a
,
423 const struct search_result
*ra
= a
;
424 const struct search_result
*rb
= b
;
426 return ra
->category
- rb
->category
;
430 add_token_result (const gchar
*app_name
,
433 if G_UNLIKELY (static_token_results_size
== static_token_results_allocated
)
435 static_token_results_allocated
= MAX (16, static_token_results_allocated
* 2);
436 static_token_results
= g_renew (struct search_result
, static_token_results
, static_token_results_allocated
);
439 static_token_results
[static_token_results_size
].app_name
= app_name
;
440 static_token_results
[static_token_results_size
].category
= category
;
441 static_token_results_size
++;
445 merge_token_results (gboolean first
)
447 qsort (static_token_results
, static_token_results_size
, sizeof (struct search_result
), compare_results
);
449 /* If this is the first token then we are basically merging a list with
450 * itself -- we only perform de-duplication.
452 * If this is not the first token then we are doing a real merge.
456 const gchar
*last_name
= NULL
;
459 /* We must de-duplicate, but we do so by taking the best category
462 * The final list can be as large as the input here, so make sure
463 * we have enough room (even if it's too much room).
466 if G_UNLIKELY (static_search_results_allocated
< static_token_results_size
)
468 static_search_results_allocated
= static_token_results_allocated
;
469 static_search_results
= g_renew (struct search_result
,
470 static_search_results
,
471 static_search_results_allocated
);
474 for (i
= 0; i
< static_token_results_size
; i
++)
476 /* The list is sorted so that the best match for a given id
477 * will be at the front, so once we have copied an id, skip
478 * the rest of the entries for the same id.
480 if (static_token_results
[i
].app_name
== last_name
)
483 last_name
= static_token_results
[i
].app_name
;
485 static_search_results
[static_search_results_size
++] = static_token_results
[i
];
490 const gchar
*last_name
= NULL
;
494 /* We only ever remove items from the results list, so no need to
495 * resize to ensure that we have enough room.
497 for (i
= 0; i
< static_token_results_size
; i
++)
499 if (static_token_results
[i
].app_name
== last_name
)
502 last_name
= static_token_results
[i
].app_name
;
504 /* Now we only want to have a result in static_search_results
505 * if we already have it there *and* we have it in
506 * static_token_results as well. The category will be the
509 * Skip past the results in static_search_results that are not
510 * going to be matches.
512 while (k
< static_search_results_size
&&
513 static_search_results
[k
].app_name
< static_token_results
[i
].app_name
)
516 if (k
< static_search_results_size
&&
517 static_search_results
[k
].app_name
== static_token_results
[i
].app_name
)
521 * Category should be the worse of the two (ie:
522 * numerically larger).
524 static_search_results
[j
].app_name
= static_search_results
[k
].app_name
;
525 static_search_results
[j
].category
= MAX (static_search_results
[k
].category
,
526 static_token_results
[i
].category
);
531 static_search_results_size
= j
;
534 /* Clear it out for next time... */
535 static_token_results_size
= 0;
539 reset_total_search_results (void)
541 static_total_results_size
= 0;
545 sort_total_search_results (void)
547 qsort (static_total_results
, static_total_results_size
, sizeof (struct search_result
), compare_categories
);
551 merge_directory_results (void)
553 if G_UNLIKELY (static_total_results_size
+ static_search_results_size
> static_total_results_allocated
)
555 static_total_results_allocated
= MAX (16, static_total_results_allocated
);
556 while (static_total_results_allocated
< static_total_results_size
+ static_search_results_size
)
557 static_total_results_allocated
*= 2;
558 static_total_results
= g_renew (struct search_result
, static_total_results
, static_total_results_allocated
);
561 memcpy (static_total_results
+ static_total_results_size
,
562 static_search_results
,
563 static_search_results_size
* sizeof (struct search_result
));
565 static_total_results_size
+= static_search_results_size
;
567 /* Clear it out for next time... */
568 static_search_results_size
= 0;
571 /* Support for unindexed DesktopFileDirs {{{2 */
573 get_apps_from_dir (GHashTable
**apps
,
577 const char *basename
;
580 dir
= g_dir_open (dirname
, 0, NULL
);
585 while ((basename
= g_dir_read_name (dir
)) != NULL
)
589 filename
= g_build_filename (dirname
, basename
, NULL
);
591 if (g_str_has_suffix (basename
, ".desktop"))
595 app_name
= g_strconcat (prefix
, basename
, NULL
);
598 *apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
600 g_hash_table_insert (*apps
, app_name
, g_strdup (filename
));
602 else if (g_file_test (filename
, G_FILE_TEST_IS_DIR
))
606 subprefix
= g_strconcat (prefix
, basename
, "-", NULL
);
607 get_apps_from_dir (apps
, filename
, subprefix
);
622 } UnindexedMimeTweaks
;
625 free_mime_tweaks (gpointer data
)
627 UnindexedMimeTweaks
*tweaks
= data
;
629 g_strfreev (tweaks
->additions
);
630 g_strfreev (tweaks
->removals
);
631 g_strfreev (tweaks
->defaults
);
633 g_slice_free (UnindexedMimeTweaks
, tweaks
);
636 static UnindexedMimeTweaks
*
637 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir
*dir
,
638 const gchar
*mime_type
)
640 UnindexedMimeTweaks
*tweaks
;
641 gchar
*unaliased_type
;
643 unaliased_type
= _g_unix_content_type_unalias (mime_type
);
644 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
648 tweaks
= g_slice_new0 (UnindexedMimeTweaks
);
649 g_hash_table_insert (dir
->mime_tweaks
, unaliased_type
, tweaks
);
652 g_free (unaliased_type
);
657 /* consumes 'to_add' */
659 expand_strv (gchar
***strv_ptr
,
661 gchar
* const *blacklist
)
663 guint strv_len
, add_len
;
674 strv_len
= g_strv_length (strv
);
675 add_len
= g_strv_length (to_add
);
676 strv
= g_renew (gchar
*, strv
, strv_len
+ add_len
+ 1);
678 for (i
= 0; to_add
[i
]; i
++)
680 /* Don't add blacklisted strings */
682 for (j
= 0; blacklist
[j
]; j
++)
683 if (g_str_equal (to_add
[i
], blacklist
[j
]))
686 /* Don't add duplicates already in the list */
687 for (j
= 0; j
< strv_len
; j
++)
688 if (g_str_equal (to_add
[i
], strv
[j
]))
691 strv
[strv_len
++] = to_add
[i
];
698 strv
[strv_len
] = NULL
;
705 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir
*dir
,
706 const gchar
*filename
,
707 const gchar
*added_group
,
708 gboolean tweaks_permitted
)
710 const gchar default_group
[] = "Default Applications";
711 const gchar removed_group
[] = "Removed Assocations";
712 UnindexedMimeTweaks
*tweaks
;
713 char **desktop_file_ids
;
718 key_file
= g_key_file_new ();
719 if (!g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
))
721 g_key_file_free (key_file
);
725 mime_types
= g_key_file_get_keys (key_file
, added_group
, NULL
, NULL
);
727 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
729 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
730 "mimeapps.list file may add or remove associations.", filename
, added_group
);
731 g_strfreev (mime_types
);
735 if (mime_types
!= NULL
)
737 for (i
= 0; mime_types
[i
] != NULL
; i
++)
739 desktop_file_ids
= g_key_file_get_string_list (key_file
, added_group
, mime_types
[i
], NULL
, NULL
);
741 if (desktop_file_ids
)
743 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
744 expand_strv (&tweaks
->additions
, desktop_file_ids
, tweaks
->removals
);
748 g_strfreev (mime_types
);
751 mime_types
= g_key_file_get_keys (key_file
, removed_group
, NULL
, NULL
);
753 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
755 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
756 "mimeapps.list file may add or remove associations.", filename
, removed_group
);
757 g_strfreev (mime_types
);
761 if (mime_types
!= NULL
)
763 for (i
= 0; mime_types
[i
] != NULL
; i
++)
765 desktop_file_ids
= g_key_file_get_string_list (key_file
, removed_group
, mime_types
[i
], NULL
, NULL
);
767 if (desktop_file_ids
)
769 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
770 expand_strv (&tweaks
->removals
, desktop_file_ids
, tweaks
->additions
);
774 g_strfreev (mime_types
);
777 mime_types
= g_key_file_get_keys (key_file
, default_group
, NULL
, NULL
);
779 if (mime_types
!= NULL
)
781 for (i
= 0; mime_types
[i
] != NULL
; i
++)
783 desktop_file_ids
= g_key_file_get_string_list (key_file
, default_group
, mime_types
[i
], NULL
, NULL
);
785 if (desktop_file_ids
)
787 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
788 expand_strv (&tweaks
->defaults
, desktop_file_ids
, NULL
);
792 g_strfreev (mime_types
);
795 g_key_file_free (key_file
);
799 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir
*dir
)
801 const gchar
* const *desktops
;
805 dir
->mime_tweaks
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, free_mime_tweaks
);
807 /* We process in order of precedence, using a blacklisting approach to
808 * avoid recording later instructions that conflict with ones we found
811 * We first start with the XDG_CURRENT_DESKTOP files, in precedence
814 desktops
= get_lowercase_current_desktops ();
815 for (i
= 0; desktops
[i
]; i
++)
817 filename
= g_strdup_printf ("%s/%s-mimeapps.list", dir
->path
, desktops
[i
]);
818 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, "Added Associations", FALSE
);
822 /* Next, the non-desktop-specific mimeapps.list */
823 filename
= g_strdup_printf ("%s/mimeapps.list", dir
->path
);
824 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, "Added Associations", TRUE
);
827 /* The remaining files are only checked for in directories that might
828 * contain desktop files (ie: not the config dirs).
833 /* We have 'defaults.list' which was only ever understood by GLib. It
834 * exists widely, but it has never been part of any spec and it should
835 * be treated as deprecated. This will be removed in a future
838 filename
= g_strdup_printf ("%s/defaults.list", dir
->path
);
839 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, "Added Associations", FALSE
);
842 /* Finally, the mimeinfo.cache, which is just a cached copy of what we
843 * would find in the MimeTypes= lines of all of the desktop files.
845 filename
= g_strdup_printf ("%s/mimeinfo.cache", dir
->path
);
846 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, "MIME Cache", TRUE
);
851 desktop_file_dir_unindexed_init (DesktopFileDir
*dir
)
854 get_apps_from_dir (&dir
->app_names
, dir
->path
, "");
856 desktop_file_dir_unindexed_read_mimeapps_lists (dir
);
859 static GDesktopAppInfo
*
860 desktop_file_dir_unindexed_get_app (DesktopFileDir
*dir
,
861 const gchar
*desktop_id
)
863 const gchar
*filename
;
865 filename
= g_hash_table_lookup (dir
->app_names
, desktop_id
);
870 return g_desktop_app_info_new_from_filename (filename
);
874 desktop_file_dir_unindexed_get_all (DesktopFileDir
*dir
,
881 if (dir
->app_names
== NULL
)
884 g_hash_table_iter_init (&iter
, dir
->app_names
);
885 while (g_hash_table_iter_next (&iter
, &app_name
, &filename
))
887 if (desktop_file_dir_app_name_is_masked (dir
, app_name
))
890 add_to_table_if_appropriate (apps
, app_name
, g_desktop_app_info_new_from_filename (filename
));
894 typedef struct _MemoryIndexEntry MemoryIndexEntry
;
895 typedef GHashTable MemoryIndex
;
897 struct _MemoryIndexEntry
899 const gchar
*app_name
; /* pointer to the hashtable key */
901 MemoryIndexEntry
*next
;
905 memory_index_entry_free (gpointer data
)
907 MemoryIndexEntry
*mie
= data
;
911 MemoryIndexEntry
*next
= mie
->next
;
913 g_slice_free (MemoryIndexEntry
, mie
);
919 memory_index_add_token (MemoryIndex
*mi
,
922 const gchar
*app_name
)
924 MemoryIndexEntry
*mie
, *first
;
926 mie
= g_slice_new (MemoryIndexEntry
);
927 mie
->app_name
= app_name
;
928 mie
->match_category
= match_category
;
930 first
= g_hash_table_lookup (mi
, token
);
934 mie
->next
= first
->next
;
940 g_hash_table_insert (mi
, g_strdup (token
), mie
);
945 memory_index_add_string (MemoryIndex
*mi
,
948 const gchar
*app_name
)
950 gchar
**tokens
, **alternates
;
953 tokens
= g_str_tokenize_and_fold (string
, NULL
, &alternates
);
955 for (i
= 0; tokens
[i
]; i
++)
956 memory_index_add_token (mi
, tokens
[i
], match_category
, app_name
);
958 for (i
= 0; alternates
[i
]; i
++)
959 memory_index_add_token (mi
, alternates
[i
], match_category
, app_name
);
961 g_strfreev (alternates
);
966 memory_index_new (void)
968 return g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, memory_index_entry_free
);
972 desktop_file_dir_unindexed_setup_search (DesktopFileDir
*dir
)
977 dir
->memory_index
= memory_index_new ();
978 dir
->memory_implementations
= memory_index_new ();
980 /* Nothing to search? */
981 if (dir
->app_names
== NULL
)
984 g_hash_table_iter_init (&iter
, dir
->app_names
);
985 while (g_hash_table_iter_next (&iter
, &app
, &path
))
989 if (desktop_file_dir_app_name_is_masked (dir
, app
))
992 key_file
= g_key_file_new ();
994 if (g_key_file_load_from_file (key_file
, path
, G_KEY_FILE_NONE
, NULL
) &&
995 !g_key_file_get_boolean (key_file
, "Desktop Entry", "Hidden", NULL
))
997 /* Index the interesting keys... */
1001 for (i
= 0; i
< G_N_ELEMENTS (desktop_key_match_category
); i
++)
1006 if (!desktop_key_match_category
[i
])
1009 raw
= g_key_file_get_locale_string (key_file
, "Desktop Entry", desktop_key_get_name (i
), NULL
, NULL
);
1012 if (i
== DESKTOP_KEY_Exec
&& raw
!= NULL
)
1014 /* Special handling: only match basename of first field */
1018 /* Remove extra arguments, if any */
1019 space
= raw
+ strcspn (raw
, " \t\n"); /* IFS */
1022 /* Skip the pathname, if any */
1023 if ((slash
= strrchr (raw
, '/')))
1028 memory_index_add_string (dir
->memory_index
, value
, desktop_key_match_category
[i
], app
);
1033 /* Make note of the Implements= line */
1034 implements
= g_key_file_get_string_list (key_file
, "Desktop Entry", "Implements", NULL
, NULL
);
1035 for (i
= 0; implements
&& implements
[i
]; i
++)
1036 memory_index_add_token (dir
->memory_implementations
, implements
[i
], 0, app
);
1037 g_strfreev (implements
);
1040 g_key_file_free (key_file
);
1045 desktop_file_dir_unindexed_search (DesktopFileDir
*dir
,
1046 const gchar
*search_token
)
1048 GHashTableIter iter
;
1049 gpointer key
, value
;
1051 if (!dir
->memory_index
)
1052 desktop_file_dir_unindexed_setup_search (dir
);
1054 g_hash_table_iter_init (&iter
, dir
->memory_index
);
1055 while (g_hash_table_iter_next (&iter
, &key
, &value
))
1057 MemoryIndexEntry
*mie
= value
;
1059 if (!g_str_has_prefix (key
, search_token
))
1064 add_token_result (mie
->app_name
, mie
->match_category
);
1071 array_contains (GPtrArray
*array
,
1076 for (i
= 0; i
< array
->len
; i
++)
1077 if (g_str_equal (array
->pdata
[i
], str
))
1084 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir
*dir
,
1085 const gchar
*mime_type
,
1087 GPtrArray
*blacklist
)
1089 UnindexedMimeTweaks
*tweaks
;
1092 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1097 if (tweaks
->additions
)
1099 for (i
= 0; tweaks
->additions
[i
]; i
++)
1101 gchar
*app_name
= tweaks
->additions
[i
];
1103 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1104 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1105 g_ptr_array_add (hits
, g_strdup (app_name
));
1109 if (tweaks
->removals
)
1111 for (i
= 0; tweaks
->removals
[i
]; i
++)
1113 gchar
*app_name
= tweaks
->removals
[i
];
1115 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1116 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1117 g_ptr_array_add (blacklist
, app_name
);
1123 desktop_file_dir_unindexed_default_lookup (DesktopFileDir
*dir
,
1124 const gchar
*mime_type
,
1127 UnindexedMimeTweaks
*tweaks
;
1130 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1132 if (!tweaks
|| !tweaks
->defaults
)
1135 for (i
= 0; tweaks
->defaults
[i
]; i
++)
1137 gchar
*app_name
= tweaks
->defaults
[i
];
1139 if (!array_contains (results
, app_name
))
1140 g_ptr_array_add (results
, g_strdup (app_name
));
1145 desktop_file_dir_unindexed_get_implementations (DesktopFileDir
*dir
,
1147 const gchar
*interface
)
1149 MemoryIndexEntry
*mie
;
1151 if (!dir
->memory_index
)
1152 desktop_file_dir_unindexed_setup_search (dir
);
1154 for (mie
= g_hash_table_lookup (dir
->memory_implementations
, interface
); mie
; mie
= mie
->next
)
1155 *results
= g_list_prepend (*results
, g_strdup (mie
->app_name
));
1158 /* DesktopFileDir "API" {{{2 */
1161 * desktop_file_dir_create:
1162 * @array: the #GArray to add a new item to
1163 * @data_dir: an XDG_DATA_DIR
1165 * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
1169 desktop_file_dir_create (GArray
*array
,
1170 const gchar
*data_dir
)
1172 DesktopFileDir dir
= { 0, };
1174 dir
.path
= g_build_filename (data_dir
, "applications", NULL
);
1176 g_array_append_val (array
, dir
);
1180 * desktop_file_dir_create:
1181 * @array: the #GArray to add a new item to
1182 * @config_dir: an XDG_CONFIG_DIR
1184 * Just the same as desktop_file_dir_create() except that it does not
1185 * add the "applications" directory. It also marks the directory as
1186 * config-only, which prevents us from attempting to find desktop files
1190 desktop_file_dir_create_for_config (GArray
*array
,
1191 const gchar
*config_dir
)
1193 DesktopFileDir dir
= { 0, };
1195 dir
.path
= g_strdup (config_dir
);
1196 dir
.is_config
= TRUE
;
1198 g_array_append_val (array
, dir
);
1202 * desktop_file_dir_reset:
1203 * @dir: a #DesktopFileDir
1205 * Cleans up @dir, releasing most resources that it was using.
1208 desktop_file_dir_reset (DesktopFileDir
*dir
)
1212 g_signal_handlers_disconnect_by_func (dir
->monitor
, desktop_file_dir_changed
, dir
);
1213 g_object_unref (dir
->monitor
);
1214 dir
->monitor
= NULL
;
1219 g_hash_table_unref (dir
->app_names
);
1220 dir
->app_names
= NULL
;
1223 if (dir
->memory_index
)
1225 g_hash_table_unref (dir
->memory_index
);
1226 dir
->memory_index
= NULL
;
1229 if (dir
->mime_tweaks
)
1231 g_hash_table_unref (dir
->mime_tweaks
);
1232 dir
->mime_tweaks
= NULL
;
1235 if (dir
->memory_implementations
)
1237 g_hash_table_unref (dir
->memory_implementations
);
1238 dir
->memory_implementations
= NULL
;
1241 dir
->is_setup
= FALSE
;
1245 * desktop_file_dir_init:
1246 * @dir: a #DesktopFileDir
1248 * Does initial setup for @dir
1250 * You should only call this if @dir is not already setup.
1253 desktop_file_dir_init (DesktopFileDir
*dir
)
1255 g_assert (!dir
->is_setup
);
1257 g_assert (!dir
->monitor
);
1258 dir
->monitor
= g_local_directory_monitor_new_in_worker (dir
->path
, G_FILE_MONITOR_NONE
, NULL
);
1262 g_signal_connect (dir
->monitor
, "changed", G_CALLBACK (desktop_file_dir_changed
), dir
);
1263 g_local_directory_monitor_start (dir
->monitor
);
1266 desktop_file_dir_unindexed_init (dir
);
1268 dir
->is_setup
= TRUE
;
1272 * desktop_file_dir_get_app:
1273 * @dir: a DesktopFileDir
1274 * @desktop_id: the desktop ID to load
1276 * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1277 * within @dir, even if it is hidden.
1279 * This function does not check if @desktop_id would be masked by a
1280 * directory with higher precedence. The caller must do so.
1282 static GDesktopAppInfo
*
1283 desktop_file_dir_get_app (DesktopFileDir
*dir
,
1284 const gchar
*desktop_id
)
1286 if (!dir
->app_names
)
1289 return desktop_file_dir_unindexed_get_app (dir
, desktop_id
);
1293 * desktop_file_dir_get_all:
1294 * @dir: a DesktopFileDir
1295 * @apps: a #GHashTable<string, GDesktopAppInfo>
1297 * Loads all desktop files in @dir and adds them to @apps, careful to
1298 * ensure we don't add any files masked by a similarly-named file in a
1299 * higher-precedence directory.
1302 desktop_file_dir_get_all (DesktopFileDir
*dir
,
1305 desktop_file_dir_unindexed_get_all (dir
, apps
);
1309 * desktop_file_dir_mime_lookup:
1310 * @dir: a #DesktopFileDir
1311 * @mime_type: the mime type to look up
1312 * @hits: the array to store the hits
1313 * @blacklist: the array to store the blacklist
1315 * Does a lookup of a mimetype against one desktop file directory,
1316 * recording any hits and blacklisting and "Removed" associations (so
1317 * later directories don't record them as hits).
1319 * The items added to @hits are duplicated, but the ones in @blacklist
1320 * are weak pointers. This facilitates simply freeing the blacklist
1321 * (which is only used for internal bookkeeping) but using the pdata of
1322 * @hits as the result of the operation.
1325 desktop_file_dir_mime_lookup (DesktopFileDir
*dir
,
1326 const gchar
*mime_type
,
1328 GPtrArray
*blacklist
)
1330 desktop_file_dir_unindexed_mime_lookup (dir
, mime_type
, hits
, blacklist
);
1334 * desktop_file_dir_default_lookup:
1335 * @dir: a #DesktopFileDir
1336 * @mime_type: the mime type to look up
1337 * @results: an array to store the results in
1339 * Collects the "default" applications for a given mime type from @dir.
1342 desktop_file_dir_default_lookup (DesktopFileDir
*dir
,
1343 const gchar
*mime_type
,
1346 desktop_file_dir_unindexed_default_lookup (dir
, mime_type
, results
);
1350 * desktop_file_dir_search:
1351 * @dir: a #DesktopFileDir
1352 * @term: a normalised and casefolded search term
1354 * Finds the names of applications in @dir that match @term.
1357 desktop_file_dir_search (DesktopFileDir
*dir
,
1358 const gchar
*search_token
)
1360 desktop_file_dir_unindexed_search (dir
, search_token
);
1364 desktop_file_dir_get_implementations (DesktopFileDir
*dir
,
1366 const gchar
*interface
)
1368 desktop_file_dir_unindexed_get_implementations (dir
, results
, interface
);
1371 /* Lock/unlock and global setup API {{{2 */
1374 desktop_file_dirs_lock (void)
1378 g_mutex_lock (&desktop_file_dir_lock
);
1380 if (desktop_file_dirs
== NULL
)
1382 const char * const *dirs
;
1386 tmp
= g_array_new (FALSE
, FALSE
, sizeof (DesktopFileDir
));
1388 /* First, the configs. Highest priority: the user's ~/.config */
1389 desktop_file_dir_create_for_config (tmp
, g_get_user_config_dir ());
1391 /* Next, the system configs (/etc/xdg, and so on). */
1392 dirs
= g_get_system_config_dirs ();
1393 for (i
= 0; dirs
[i
]; i
++)
1394 desktop_file_dir_create_for_config (tmp
, dirs
[i
]);
1396 /* Now the data. Highest priority: the user's ~/.local/share/applications */
1397 desktop_file_dir_user_data_index
= tmp
->len
;
1398 desktop_file_dir_create (tmp
, g_get_user_data_dir ());
1400 /* Following that, XDG_DATA_DIRS/applications, in order */
1401 dirs
= g_get_system_data_dirs ();
1402 for (i
= 0; dirs
[i
]; i
++)
1403 desktop_file_dir_create (tmp
, dirs
[i
]);
1405 /* The list of directories will never change after this. */
1406 desktop_file_dirs
= (DesktopFileDir
*) tmp
->data
;
1407 n_desktop_file_dirs
= tmp
->len
;
1409 g_array_free (tmp
, FALSE
);
1412 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1413 if (!desktop_file_dirs
[i
].is_setup
)
1414 desktop_file_dir_init (&desktop_file_dirs
[i
]);
1418 desktop_file_dirs_unlock (void)
1420 g_mutex_unlock (&desktop_file_dir_lock
);
1424 desktop_file_dirs_invalidate_user_config (void)
1426 g_mutex_lock (&desktop_file_dir_lock
);
1428 if (n_desktop_file_dirs
)
1429 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_config_index
]);
1431 g_mutex_unlock (&desktop_file_dir_lock
);
1435 desktop_file_dirs_invalidate_user_data (void)
1437 g_mutex_lock (&desktop_file_dir_lock
);
1439 if (n_desktop_file_dirs
)
1440 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_data_index
]);
1442 g_mutex_unlock (&desktop_file_dir_lock
);
1445 /* GDesktopAppInfo implementation {{{1 */
1446 /* GObject implementation {{{2 */
1448 g_desktop_app_info_finalize (GObject
*object
)
1450 GDesktopAppInfo
*info
;
1452 info
= G_DESKTOP_APP_INFO (object
);
1454 g_free (info
->desktop_id
);
1455 g_free (info
->filename
);
1458 g_key_file_unref (info
->keyfile
);
1460 g_free (info
->name
);
1461 g_free (info
->generic_name
);
1462 g_free (info
->fullname
);
1463 g_free (info
->comment
);
1464 g_free (info
->icon_name
);
1466 g_object_unref (info
->icon
);
1467 g_strfreev (info
->keywords
);
1468 g_strfreev (info
->only_show_in
);
1469 g_strfreev (info
->not_show_in
);
1470 g_free (info
->try_exec
);
1471 g_free (info
->exec
);
1472 g_free (info
->binary
);
1473 g_free (info
->path
);
1474 g_free (info
->categories
);
1475 g_free (info
->startup_wm_class
);
1476 g_strfreev (info
->mime_types
);
1477 g_free (info
->app_id
);
1478 g_strfreev (info
->actions
);
1480 G_OBJECT_CLASS (g_desktop_app_info_parent_class
)->finalize (object
);
1484 g_desktop_app_info_set_property (GObject
*object
,
1486 const GValue
*value
,
1489 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1494 self
->filename
= g_value_dup_string (value
);
1498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1504 g_desktop_app_info_get_property (GObject
*object
,
1509 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1514 g_value_set_string (value
, self
->filename
);
1517 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1523 g_desktop_app_info_class_init (GDesktopAppInfoClass
*klass
)
1525 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
1527 gobject_class
->get_property
= g_desktop_app_info_get_property
;
1528 gobject_class
->set_property
= g_desktop_app_info_set_property
;
1529 gobject_class
->finalize
= g_desktop_app_info_finalize
;
1532 * GDesktopAppInfo:filename:
1534 * The origin filename of this #GDesktopAppInfo
1536 g_object_class_install_property (gobject_class
,
1538 g_param_spec_string ("filename", "Filename", "", NULL
,
1539 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1543 g_desktop_app_info_init (GDesktopAppInfo
*local
)
1547 /* Construction... {{{2 */
1551 * @exec: an exec line
1553 * Returns the first word in an exec line (ie: the binary name).
1555 * If @exec is " progname --foo %F" then returns "progname".
1558 binary_from_exec (const char *exec
)
1560 const char *p
, *start
;
1566 while (*p
!= ' ' && *p
!= 0)
1569 return g_strndup (start
, p
- start
);
1573 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo
*info
,
1580 gboolean bus_activatable
;
1582 start_group
= g_key_file_get_start_group (key_file
);
1583 if (start_group
== NULL
|| strcmp (start_group
, G_KEY_FILE_DESKTOP_GROUP
) != 0)
1585 g_free (start_group
);
1588 g_free (start_group
);
1590 type
= g_key_file_get_string (key_file
,
1591 G_KEY_FILE_DESKTOP_GROUP
,
1592 G_KEY_FILE_DESKTOP_KEY_TYPE
,
1594 if (type
== NULL
|| strcmp (type
, G_KEY_FILE_DESKTOP_TYPE_APPLICATION
) != 0)
1601 try_exec
= g_key_file_get_string (key_file
,
1602 G_KEY_FILE_DESKTOP_GROUP
,
1603 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC
,
1605 if (try_exec
&& try_exec
[0] != '\0')
1608 t
= g_find_program_in_path (try_exec
);
1617 exec
= g_key_file_get_string (key_file
,
1618 G_KEY_FILE_DESKTOP_GROUP
,
1619 G_KEY_FILE_DESKTOP_KEY_EXEC
,
1621 if (exec
&& exec
[0] != '\0')
1625 if (!g_shell_parse_argv (exec
, &argc
, &argv
, NULL
))
1634 t
= g_find_program_in_path (argv
[0]);
1647 info
->name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NAME
, NULL
, NULL
);
1648 info
->generic_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, GENERIC_NAME_KEY
, NULL
, NULL
);
1649 info
->fullname
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, FULL_NAME_KEY
, NULL
, NULL
);
1650 info
->keywords
= g_key_file_get_locale_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, KEYWORDS_KEY
, NULL
, NULL
, NULL
);
1651 info
->comment
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_COMMENT
, NULL
, NULL
);
1652 info
->nodisplay
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, NULL
) != FALSE
;
1653 info
->icon_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ICON
, NULL
, NULL
);
1654 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
);
1655 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
);
1656 info
->try_exec
= try_exec
;
1658 info
->path
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_PATH
, NULL
);
1659 info
->terminal
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_TERMINAL
, NULL
) != FALSE
;
1660 info
->startup_notify
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY
, NULL
) != FALSE
;
1661 info
->no_fuse
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, "X-GIO-NoFuse", NULL
) != FALSE
;
1662 info
->hidden
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_HIDDEN
, NULL
) != FALSE
;
1663 info
->categories
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_CATEGORIES
, NULL
);
1664 info
->startup_wm_class
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, STARTUP_WM_CLASS_KEY
, NULL
);
1665 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
);
1666 bus_activatable
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE
, NULL
);
1667 info
->actions
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ACTIONS
, NULL
, NULL
);
1669 /* Remove the special-case: no Actions= key just means 0 extra actions */
1670 if (info
->actions
== NULL
)
1671 info
->actions
= g_new0 (gchar
*, 0 + 1);
1674 if (info
->icon_name
)
1676 if (g_path_is_absolute (info
->icon_name
))
1680 file
= g_file_new_for_path (info
->icon_name
);
1681 info
->icon
= g_file_icon_new (file
);
1682 g_object_unref (file
);
1688 /* Work around a common mistake in desktop files */
1689 if ((p
= strrchr (info
->icon_name
, '.')) != NULL
&&
1690 (strcmp (p
, ".png") == 0 ||
1691 strcmp (p
, ".xpm") == 0 ||
1692 strcmp (p
, ".svg") == 0))
1695 info
->icon
= g_themed_icon_new (info
->icon_name
);
1700 info
->binary
= binary_from_exec (info
->exec
);
1702 if (info
->path
&& info
->path
[0] == '\0')
1704 g_free (info
->path
);
1708 /* Can only be DBusActivatable if we know the filename, which means
1709 * that this won't work for the load-from-keyfile case.
1711 if (bus_activatable
&& info
->filename
)
1716 basename
= g_path_get_basename (info
->filename
);
1717 last_dot
= strrchr (basename
, '.');
1719 if (last_dot
&& g_str_equal (last_dot
, ".desktop"))
1723 if (g_dbus_is_interface_name (basename
))
1724 info
->app_id
= g_strdup (basename
);
1730 info
->keyfile
= g_key_file_ref (key_file
);
1736 g_desktop_app_info_load_file (GDesktopAppInfo
*self
)
1739 gboolean retval
= FALSE
;
1741 g_return_val_if_fail (self
->filename
!= NULL
, FALSE
);
1743 self
->desktop_id
= g_path_get_basename (self
->filename
);
1745 key_file
= g_key_file_new ();
1747 if (g_key_file_load_from_file (key_file
, self
->filename
, G_KEY_FILE_NONE
, NULL
))
1748 retval
= g_desktop_app_info_load_from_keyfile (self
, key_file
);
1750 g_key_file_unref (key_file
);
1755 * g_desktop_app_info_new_from_keyfile:
1756 * @key_file: an opened #GKeyFile
1758 * Creates a new #GDesktopAppInfo.
1760 * Returns: a new #GDesktopAppInfo or %NULL on error.
1765 g_desktop_app_info_new_from_keyfile (GKeyFile
*key_file
)
1767 GDesktopAppInfo
*info
;
1769 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1770 info
->filename
= NULL
;
1771 if (!g_desktop_app_info_load_from_keyfile (info
, key_file
))
1773 g_object_unref (info
);
1780 * g_desktop_app_info_new_from_filename:
1781 * @filename: the path of a desktop file, in the GLib filename encoding
1783 * Creates a new #GDesktopAppInfo.
1785 * Returns: a new #GDesktopAppInfo or %NULL on error.
1788 g_desktop_app_info_new_from_filename (const char *filename
)
1790 GDesktopAppInfo
*info
= NULL
;
1792 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, "filename", filename
, NULL
);
1793 if (!g_desktop_app_info_load_file (info
))
1795 g_object_unref (info
);
1802 * g_desktop_app_info_new:
1803 * @desktop_id: the desktop file id
1805 * Creates a new #GDesktopAppInfo based on a desktop file id.
1807 * A desktop file id is the basename of the desktop file, including the
1808 * .desktop extension. GIO is looking for a desktop file with this name
1809 * in the `applications` subdirectories of the XDG
1810 * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1811 * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1812 * prefix-to-subdirectory mapping that is described in the
1813 * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1814 * (i.e. a desktop id of kde-foo.desktop will match
1815 * `/usr/share/applications/kde/foo.desktop`).
1817 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
1820 g_desktop_app_info_new (const char *desktop_id
)
1822 GDesktopAppInfo
*appinfo
= NULL
;
1825 desktop_file_dirs_lock ();
1827 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1829 appinfo
= desktop_file_dir_get_app (&desktop_file_dirs
[i
], desktop_id
);
1835 desktop_file_dirs_unlock ();
1837 if (appinfo
== NULL
)
1840 g_free (appinfo
->desktop_id
);
1841 appinfo
->desktop_id
= g_strdup (desktop_id
);
1843 if (g_desktop_app_info_get_is_hidden (appinfo
))
1845 g_object_unref (appinfo
);
1853 g_desktop_app_info_dup (GAppInfo
*appinfo
)
1855 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1856 GDesktopAppInfo
*new_info
;
1858 new_info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1860 new_info
->filename
= g_strdup (info
->filename
);
1861 new_info
->desktop_id
= g_strdup (info
->desktop_id
);
1864 new_info
->keyfile
= g_key_file_ref (info
->keyfile
);
1866 new_info
->name
= g_strdup (info
->name
);
1867 new_info
->generic_name
= g_strdup (info
->generic_name
);
1868 new_info
->fullname
= g_strdup (info
->fullname
);
1869 new_info
->keywords
= g_strdupv (info
->keywords
);
1870 new_info
->comment
= g_strdup (info
->comment
);
1871 new_info
->nodisplay
= info
->nodisplay
;
1872 new_info
->icon_name
= g_strdup (info
->icon_name
);
1874 new_info
->icon
= g_object_ref (info
->icon
);
1875 new_info
->only_show_in
= g_strdupv (info
->only_show_in
);
1876 new_info
->not_show_in
= g_strdupv (info
->not_show_in
);
1877 new_info
->try_exec
= g_strdup (info
->try_exec
);
1878 new_info
->exec
= g_strdup (info
->exec
);
1879 new_info
->binary
= g_strdup (info
->binary
);
1880 new_info
->path
= g_strdup (info
->path
);
1881 new_info
->app_id
= g_strdup (info
->app_id
);
1882 new_info
->hidden
= info
->hidden
;
1883 new_info
->terminal
= info
->terminal
;
1884 new_info
->startup_notify
= info
->startup_notify
;
1886 return G_APP_INFO (new_info
);
1889 /* GAppInfo interface implementation functions {{{2 */
1892 g_desktop_app_info_equal (GAppInfo
*appinfo1
,
1895 GDesktopAppInfo
*info1
= G_DESKTOP_APP_INFO (appinfo1
);
1896 GDesktopAppInfo
*info2
= G_DESKTOP_APP_INFO (appinfo2
);
1898 if (info1
->desktop_id
== NULL
||
1899 info2
->desktop_id
== NULL
)
1900 return info1
== info2
;
1902 return strcmp (info1
->desktop_id
, info2
->desktop_id
) == 0;
1906 g_desktop_app_info_get_id (GAppInfo
*appinfo
)
1908 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1910 return info
->desktop_id
;
1914 g_desktop_app_info_get_name (GAppInfo
*appinfo
)
1916 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1918 if (info
->name
== NULL
)
1919 return _("Unnamed");
1924 g_desktop_app_info_get_display_name (GAppInfo
*appinfo
)
1926 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1928 if (info
->fullname
== NULL
)
1929 return g_desktop_app_info_get_name (appinfo
);
1930 return info
->fullname
;
1934 * g_desktop_app_info_get_is_hidden:
1935 * @info: a #GDesktopAppInfo.
1937 * A desktop file is hidden if the Hidden key in it is
1940 * Returns: %TRUE if hidden, %FALSE otherwise.
1943 g_desktop_app_info_get_is_hidden (GDesktopAppInfo
*info
)
1945 return info
->hidden
;
1949 * g_desktop_app_info_get_filename:
1950 * @info: a #GDesktopAppInfo
1952 * When @info was created from a known filename, return it. In some
1953 * situations such as the #GDesktopAppInfo returned from
1954 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
1956 * Returns: The full path to the file for @info, or %NULL if not known.
1960 g_desktop_app_info_get_filename (GDesktopAppInfo
*info
)
1962 return info
->filename
;
1966 g_desktop_app_info_get_description (GAppInfo
*appinfo
)
1968 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1970 return info
->comment
;
1974 g_desktop_app_info_get_executable (GAppInfo
*appinfo
)
1976 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1978 return info
->binary
;
1982 g_desktop_app_info_get_commandline (GAppInfo
*appinfo
)
1984 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1990 g_desktop_app_info_get_icon (GAppInfo
*appinfo
)
1992 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1998 * g_desktop_app_info_get_categories:
1999 * @info: a #GDesktopAppInfo
2001 * Gets the categories from the desktop file.
2003 * Returns: The unparsed Categories key from the desktop file;
2004 * i.e. no attempt is made to split it by ';' or validate it.
2007 g_desktop_app_info_get_categories (GDesktopAppInfo
*info
)
2009 return info
->categories
;
2013 * g_desktop_app_info_get_keywords:
2014 * @info: a #GDesktopAppInfo
2016 * Gets the keywords from the desktop file.
2018 * Returns: (transfer none): The value of the Keywords key
2022 const char * const *
2023 g_desktop_app_info_get_keywords (GDesktopAppInfo
*info
)
2025 return (const char * const *)info
->keywords
;
2029 * g_desktop_app_info_get_generic_name:
2030 * @info: a #GDesktopAppInfo
2032 * Gets the generic name from the destkop file.
2034 * Returns: The value of the GenericName key
2037 g_desktop_app_info_get_generic_name (GDesktopAppInfo
*info
)
2039 return info
->generic_name
;
2043 * g_desktop_app_info_get_nodisplay:
2044 * @info: a #GDesktopAppInfo
2046 * Gets the value of the NoDisplay key, which helps determine if the
2047 * application info should be shown in menus. See
2048 * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2050 * Returns: The value of the NoDisplay key
2055 g_desktop_app_info_get_nodisplay (GDesktopAppInfo
*info
)
2057 return info
->nodisplay
;
2061 * g_desktop_app_info_get_show_in:
2062 * @info: a #GDesktopAppInfo
2063 * @desktop_env: (nullable): a string specifying a desktop name
2065 * Checks if the application info should be shown in menus that list available
2066 * applications for a specific name of the desktop, based on the
2067 * `OnlyShowIn` and `NotShowIn` keys.
2069 * @desktop_env should typically be given as %NULL, in which case the
2070 * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
2071 * to override the default mechanism then you may specify @desktop_env,
2072 * but this is not recommended.
2074 * Note that g_app_info_should_show() for @info will include this check (with
2075 * %NULL for @desktop_env) as well as additional checks.
2077 * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2078 * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2084 g_desktop_app_info_get_show_in (GDesktopAppInfo
*info
,
2085 const gchar
*desktop_env
)
2087 const gchar
*specified_envs
[] = { desktop_env
, NULL
};
2088 const gchar
* const *envs
;
2091 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
2094 envs
= specified_envs
;
2096 envs
= get_current_desktops (NULL
);
2098 for (i
= 0; envs
[i
]; i
++)
2102 if (info
->only_show_in
)
2103 for (j
= 0; info
->only_show_in
[j
]; j
++)
2104 if (g_str_equal (info
->only_show_in
[j
], envs
[i
]))
2107 if (info
->not_show_in
)
2108 for (j
= 0; info
->not_show_in
[j
]; j
++)
2109 if (g_str_equal (info
->not_show_in
[j
], envs
[i
]))
2113 return info
->only_show_in
== NULL
;
2116 /* Launching... {{{2 */
2119 expand_macro_single (char macro
, char *uri
)
2122 char *result
= NULL
;
2126 file
= g_file_new_for_uri (uri
);
2132 result
= g_shell_quote (uri
);
2136 path
= g_file_get_path (file
);
2138 result
= g_shell_quote (path
);
2142 path
= g_file_get_path (file
);
2145 name
= g_path_get_dirname (path
);
2146 result
= g_shell_quote (name
);
2152 path
= g_file_get_path (file
);
2155 name
= g_path_get_basename (path
);
2156 result
= g_shell_quote (name
);
2162 g_object_unref (file
);
2169 expand_macro (char macro
,
2171 GDesktopAppInfo
*info
,
2174 GList
*uris
= *uri_list
;
2176 gboolean force_file_uri
;
2177 char force_file_uri_macro
;
2180 g_return_if_fail (exec
!= NULL
);
2182 /* On %u and %U, pass POSIX file path pointing to the URI via
2183 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2184 * running or the URI doesn't have a POSIX file path via FUSE
2185 * we'll just pass the URI.
2187 force_file_uri_macro
= macro
;
2188 force_file_uri
= FALSE
;
2194 force_file_uri_macro
= 'f';
2195 force_file_uri
= TRUE
;
2198 force_file_uri_macro
= 'F';
2199 force_file_uri
= TRUE
;
2215 if (!force_file_uri
||
2216 /* Pass URI if it contains an anchor */
2217 strchr (uri
, '#') != NULL
)
2219 expanded
= expand_macro_single (macro
, uri
);
2223 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2224 if (expanded
== NULL
)
2225 expanded
= expand_macro_single (macro
, uri
);
2230 g_string_append (exec
, expanded
);
2246 if (!force_file_uri
||
2247 /* Pass URI if it contains an anchor */
2248 strchr (uri
, '#') != NULL
)
2250 expanded
= expand_macro_single (macro
, uri
);
2254 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2255 if (expanded
== NULL
)
2256 expanded
= expand_macro_single (macro
, uri
);
2261 g_string_append (exec
, expanded
);
2267 if (uris
!= NULL
&& expanded
)
2268 g_string_append_c (exec
, ' ');
2274 if (info
->icon_name
)
2276 g_string_append (exec
, "--icon ");
2277 expanded
= g_shell_quote (info
->icon_name
);
2278 g_string_append (exec
, expanded
);
2286 expanded
= g_shell_quote (info
->name
);
2287 g_string_append (exec
, expanded
);
2295 expanded
= g_shell_quote (info
->filename
);
2296 g_string_append (exec
, expanded
);
2301 case 'm': /* deprecated */
2305 g_string_append_c (exec
, '%');
2313 expand_application_parameters (GDesktopAppInfo
*info
,
2314 const gchar
*exec_line
,
2320 GList
*uri_list
= *uris
;
2321 const char *p
= exec_line
;
2322 GString
*expanded_exec
;
2325 if (exec_line
== NULL
)
2327 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2328 _("Desktop file didn't specify Exec field"));
2332 expanded_exec
= g_string_new (NULL
);
2336 if (p
[0] == '%' && p
[1] != '\0')
2338 expand_macro (p
[1], expanded_exec
, info
, uris
);
2342 g_string_append_c (expanded_exec
, *p
);
2347 /* No file substitutions */
2348 if (uri_list
== *uris
&& uri_list
!= NULL
)
2350 /* If there is no macro default to %f. This is also what KDE does */
2351 g_string_append_c (expanded_exec
, ' ');
2352 expand_macro ('f', expanded_exec
, info
, uris
);
2355 res
= g_shell_parse_argv (expanded_exec
->str
, argc
, argv
, error
);
2356 g_string_free (expanded_exec
, TRUE
);
2361 prepend_terminal_to_vector (int *argc
,
2368 char **term_argv
= NULL
;
2373 g_return_val_if_fail (argc
!= NULL
, FALSE
);
2374 g_return_val_if_fail (argv
!= NULL
, FALSE
);
2382 /* compute size if not given */
2385 for (i
= 0; the_argv
[i
] != NULL
; i
++)
2391 term_argv
= g_new0 (char *, 3);
2393 check
= g_find_program_in_path ("gnome-terminal");
2396 term_argv
[0] = check
;
2397 /* Note that gnome-terminal takes -x and
2398 * as -e in gnome-terminal is broken we use that. */
2399 term_argv
[1] = g_strdup ("-x");
2404 check
= g_find_program_in_path ("nxterm");
2406 check
= g_find_program_in_path ("color-xterm");
2408 check
= g_find_program_in_path ("rxvt");
2410 check
= g_find_program_in_path ("xterm");
2412 check
= g_find_program_in_path ("dtterm");
2415 check
= g_strdup ("xterm");
2416 g_warning ("couldn't find a terminal, falling back to xterm");
2418 term_argv
[0] = check
;
2419 term_argv
[1] = g_strdup ("-e");
2422 real_argc
= term_argc
+ *argc
;
2423 real_argv
= g_new (char *, real_argc
+ 1);
2425 for (i
= 0; i
< term_argc
; i
++)
2426 real_argv
[i
] = term_argv
[i
];
2428 for (j
= 0; j
< *argc
; j
++, i
++)
2429 real_argv
[i
] = (char *)the_argv
[j
];
2431 real_argv
[i
] = NULL
;
2437 /* we use g_free here as we sucked all the inner strings
2438 * out from it into real_argv */
2443 #endif /* G_OS_WIN32 */
2447 create_files_for_uris (GList
*uris
)
2454 for (iter
= uris
; iter
; iter
= iter
->next
)
2456 GFile
*file
= g_file_new_for_uri ((char *)iter
->data
);
2457 res
= g_list_prepend (res
, file
);
2460 return g_list_reverse (res
);
2465 GSpawnChildSetupFunc user_setup
;
2466 gpointer user_setup_data
;
2472 child_setup (gpointer user_data
)
2474 ChildSetupData
*data
= user_data
;
2476 if (data
->pid_envvar
)
2478 pid_t pid
= getpid ();
2482 /* Write the pid into the space already reserved for it in the
2483 * environment array. We can't use sprintf because it might
2484 * malloc, so we do it by hand. It's simplest to write the pid
2485 * out backwards first, then copy it over.
2487 for (i
= 0; pid
; i
++, pid
/= 10)
2488 buf
[i
] = (pid
% 10) + '0';
2489 for (i
--; i
>= 0; i
--)
2490 *(data
->pid_envvar
++) = buf
[i
];
2491 *data
->pid_envvar
= '\0';
2494 if (data
->user_setup
)
2495 data
->user_setup (data
->user_setup_data
);
2499 notify_desktop_launch (GDBusConnection
*session_bus
,
2500 GDesktopAppInfo
*info
,
2502 const char *display
,
2507 GVariantBuilder uri_variant
;
2508 GVariantBuilder extras_variant
;
2510 const char *desktop_file_id
;
2511 const char *gio_desktop_file
;
2513 if (session_bus
== NULL
)
2516 g_variant_builder_init (&uri_variant
, G_VARIANT_TYPE ("as"));
2517 for (iter
= uris
; iter
; iter
= iter
->next
)
2518 g_variant_builder_add (&uri_variant
, "s", iter
->data
);
2520 g_variant_builder_init (&extras_variant
, G_VARIANT_TYPE ("a{sv}"));
2521 if (sn_id
!= NULL
&& g_utf8_validate (sn_id
, -1, NULL
))
2522 g_variant_builder_add (&extras_variant
, "{sv}",
2526 gio_desktop_file
= g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2527 if (gio_desktop_file
!= NULL
)
2528 g_variant_builder_add (&extras_variant
, "{sv}",
2529 "origin-desktop-file",
2530 g_variant_new_bytestring (gio_desktop_file
));
2531 if (g_get_prgname () != NULL
)
2532 g_variant_builder_add (&extras_variant
, "{sv}",
2534 g_variant_new_bytestring (g_get_prgname ()));
2535 g_variant_builder_add (&extras_variant
, "{sv}",
2538 (gint64
)getpid ()));
2541 desktop_file_id
= info
->filename
;
2542 else if (info
->desktop_id
)
2543 desktop_file_id
= info
->desktop_id
;
2545 desktop_file_id
= "";
2547 msg
= g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2548 "org.gtk.gio.DesktopAppInfo",
2550 g_dbus_message_set_body (msg
, g_variant_new ("(@aysxasa{sv})",
2551 g_variant_new_bytestring (desktop_file_id
),
2552 display
? display
: "",
2556 g_dbus_connection_send_message (session_bus
,
2560 g_object_unref (msg
);
2563 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2566 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo
*info
,
2567 GDBusConnection
*session_bus
,
2568 const gchar
*exec_line
,
2570 GAppLaunchContext
*launch_context
,
2571 GSpawnFlags spawn_flags
,
2572 GSpawnChildSetupFunc user_setup
,
2573 gpointer user_setup_data
,
2574 GDesktopAppLaunchCallback pid_callback
,
2575 gpointer pid_callback_data
,
2578 gboolean completed
= FALSE
;
2580 char **argv
, **envp
;
2582 ChildSetupData data
;
2584 g_return_val_if_fail (info
!= NULL
, FALSE
);
2589 envp
= g_app_launch_context_get_environment (launch_context
);
2591 envp
= g_get_environ ();
2596 GList
*launched_uris
;
2598 char *display
, *sn_id
= NULL
;
2601 if (!expand_application_parameters (info
, exec_line
, &uris
, &argc
, &argv
, error
))
2604 /* Get the subset of URIs we're launching with this process */
2605 launched_uris
= NULL
;
2606 for (iter
= old_uris
; iter
!= NULL
&& iter
!= uris
; iter
= iter
->next
)
2607 launched_uris
= g_list_prepend (launched_uris
, iter
->data
);
2608 launched_uris
= g_list_reverse (launched_uris
);
2610 if (info
->terminal
&& !prepend_terminal_to_vector (&argc
, &argv
))
2612 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2613 _("Unable to find terminal required for application"));
2617 data
.user_setup
= user_setup
;
2618 data
.user_setup_data
= user_setup_data
;
2622 envp
= g_environ_setenv (envp
,
2623 "GIO_LAUNCHED_DESKTOP_FILE",
2626 envp
= g_environ_setenv (envp
,
2627 "GIO_LAUNCHED_DESKTOP_FILE_PID",
2628 "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
2630 data
.pid_envvar
= (char *)g_environ_getenv (envp
, "GIO_LAUNCHED_DESKTOP_FILE_PID");
2634 data
.pid_envvar
= NULL
;
2641 GList
*launched_files
= create_files_for_uris (launched_uris
);
2643 display
= g_app_launch_context_get_display (launch_context
,
2647 envp
= g_environ_setenv (envp
, "DISPLAY", display
, TRUE
);
2649 if (info
->startup_notify
)
2651 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
,
2655 envp
= g_environ_setenv (envp
, "DESKTOP_STARTUP_ID", sn_id
, TRUE
);
2658 g_list_free_full (launched_files
, g_object_unref
);
2661 if (!g_spawn_async (info
->path
,
2671 g_app_launch_context_launch_failed (launch_context
, sn_id
);
2675 g_list_free (launched_uris
);
2680 if (pid_callback
!= NULL
)
2681 pid_callback (info
, pid
, pid_callback_data
);
2683 if (launch_context
!= NULL
)
2685 GVariantBuilder builder
;
2686 GVariant
*platform_data
;
2688 g_variant_builder_init (&builder
, G_VARIANT_TYPE_ARRAY
);
2689 g_variant_builder_add (&builder
, "{sv}", "pid", g_variant_new_int32 (pid
));
2691 g_variant_builder_add (&builder
, "{sv}", "startup-notification-id", g_variant_new_string (sn_id
));
2692 platform_data
= g_variant_ref_sink (g_variant_builder_end (&builder
));
2693 g_signal_emit_by_name (launch_context
, "launched", info
, platform_data
);
2694 g_variant_unref (platform_data
);
2697 notify_desktop_launch (session_bus
,
2706 g_list_free (launched_uris
);
2711 while (uris
!= NULL
);
2723 object_path_from_appid (const gchar
*app_id
)
2728 n
= strlen (app_id
);
2729 path
= g_malloc (n
+ 2);
2733 for (i
= 0; i
< n
; i
++)
2734 if (app_id
[i
] != '.')
2735 path
[i
+ 1] = app_id
[i
];
2745 g_desktop_app_info_make_platform_data (GDesktopAppInfo
*info
,
2747 GAppLaunchContext
*launch_context
)
2749 GVariantBuilder builder
;
2751 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
2755 GList
*launched_files
= create_files_for_uris (uris
);
2757 if (info
->startup_notify
)
2761 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
, G_APP_INFO (info
), launched_files
);
2763 g_variant_builder_add (&builder
, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id
));
2766 g_list_free_full (launched_files
, g_object_unref
);
2769 return g_variant_builder_end (&builder
);
2773 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo
*info
,
2774 GDBusConnection
*session_bus
,
2776 GAppLaunchContext
*launch_context
)
2778 GVariantBuilder builder
;
2781 g_return_val_if_fail (info
!= NULL
, FALSE
);
2783 g_variant_builder_init (&builder
, G_VARIANT_TYPE_TUPLE
);
2789 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
2790 for (iter
= uris
; iter
; iter
= iter
->next
)
2791 g_variant_builder_add (&builder
, "s", iter
->data
);
2792 g_variant_builder_close (&builder
);
2795 g_variant_builder_add_value (&builder
, g_desktop_app_info_make_platform_data (info
, uris
, launch_context
));
2797 /* This is non-blocking API. Similar to launching via fork()/exec()
2798 * we don't wait around to see if the program crashed during startup.
2799 * This is what startup-notification's job is...
2801 object_path
= object_path_from_appid (info
->app_id
);
2802 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
, "org.freedesktop.Application",
2803 uris
? "Open" : "Activate", g_variant_builder_end (&builder
),
2804 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
2805 g_free (object_path
);
2811 g_desktop_app_info_launch_uris_internal (GAppInfo
*appinfo
,
2813 GAppLaunchContext
*launch_context
,
2814 GSpawnFlags spawn_flags
,
2815 GSpawnChildSetupFunc user_setup
,
2816 gpointer user_setup_data
,
2817 GDesktopAppLaunchCallback pid_callback
,
2818 gpointer pid_callback_data
,
2821 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2822 GDBusConnection
*session_bus
;
2823 gboolean success
= TRUE
;
2825 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
2827 if (session_bus
&& info
->app_id
)
2828 g_desktop_app_info_launch_uris_with_dbus (info
, session_bus
, uris
, launch_context
);
2830 success
= g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, info
->exec
, uris
, launch_context
,
2831 spawn_flags
, user_setup
, user_setup_data
,
2832 pid_callback
, pid_callback_data
, error
);
2834 if (session_bus
!= NULL
)
2836 /* This asynchronous flush holds a reference until it completes,
2837 * which ensures that the following unref won't immediately kill
2838 * the connection if we were the initial owner.
2840 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
2841 g_object_unref (session_bus
);
2848 g_desktop_app_info_launch_uris (GAppInfo
*appinfo
,
2850 GAppLaunchContext
*launch_context
,
2853 return g_desktop_app_info_launch_uris_internal (appinfo
, uris
,
2855 _SPAWN_FLAGS_DEFAULT
,
2856 NULL
, NULL
, NULL
, NULL
,
2861 g_desktop_app_info_supports_uris (GAppInfo
*appinfo
)
2863 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2865 return info
->exec
&&
2866 ((strstr (info
->exec
, "%u") != NULL
) ||
2867 (strstr (info
->exec
, "%U") != NULL
));
2871 g_desktop_app_info_supports_files (GAppInfo
*appinfo
)
2873 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2875 return info
->exec
&&
2876 ((strstr (info
->exec
, "%f") != NULL
) ||
2877 (strstr (info
->exec
, "%F") != NULL
));
2881 g_desktop_app_info_launch (GAppInfo
*appinfo
,
2883 GAppLaunchContext
*launch_context
,
2893 uri
= g_file_get_uri (files
->data
);
2894 uris
= g_list_prepend (uris
, uri
);
2895 files
= files
->next
;
2898 uris
= g_list_reverse (uris
);
2900 res
= g_desktop_app_info_launch_uris (appinfo
, uris
, launch_context
, error
);
2902 g_list_free_full (uris
, g_free
);
2908 * g_desktop_app_info_launch_uris_as_manager:
2909 * @appinfo: a #GDesktopAppInfo
2910 * @uris: (element-type utf8): List of URIs
2911 * @launch_context: (allow-none): a #GAppLaunchContext
2912 * @spawn_flags: #GSpawnFlags, used for each process
2913 * @user_setup: (scope call) (allow-none): a #GSpawnChildSetupFunc, used once
2915 * @user_setup_data: (closure user_setup) (allow-none): User data for @user_setup
2916 * @pid_callback: (scope call) (allow-none): Callback for child processes
2917 * @pid_callback_data: (closure pid_callback) (allow-none): User data for @callback
2918 * @error: return location for a #GError, or %NULL
2920 * This function performs the equivalent of g_app_info_launch_uris(),
2921 * but is intended primarily for operating system components that
2922 * launch applications. Ordinary applications should use
2923 * g_app_info_launch_uris().
2925 * If the application is launched via traditional UNIX fork()/exec()
2926 * then @spawn_flags, @user_setup and @user_setup_data are used for the
2927 * call to g_spawn_async(). Additionally, @pid_callback (with
2928 * @pid_callback_data) will be called to inform about the PID of the
2931 * If application launching occurs via some other mechanism (eg: D-Bus
2932 * activation) then @spawn_flags, @user_setup, @user_setup_data,
2933 * @pid_callback and @pid_callback_data are ignored.
2935 * Returns: %TRUE on successful launch, %FALSE otherwise.
2938 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
*appinfo
,
2940 GAppLaunchContext
*launch_context
,
2941 GSpawnFlags spawn_flags
,
2942 GSpawnChildSetupFunc user_setup
,
2943 gpointer user_setup_data
,
2944 GDesktopAppLaunchCallback pid_callback
,
2945 gpointer pid_callback_data
,
2948 return g_desktop_app_info_launch_uris_internal ((GAppInfo
*)appinfo
,
2959 /* OnlyShowIn API support {{{2 */
2962 * g_desktop_app_info_set_desktop_env:
2963 * @desktop_env: a string specifying what desktop this is
2965 * Sets the name of the desktop that the application is running in.
2966 * This is used by g_app_info_should_show() and
2967 * g_desktop_app_info_get_show_in() to evaluate the
2968 * `OnlyShowIn` and `NotShowIn`
2969 * desktop entry fields.
2971 * Should be called only once; subsequent calls are ignored.
2973 * Deprecated:2.42:do not use this API. Since 2.42 the value of the
2974 * `XDG_CURRENT_DESKTOP` environment variable will be used.
2977 g_desktop_app_info_set_desktop_env (const gchar
*desktop_env
)
2979 get_current_desktops (desktop_env
);
2983 g_desktop_app_info_should_show (GAppInfo
*appinfo
)
2985 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2987 if (info
->nodisplay
)
2990 return g_desktop_app_info_get_show_in (info
, NULL
);
2993 /* mime types/default apps support {{{2 */
3002 ensure_dir (DirType type
,
3005 char *path
, *display_name
;
3011 path
= g_build_filename (g_get_user_config_dir (), NULL
);
3015 path
= g_build_filename (g_get_user_data_dir (), "applications", NULL
);
3019 path
= g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL
);
3023 g_assert_not_reached ();
3027 if (g_mkdir_with_parents (path
, 0700) == 0)
3031 display_name
= g_filename_display_name (path
);
3032 if (type
== APP_DIR
)
3033 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3034 _("Can't create user application configuration folder %s: %s"),
3035 display_name
, g_strerror (errsv
));
3037 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3038 _("Can't create user MIME configuration folder %s: %s"),
3039 display_name
, g_strerror (errsv
));
3041 g_free (display_name
);
3048 update_mimeapps_list (const char *desktop_id
,
3049 const char *content_type
,
3050 UpdateMimeFlags flags
,
3053 char *dirname
, *filename
, *string
;
3055 gboolean load_succeeded
, res
;
3056 char **old_list
, **list
;
3057 gsize length
, data_size
;
3060 char **content_types
;
3062 /* Don't add both at start and end */
3063 g_assert (!((flags
& UPDATE_MIME_SET_DEFAULT
) &&
3064 (flags
& UPDATE_MIME_SET_NON_DEFAULT
)));
3066 dirname
= ensure_dir (CONF_DIR
, error
);
3070 filename
= g_build_filename (dirname
, "mimeapps.list", NULL
);
3073 key_file
= g_key_file_new ();
3074 load_succeeded
= g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
);
3075 if (!load_succeeded
||
3076 (!g_key_file_has_group (key_file
, ADDED_ASSOCIATIONS_GROUP
) &&
3077 !g_key_file_has_group (key_file
, REMOVED_ASSOCIATIONS_GROUP
) &&
3078 !g_key_file_has_group (key_file
, DEFAULT_APPLICATIONS_GROUP
)))
3080 g_key_file_free (key_file
);
3081 key_file
= g_key_file_new ();
3086 content_types
= g_new (char *, 2);
3087 content_types
[0] = g_strdup (content_type
);
3088 content_types
[1] = NULL
;
3092 content_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
3095 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3097 /* set as default, if requested so */
3098 string
= g_key_file_get_string (key_file
,
3099 DEFAULT_APPLICATIONS_GROUP
,
3103 if (g_strcmp0 (string
, desktop_id
) != 0 &&
3104 (flags
& UPDATE_MIME_SET_DEFAULT
))
3107 string
= g_strdup (desktop_id
);
3109 /* add in the non-default list too, if it's not already there */
3110 flags
|= UPDATE_MIME_SET_NON_DEFAULT
;
3113 if (string
== NULL
|| desktop_id
== NULL
)
3114 g_key_file_remove_key (key_file
,
3115 DEFAULT_APPLICATIONS_GROUP
,
3119 g_key_file_set_string (key_file
,
3120 DEFAULT_APPLICATIONS_GROUP
,
3129 /* reuse the list from above */
3133 g_strfreev (content_types
);
3134 content_types
= g_key_file_get_keys (key_file
, ADDED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3137 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3139 /* Add to the right place in the list */
3142 old_list
= g_key_file_get_string_list (key_file
, ADDED_ASSOCIATIONS_GROUP
,
3143 content_types
[k
], &length
, NULL
);
3145 list
= g_new (char *, 1 + length
+ 1);
3149 /* if we're adding a last-used hint, just put the application in front of the list */
3150 if (flags
& UPDATE_MIME_SET_LAST_USED
)
3152 /* avoid adding this again as non-default later */
3153 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3154 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3156 list
[i
++] = g_strdup (desktop_id
);
3161 for (j
= 0; old_list
[j
] != NULL
; j
++)
3163 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3165 /* rewrite other entries if they're different from the new one */
3166 list
[i
++] = g_strdup (old_list
[j
]);
3168 else if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3170 /* we encountered an old entry which is equal to the one we're adding as non-default,
3171 * don't change its position in the list.
3173 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3174 list
[i
++] = g_strdup (old_list
[j
]);
3179 /* add it at the end of the list */
3180 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3181 list
[i
++] = g_strdup (desktop_id
);
3185 g_strfreev (old_list
);
3187 if (list
[0] == NULL
|| desktop_id
== NULL
)
3188 g_key_file_remove_key (key_file
,
3189 ADDED_ASSOCIATIONS_GROUP
,
3193 g_key_file_set_string_list (key_file
,
3194 ADDED_ASSOCIATIONS_GROUP
,
3196 (const char * const *)list
, i
);
3203 /* reuse the list from above */
3207 g_strfreev (content_types
);
3208 content_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3211 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3213 /* Remove from removed associations group (unless remove) */
3216 old_list
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
,
3217 content_types
[k
], &length
, NULL
);
3219 list
= g_new (char *, 1 + length
+ 1);
3222 if (flags
& UPDATE_MIME_REMOVE
)
3223 list
[i
++] = g_strdup (desktop_id
);
3226 for (j
= 0; old_list
[j
] != NULL
; j
++)
3228 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3229 list
[i
++] = g_strdup (old_list
[j
]);
3234 g_strfreev (old_list
);
3236 if (list
[0] == NULL
|| desktop_id
== NULL
)
3237 g_key_file_remove_key (key_file
,
3238 REMOVED_ASSOCIATIONS_GROUP
,
3242 g_key_file_set_string_list (key_file
,
3243 REMOVED_ASSOCIATIONS_GROUP
,
3245 (const char * const *)list
, i
);
3250 g_strfreev (content_types
);
3252 data
= g_key_file_to_data (key_file
, &data_size
, error
);
3253 g_key_file_free (key_file
);
3255 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3257 desktop_file_dirs_invalidate_user_config ();
3266 g_desktop_app_info_set_as_last_used_for_type (GAppInfo
*appinfo
,
3267 const char *content_type
,
3270 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3272 if (!g_desktop_app_info_ensure_saved (info
, error
))
3275 if (!info
->desktop_id
)
3277 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3278 _("Application information lacks an identifier"));
3282 /* both add support for the content type and set as last used */
3283 return update_mimeapps_list (info
->desktop_id
, content_type
,
3284 UPDATE_MIME_SET_NON_DEFAULT
|
3285 UPDATE_MIME_SET_LAST_USED
,
3290 g_desktop_app_info_set_as_default_for_type (GAppInfo
*appinfo
,
3291 const char *content_type
,
3294 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3296 if (!g_desktop_app_info_ensure_saved (info
, error
))
3299 if (!info
->desktop_id
)
3301 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3302 _("Application information lacks an identifier"));
3306 return update_mimeapps_list (info
->desktop_id
, content_type
,
3307 UPDATE_MIME_SET_DEFAULT
,
3312 update_program_done (GPid pid
,
3316 /* Did the application exit correctly */
3317 if (g_spawn_check_exit_status (status
, NULL
))
3319 /* Here we could clean out any caches in use */
3324 run_update_command (char *command
,
3333 GError
*error
= NULL
;
3336 argv
[1] = g_build_filename (g_get_user_data_dir (), subdir
, NULL
);
3338 if (g_spawn_async ("/", argv
,
3340 G_SPAWN_SEARCH_PATH
|
3341 G_SPAWN_STDOUT_TO_DEV_NULL
|
3342 G_SPAWN_STDERR_TO_DEV_NULL
|
3343 G_SPAWN_DO_NOT_REAP_CHILD
,
3344 NULL
, NULL
, /* No setup function */
3347 g_child_watch_add (pid
, update_program_done
, NULL
);
3350 /* If we get an error at this point, it's quite likely the user doesn't
3351 * have an installed copy of either 'update-mime-database' or
3352 * 'update-desktop-database'. I don't think we want to popup an error
3353 * dialog at this point, so we just do a g_warning to give the user a
3354 * chance of debugging it.
3356 g_warning ("%s", error
->message
);
3363 g_desktop_app_info_set_as_default_for_extension (GAppInfo
*appinfo
,
3364 const char *extension
,
3367 char *filename
, *basename
, *mimetype
;
3371 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo
), error
))
3374 dirname
= ensure_dir (MIMETYPE_DIR
, error
);
3378 basename
= g_strdup_printf ("user-extension-%s.xml", extension
);
3379 filename
= g_build_filename (dirname
, basename
, NULL
);
3383 mimetype
= g_strdup_printf ("application/x-extension-%s", extension
);
3385 if (!g_file_test (filename
, G_FILE_TEST_EXISTS
))
3390 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3391 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3392 " <mime-type type=\"%s\">\n"
3393 " <comment>%s document</comment>\n"
3394 " <glob pattern=\"*.%s\"/>\n"
3396 "</mime-info>\n", mimetype
, extension
, extension
);
3398 g_file_set_contents (filename
, contents
, -1, NULL
);
3401 run_update_command ("update-mime-database", "mime");
3405 res
= g_desktop_app_info_set_as_default_for_type (appinfo
,
3415 g_desktop_app_info_add_supports_type (GAppInfo
*appinfo
,
3416 const char *content_type
,
3419 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3421 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3424 return update_mimeapps_list (info
->desktop_id
, content_type
,
3425 UPDATE_MIME_SET_NON_DEFAULT
,
3430 g_desktop_app_info_can_remove_supports_type (GAppInfo
*appinfo
)
3436 g_desktop_app_info_remove_supports_type (GAppInfo
*appinfo
,
3437 const char *content_type
,
3440 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3442 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3445 return update_mimeapps_list (info
->desktop_id
, content_type
,
3450 static const char **
3451 g_desktop_app_info_get_supported_types (GAppInfo
*appinfo
)
3453 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3455 return (const char**) info
->mime_types
;
3458 /* Saving and deleting {{{2 */
3461 g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
3467 char *data
, *desktop_id
;
3472 if (info
->filename
!= NULL
)
3475 /* This is only used for object created with
3476 * g_app_info_create_from_commandline. All other
3477 * object should have a filename
3480 dirname
= ensure_dir (APP_DIR
, error
);
3484 key_file
= g_key_file_new ();
3486 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3487 "Encoding", "UTF-8");
3488 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3489 G_KEY_FILE_DESKTOP_KEY_VERSION
, "1.0");
3490 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3491 G_KEY_FILE_DESKTOP_KEY_TYPE
,
3492 G_KEY_FILE_DESKTOP_TYPE_APPLICATION
);
3494 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3495 G_KEY_FILE_DESKTOP_KEY_TERMINAL
, TRUE
);
3496 if (info
->nodisplay
)
3497 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3498 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3500 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3501 G_KEY_FILE_DESKTOP_KEY_EXEC
, info
->exec
);
3503 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3504 G_KEY_FILE_DESKTOP_KEY_NAME
, info
->name
);
3506 if (info
->generic_name
!= NULL
)
3507 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3508 GENERIC_NAME_KEY
, info
->generic_name
);
3510 if (info
->fullname
!= NULL
)
3511 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3512 FULL_NAME_KEY
, info
->fullname
);
3514 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3515 G_KEY_FILE_DESKTOP_KEY_COMMENT
, info
->comment
);
3517 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3518 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3520 data
= g_key_file_to_data (key_file
, &data_size
, NULL
);
3521 g_key_file_free (key_file
);
3523 desktop_id
= g_strdup_printf ("userapp-%s-XXXXXX.desktop", info
->name
);
3524 filename
= g_build_filename (dirname
, desktop_id
, NULL
);
3525 g_free (desktop_id
);
3528 fd
= g_mkstemp (filename
);
3533 display_name
= g_filename_display_name (filename
);
3534 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3535 _("Can't create user desktop file %s"), display_name
);
3536 g_free (display_name
);
3542 desktop_id
= g_path_get_basename (filename
);
3544 /* FIXME - actually handle error */
3545 (void) g_close (fd
, NULL
);
3547 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3551 g_free (desktop_id
);
3556 info
->filename
= filename
;
3557 info
->desktop_id
= desktop_id
;
3559 run_update_command ("update-desktop-database", "applications");
3561 /* We just dropped a file in the user's desktop file directory. Save
3562 * the monitor the bother of having to notice it and invalidate
3565 * This means that calls directly following this will be able to see
3566 * the results immediately.
3568 desktop_file_dirs_invalidate_user_data ();
3574 g_desktop_app_info_can_delete (GAppInfo
*appinfo
)
3576 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3580 if (strstr (info
->filename
, "/userapp-"))
3581 return g_access (info
->filename
, W_OK
) == 0;
3588 g_desktop_app_info_delete (GAppInfo
*appinfo
)
3590 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3594 if (g_remove (info
->filename
) == 0)
3596 update_mimeapps_list (info
->desktop_id
, NULL
,
3600 g_free (info
->filename
);
3601 info
->filename
= NULL
;
3602 g_free (info
->desktop_id
);
3603 info
->desktop_id
= NULL
;
3612 /* Create for commandline {{{2 */
3614 * g_app_info_create_from_commandline:
3615 * @commandline: the commandline to use
3616 * @application_name: (allow-none): the application name, or %NULL to use @commandline
3617 * @flags: flags that can specify details of the created #GAppInfo
3618 * @error: a #GError location to store the error occurring, %NULL to ignore.
3620 * Creates a new #GAppInfo from the given information.
3622 * Note that for @commandline, the quoting rules of the Exec key of the
3623 * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3624 * are applied. For example, if the @commandline contains
3625 * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3626 * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
3628 * Returns: (transfer full): new #GAppInfo for given command.
3631 g_app_info_create_from_commandline (const char *commandline
,
3632 const char *application_name
,
3633 GAppInfoCreateFlags flags
,
3638 GDesktopAppInfo
*info
;
3640 g_return_val_if_fail (commandline
, NULL
);
3642 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
3644 info
->filename
= NULL
;
3645 info
->desktop_id
= NULL
;
3647 info
->terminal
= (flags
& G_APP_INFO_CREATE_NEEDS_TERMINAL
) != 0;
3648 info
->startup_notify
= (flags
& G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION
) != 0;
3649 info
->hidden
= FALSE
;
3650 if ((flags
& G_APP_INFO_CREATE_SUPPORTS_URIS
) != 0)
3651 info
->exec
= g_strconcat (commandline
, " %u", NULL
);
3653 info
->exec
= g_strconcat (commandline
, " %f", NULL
);
3654 info
->nodisplay
= TRUE
;
3655 info
->binary
= binary_from_exec (info
->exec
);
3657 if (application_name
)
3658 info
->name
= g_strdup (application_name
);
3661 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
3662 split
= g_strsplit (commandline
, " ", 2);
3663 basename
= split
[0] ? g_path_get_basename (split
[0]) : NULL
;
3665 info
->name
= basename
;
3666 if (info
->name
== NULL
)
3667 info
->name
= g_strdup ("custom");
3669 info
->comment
= g_strdup_printf (_("Custom definition for %s"), info
->name
);
3671 return G_APP_INFO (info
);
3674 /* GAppInfo interface init */
3677 g_desktop_app_info_iface_init (GAppInfoIface
*iface
)
3679 iface
->dup
= g_desktop_app_info_dup
;
3680 iface
->equal
= g_desktop_app_info_equal
;
3681 iface
->get_id
= g_desktop_app_info_get_id
;
3682 iface
->get_name
= g_desktop_app_info_get_name
;
3683 iface
->get_description
= g_desktop_app_info_get_description
;
3684 iface
->get_executable
= g_desktop_app_info_get_executable
;
3685 iface
->get_icon
= g_desktop_app_info_get_icon
;
3686 iface
->launch
= g_desktop_app_info_launch
;
3687 iface
->supports_uris
= g_desktop_app_info_supports_uris
;
3688 iface
->supports_files
= g_desktop_app_info_supports_files
;
3689 iface
->launch_uris
= g_desktop_app_info_launch_uris
;
3690 iface
->should_show
= g_desktop_app_info_should_show
;
3691 iface
->set_as_default_for_type
= g_desktop_app_info_set_as_default_for_type
;
3692 iface
->set_as_default_for_extension
= g_desktop_app_info_set_as_default_for_extension
;
3693 iface
->add_supports_type
= g_desktop_app_info_add_supports_type
;
3694 iface
->can_remove_supports_type
= g_desktop_app_info_can_remove_supports_type
;
3695 iface
->remove_supports_type
= g_desktop_app_info_remove_supports_type
;
3696 iface
->can_delete
= g_desktop_app_info_can_delete
;
3697 iface
->do_delete
= g_desktop_app_info_delete
;
3698 iface
->get_commandline
= g_desktop_app_info_get_commandline
;
3699 iface
->get_display_name
= g_desktop_app_info_get_display_name
;
3700 iface
->set_as_last_used_for_type
= g_desktop_app_info_set_as_last_used_for_type
;
3701 iface
->get_supported_types
= g_desktop_app_info_get_supported_types
;
3704 /* Recommended applications {{{2 */
3706 /* Converts content_type into a list of itself with all of its parent
3707 * types (if include_fallback is enabled) or just returns a single-item
3708 * list with the unaliased content type.
3711 get_list_of_mimetypes (const gchar
*content_type
,
3712 gboolean include_fallback
)
3717 array
= g_ptr_array_new ();
3718 unaliased
= _g_unix_content_type_unalias (content_type
);
3719 g_ptr_array_add (array
, unaliased
);
3721 if (include_fallback
)
3725 /* Iterate the array as we grow it, until we have nothing more to add */
3726 for (i
= 0; i
< array
->len
; i
++)
3728 gchar
**parents
= _g_unix_content_type_get_parents (g_ptr_array_index (array
, i
));
3731 for (j
= 0; parents
[j
]; j
++)
3732 /* Don't add duplicates */
3733 if (!array_contains (array
, parents
[j
]))
3734 g_ptr_array_add (array
, parents
[j
]);
3736 g_free (parents
[j
]);
3738 /* We already stole or freed each element. Free the container. */
3743 g_ptr_array_add (array
, NULL
);
3745 return (gchar
**) g_ptr_array_free (array
, FALSE
);
3749 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar
*content_type
,
3750 gboolean include_fallback
)
3752 GPtrArray
*hits
, *blacklist
;
3756 hits
= g_ptr_array_new ();
3757 blacklist
= g_ptr_array_new ();
3759 types
= get_list_of_mimetypes (content_type
, include_fallback
);
3761 desktop_file_dirs_lock ();
3763 for (i
= 0; types
[i
]; i
++)
3764 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
3765 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], hits
, blacklist
);
3767 desktop_file_dirs_unlock ();
3769 g_ptr_array_add (hits
, NULL
);
3771 g_ptr_array_free (blacklist
, TRUE
);
3774 return (gchar
**) g_ptr_array_free (hits
, FALSE
);
3778 g_desktop_app_info_get_defaults_for_content_type (const gchar
*content_type
)
3784 types
= get_list_of_mimetypes (content_type
, TRUE
);
3785 results
= g_ptr_array_new ();
3787 desktop_file_dirs_lock ();
3789 for (i
= 0; types
[i
]; i
++)
3790 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
3791 desktop_file_dir_default_lookup (&desktop_file_dirs
[j
], types
[i
], results
);
3793 desktop_file_dirs_unlock ();
3795 g_ptr_array_add (results
, NULL
);
3798 return (gchar
**) g_ptr_array_free (results
, FALSE
);
3802 * g_app_info_get_recommended_for_type:
3803 * @content_type: the content type to find a #GAppInfo for
3805 * Gets a list of recommended #GAppInfos for a given content type, i.e.
3806 * those applications which claim to support the given content type exactly,
3807 * and not by MIME type subclassing.
3808 * Note that the first application of the list is the last used one, i.e.
3809 * the last one for which g_app_info_set_as_last_used_for_type() has been
3812 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3813 * for given @content_type or %NULL on error.
3818 g_app_info_get_recommended_for_type (const gchar
*content_type
)
3820 gchar
**desktop_ids
;
3824 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3826 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3829 for (i
= 0; desktop_ids
[i
]; i
++)
3831 GDesktopAppInfo
*info
;
3833 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3835 infos
= g_list_prepend (infos
, info
);
3838 g_strfreev (desktop_ids
);
3840 return g_list_reverse (infos
);
3844 * g_app_info_get_fallback_for_type:
3845 * @content_type: the content type to find a #GAppInfo for
3847 * Gets a list of fallback #GAppInfos for a given content type, i.e.
3848 * those applications which claim to support the given content type
3849 * by MIME type subclassing and not directly.
3851 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3852 * for given @content_type or %NULL on error.
3857 g_app_info_get_fallback_for_type (const gchar
*content_type
)
3859 gchar
**recommended_ids
;
3864 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3866 recommended_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3867 all_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3870 for (i
= 0; all_ids
[i
]; i
++)
3872 GDesktopAppInfo
*info
;
3875 /* Don't return the ones on the recommended list */
3876 for (j
= 0; recommended_ids
[j
]; j
++)
3877 if (g_str_equal (all_ids
[i
], recommended_ids
[j
]))
3880 if (recommended_ids
[j
])
3883 info
= g_desktop_app_info_new (all_ids
[i
]);
3886 infos
= g_list_prepend (infos
, info
);
3889 g_strfreev (recommended_ids
);
3890 g_strfreev (all_ids
);
3892 return g_list_reverse (infos
);
3896 * g_app_info_get_all_for_type:
3897 * @content_type: the content type to find a #GAppInfo for
3899 * Gets a list of all #GAppInfos for a given content type,
3900 * including the recommended and fallback #GAppInfos. See
3901 * g_app_info_get_recommended_for_type() and
3902 * g_app_info_get_fallback_for_type().
3904 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3905 * for given @content_type or %NULL on error.
3908 g_app_info_get_all_for_type (const char *content_type
)
3910 gchar
**desktop_ids
;
3914 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3916 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3919 for (i
= 0; desktop_ids
[i
]; i
++)
3921 GDesktopAppInfo
*info
;
3923 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3925 infos
= g_list_prepend (infos
, info
);
3928 g_strfreev (desktop_ids
);
3930 return g_list_reverse (infos
);
3934 * g_app_info_reset_type_associations:
3935 * @content_type: a content type
3937 * Removes all changes to the type associations done by
3938 * g_app_info_set_as_default_for_type(),
3939 * g_app_info_set_as_default_for_extension(),
3940 * g_app_info_add_supports_type() or
3941 * g_app_info_remove_supports_type().
3946 g_app_info_reset_type_associations (const char *content_type
)
3948 update_mimeapps_list (NULL
, content_type
,
3954 * g_app_info_get_default_for_type:
3955 * @content_type: the content type to find a #GAppInfo for
3956 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
3959 * Gets the default #GAppInfo for a given content type.
3961 * Returns: (transfer full): #GAppInfo for given @content_type or
3965 g_app_info_get_default_for_type (const char *content_type
,
3966 gboolean must_support_uris
)
3968 gchar
**desktop_ids
;
3972 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3974 desktop_ids
= g_desktop_app_info_get_defaults_for_content_type (content_type
);
3977 for (i
= 0; desktop_ids
[i
]; i
++)
3979 info
= (GAppInfo
*) g_desktop_app_info_new (desktop_ids
[i
]);
3983 if (!must_support_uris
|| g_app_info_supports_uris (info
))
3986 g_object_unref (info
);
3991 g_strfreev (desktop_ids
);
3993 /* If we can't find a default app for this content type, pick one from
3994 * the list of all supported apps. This will be ordered by the user's
3995 * preference and by "recommended" apps first, so the first one we
3996 * find is probably the best fallback.
4000 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
4002 for (i
= 0; desktop_ids
[i
]; i
++)
4004 info
= (GAppInfo
*) g_desktop_app_info_new (desktop_ids
[i
]);
4008 if (!must_support_uris
|| g_app_info_supports_uris (info
))
4011 g_object_unref (info
);
4016 g_strfreev (desktop_ids
);
4023 * g_app_info_get_default_for_uri_scheme:
4024 * @uri_scheme: a string containing a URI scheme.
4026 * Gets the default application for handling URIs with
4027 * the given URI scheme. A URI scheme is the initial part
4028 * of the URI, up to but not including the ':', e.g. "http",
4031 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4034 g_app_info_get_default_for_uri_scheme (const char *uri_scheme
)
4037 char *content_type
, *scheme_down
;
4039 scheme_down
= g_ascii_strdown (uri_scheme
, -1);
4040 content_type
= g_strdup_printf ("x-scheme-handler/%s", scheme_down
);
4041 g_free (scheme_down
);
4042 app_info
= g_app_info_get_default_for_type (content_type
, FALSE
);
4043 g_free (content_type
);
4048 /* "Get all" API {{{2 */
4051 * g_desktop_app_info_get_implementations:
4052 * @interface: the name of the interface
4054 * Gets all applications that implement @interface.
4056 * An application implements an interface if that interface is listed in
4057 * the Implements= line of the desktop file of the application.
4062 g_desktop_app_info_get_implementations (const gchar
*interface
)
4064 GList
*result
= NULL
;
4068 desktop_file_dirs_lock ();
4070 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4071 desktop_file_dir_get_implementations (&desktop_file_dirs
[i
], &result
, interface
);
4073 desktop_file_dirs_unlock ();
4078 gchar
*name
= (*ptr
)->data
;
4079 GDesktopAppInfo
*app
;
4081 app
= g_desktop_app_info_new (name
);
4087 ptr
= &(*ptr
)->next
;
4090 *ptr
= g_list_delete_link (*ptr
, *ptr
);
4097 * g_desktop_app_info_search:
4098 * @search_string: the search string to use
4100 * Searches desktop files for ones that match @search_string.
4102 * The return value is an array of strvs. Each strv contains a list of
4103 * applications that matched @search_string with an equal score. The
4104 * outer list is sorted by score so that the first strv contains the
4105 * best-matching applications, and so on.
4106 * The algorithm for determining matches is undefined and may change at
4109 * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4110 * list of strvs. Free each item with g_strfreev() and free the outer
4111 * list with g_free().
4114 g_desktop_app_info_search (const gchar
*search_string
)
4116 gchar
**search_tokens
;
4117 gint last_category
= -1;
4119 gint n_categories
= 0;
4120 gint start_of_category
;
4123 search_tokens
= g_str_tokenize_and_fold (search_string
, NULL
, NULL
);
4125 desktop_file_dirs_lock ();
4127 reset_total_search_results ();
4129 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4131 for (j
= 0; search_tokens
[j
]; j
++)
4133 desktop_file_dir_search (&desktop_file_dirs
[i
], search_tokens
[j
]);
4134 merge_token_results (j
== 0);
4136 merge_directory_results ();
4139 sort_total_search_results ();
4141 /* Count the total number of unique categories */
4142 for (i
= 0; i
< static_total_results_size
; i
++)
4143 if (static_total_results
[i
].category
!= last_category
)
4145 last_category
= static_total_results
[i
].category
;
4149 results
= g_new (gchar
**, n_categories
+ 1);
4151 /* Start loading into the results list */
4152 start_of_category
= 0;
4153 for (i
= 0; i
< n_categories
; i
++)
4155 gint n_items_in_category
= 0;
4159 this_category
= static_total_results
[start_of_category
].category
;
4161 while (start_of_category
+ n_items_in_category
< static_total_results_size
&&
4162 static_total_results
[start_of_category
+ n_items_in_category
].category
== this_category
)
4163 n_items_in_category
++;
4165 results
[i
] = g_new (gchar
*, n_items_in_category
+ 1);
4166 for (j
= 0; j
< n_items_in_category
; j
++)
4167 results
[i
][j
] = g_strdup (static_total_results
[start_of_category
+ j
].app_name
);
4168 results
[i
][j
] = NULL
;
4170 start_of_category
+= n_items_in_category
;
4174 desktop_file_dirs_unlock ();
4176 g_strfreev (search_tokens
);
4182 * g_app_info_get_all:
4184 * Gets a list of all of the applications currently registered
4187 * For desktop files, this includes applications that have
4188 * `NoDisplay=true` set or are excluded from display by means
4189 * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4190 * The returned list does not include applications which have
4191 * the `Hidden` key set.
4193 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4196 g_app_info_get_all (void)
4199 GHashTableIter iter
;
4204 apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
4206 desktop_file_dirs_lock ();
4208 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4209 desktop_file_dir_get_all (&desktop_file_dirs
[i
], apps
);
4211 desktop_file_dirs_unlock ();
4214 g_hash_table_iter_init (&iter
, apps
);
4215 while (g_hash_table_iter_next (&iter
, NULL
, &value
))
4218 infos
= g_list_prepend (infos
, value
);
4221 g_hash_table_destroy (apps
);
4226 /* GDesktopAppInfoLookup interface {{{2 */
4228 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4230 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface
;
4231 G_DEFINE_INTERFACE (GDesktopAppInfoLookup
, g_desktop_app_info_lookup
, G_TYPE_OBJECT
)
4234 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface
*iface
)
4238 /* "Get for mime type" APIs {{{2 */
4241 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4242 * @lookup: a #GDesktopAppInfoLookup
4243 * @uri_scheme: a string containing a URI scheme.
4245 * Gets the default application for launching applications
4246 * using this URI scheme for a particular GDesktopAppInfoLookup
4249 * The GDesktopAppInfoLookup interface and this function is used
4250 * to implement g_app_info_get_default_for_uri_scheme() backends
4251 * in a GIO module. There is no reason for applications to use it
4252 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4254 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4256 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
4259 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup
*lookup
,
4260 const char *uri_scheme
)
4262 GDesktopAppInfoLookupIface
*iface
;
4264 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup
), NULL
);
4266 iface
= G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup
);
4268 return (* iface
->get_default_for_uri_scheme
) (lookup
, uri_scheme
);
4271 G_GNUC_END_IGNORE_DEPRECATIONS
4273 /* Misc getter APIs {{{2 */
4276 * g_desktop_app_info_get_startup_wm_class:
4277 * @info: a #GDesktopAppInfo that supports startup notify
4279 * Retrieves the StartupWMClass field from @info. This represents the
4280 * WM_CLASS property of the main window of the application, if launched
4283 * Returns: (transfer none): the startup WM class, or %NULL if none is set
4284 * in the desktop file.
4289 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo
*info
)
4291 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4293 return info
->startup_wm_class
;
4297 * g_desktop_app_info_get_string:
4298 * @info: a #GDesktopAppInfo
4299 * @key: the key to look up
4301 * Looks up a string value in the keyfile backing @info.
4303 * The @key is looked up in the "Desktop Entry" group.
4305 * Returns: a newly allocated string, or %NULL if the key
4311 g_desktop_app_info_get_string (GDesktopAppInfo
*info
,
4314 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4316 return g_key_file_get_string (info
->keyfile
,
4317 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4321 * g_desktop_app_info_get_boolean:
4322 * @info: a #GDesktopAppInfo
4323 * @key: the key to look up
4325 * Looks up a boolean value in the keyfile backing @info.
4327 * The @key is looked up in the "Desktop Entry" group.
4329 * Returns: the boolean value, or %FALSE if the key
4335 g_desktop_app_info_get_boolean (GDesktopAppInfo
*info
,
4338 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4340 return g_key_file_get_boolean (info
->keyfile
,
4341 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4345 * g_desktop_app_info_has_key:
4346 * @info: a #GDesktopAppInfo
4347 * @key: the key to look up
4349 * Returns whether @key exists in the "Desktop Entry" group
4350 * of the keyfile backing @info.
4352 * Returns: %TRUE if the @key exists
4357 g_desktop_app_info_has_key (GDesktopAppInfo
*info
,
4360 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4362 return g_key_file_has_key (info
->keyfile
,
4363 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4366 /* Desktop actions support {{{2 */
4369 * g_desktop_app_info_list_actions:
4370 * @info: a #GDesktopAppInfo
4372 * Returns the list of "additional application actions" supported on the
4373 * desktop file, as per the desktop file specification.
4375 * As per the specification, this is the list of actions that are
4376 * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4378 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4382 const gchar
* const *
4383 g_desktop_app_info_list_actions (GDesktopAppInfo
*info
)
4385 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4387 return (const gchar
**) info
->actions
;
4391 app_info_has_action (GDesktopAppInfo
*info
,
4392 const gchar
*action_name
)
4396 for (i
= 0; info
->actions
[i
]; i
++)
4397 if (g_str_equal (info
->actions
[i
], action_name
))
4404 * g_desktop_app_info_get_action_name:
4405 * @info: a #GDesktopAppInfo
4406 * @action_name: the name of the action as from
4407 * g_desktop_app_info_list_actions()
4409 * Gets the user-visible display name of the "additional application
4410 * action" specified by @action_name.
4412 * This corresponds to the "Name" key within the keyfile group for the
4415 * Returns: (transfer full): the locale-specific action name
4420 g_desktop_app_info_get_action_name (GDesktopAppInfo
*info
,
4421 const gchar
*action_name
)
4426 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4427 g_return_val_if_fail (action_name
!= NULL
, NULL
);
4428 g_return_val_if_fail (app_info_has_action (info
, action_name
), NULL
);
4430 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4431 result
= g_key_file_get_locale_string (info
->keyfile
, group_name
, "Name", NULL
, NULL
);
4432 g_free (group_name
);
4434 /* The spec says that the Name field must be given.
4436 * If it's not, let's follow the behaviour of our get_name()
4437 * implementation above and never return %NULL.
4440 result
= g_strdup (_("Unnamed"));
4446 * g_desktop_app_info_launch_action:
4447 * @info: a #GDesktopAppInfo
4448 * @action_name: the name of the action as from
4449 * g_desktop_app_info_list_actions()
4450 * @launch_context: (allow-none): a #GAppLaunchContext
4452 * Activates the named application action.
4454 * You may only call this function on action names that were
4455 * returned from g_desktop_app_info_list_actions().
4457 * Note that if the main entry of the desktop file indicates that the
4458 * application supports startup notification, and @launch_context is
4459 * non-%NULL, then startup notification will be used when activating the
4460 * action (and as such, invocation of the action on the receiving side
4461 * must signal the end of startup notification when it is completed).
4462 * This is the expected behaviour of applications declaring additional
4463 * actions, as per the desktop file specification.
4465 * As with g_app_info_launch() there is no way to detect failures that
4466 * occur while using this function.
4471 g_desktop_app_info_launch_action (GDesktopAppInfo
*info
,
4472 const gchar
*action_name
,
4473 GAppLaunchContext
*launch_context
)
4475 GDBusConnection
*session_bus
;
4477 g_return_if_fail (G_IS_DESKTOP_APP_INFO (info
));
4478 g_return_if_fail (action_name
!= NULL
);
4479 g_return_if_fail (app_info_has_action (info
, action_name
));
4481 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
4483 if (session_bus
&& info
->app_id
)
4487 object_path
= object_path_from_appid (info
->app_id
);
4488 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
,
4489 "org.freedesktop.Application", "ActivateAction",
4490 g_variant_new ("(sav@a{sv})", action_name
, NULL
,
4491 g_desktop_app_info_make_platform_data (info
, NULL
, launch_context
)),
4492 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
4493 g_free (object_path
);
4500 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4501 exec_line
= g_key_file_get_string (info
->keyfile
, group_name
, "Exec", NULL
);
4502 g_free (group_name
);
4505 g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, exec_line
, NULL
, launch_context
,
4506 _SPAWN_FLAGS_DEFAULT
, NULL
, NULL
, NULL
, NULL
, NULL
);
4509 if (session_bus
!= NULL
)
4511 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
4512 g_object_unref (session_bus
);
4517 /* vim:set foldmethod=marker: */