1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Ryan Lortie <desrt@desrt.ca>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
35 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
38 #include "glib-unix.h"
42 #include "gthemedicon.h"
43 #include "gfileicon.h"
44 #include <glib/gstdio.h>
46 #include "giomodule-priv.h"
48 #include "gappinfoprivate.h"
49 #include "glocalfilemonitor.h"
52 #include "gdocumentportal.h"
56 * SECTION:gdesktopappinfo
57 * @title: GDesktopAppInfo
58 * @short_description: Application information from desktop files
59 * @include: gio/gdesktopappinfo.h
61 * #GDesktopAppInfo is an implementation of #GAppInfo based on
64 * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
65 * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
69 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
70 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
71 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
72 #define MIME_CACHE_GROUP "MIME Cache"
73 #define GENERIC_NAME_KEY "GenericName"
74 #define FULL_NAME_KEY "X-GNOME-FullName"
75 #define KEYWORDS_KEY "Keywords"
76 #define STARTUP_WM_CLASS_KEY "StartupWMClass"
83 static void g_desktop_app_info_iface_init (GAppInfoIface
*iface
);
84 static gboolean
g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
90 * Information about an installed application from a desktop file.
92 struct _GDesktopAppInfo
94 GObject parent_instance
;
116 char *startup_wm_class
;
123 guint startup_notify
: 1;
128 UPDATE_MIME_NONE
= 1 << 0,
129 UPDATE_MIME_SET_DEFAULT
= 1 << 1,
130 UPDATE_MIME_SET_NON_DEFAULT
= 1 << 2,
131 UPDATE_MIME_REMOVE
= 1 << 3,
132 UPDATE_MIME_SET_LAST_USED
= 1 << 4,
135 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo
, g_desktop_app_info
, G_TYPE_OBJECT
,
136 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO
, g_desktop_app_info_iface_init
))
138 /* DesktopFileDir implementation {{{1 */
143 gchar
*alternatively_watching
;
146 GFileMonitor
*monitor
;
147 GHashTable
*app_names
;
148 GHashTable
*mime_tweaks
;
149 GHashTable
*memory_index
;
150 GHashTable
*memory_implementations
;
153 static DesktopFileDir
*desktop_file_dirs
;
154 static guint n_desktop_file_dirs
;
155 static const guint desktop_file_dir_user_config_index
= 0;
156 static guint desktop_file_dir_user_data_index
;
157 static GMutex desktop_file_dir_lock
;
159 /* Monitor 'changed' signal handler {{{2 */
160 static void desktop_file_dir_reset (DesktopFileDir
*dir
);
163 * desktop_file_dir_get_alternative_dir:
164 * @dir: a #DesktopFileDir
166 * Gets the "alternative" directory to monitor in case the path
169 * If the path exists this will return NULL, otherwise it will return a
170 * parent directory of the path.
172 * This is used to avoid inotify on a non-existent directory (which
173 * results in polling).
175 * See https://bugzilla.gnome.org/show_bug.cgi?id=522314 for more info.
178 desktop_file_dir_get_alternative_dir (DesktopFileDir
*dir
)
182 /* If the directory itself exists then we need no alternative. */
183 if (g_access (dir
->path
, R_OK
| X_OK
) == 0)
186 /* Otherwise, try the parent directories until we find one. */
187 parent
= g_path_get_dirname (dir
->path
);
189 while (g_access (parent
, R_OK
| X_OK
) != 0)
193 parent
= g_path_get_dirname (tmp
);
195 /* If somehow we get to '/' or '.' then just stop... */
196 if (g_str_equal (parent
, tmp
))
209 desktop_file_dir_changed (GFileMonitor
*monitor
,
212 GFileMonitorEvent event_type
,
215 DesktopFileDir
*dir
= user_data
;
216 gboolean do_nothing
= FALSE
;
218 /* We are not interested in receiving notifications forever just
219 * because someone asked about one desktop file once.
221 * After we receive the first notification, reset the dir, destroying
222 * the monitor. We will take this as a hint, next time that we are
223 * asked, that we need to check if everything is up to date.
225 * If this is a notification for a parent directory (because the
226 * desktop directory didn't exist) then we shouldn't fire the signal
227 * unless something actually changed.
229 g_mutex_lock (&desktop_file_dir_lock
);
231 if (dir
->alternatively_watching
)
233 gchar
*alternative_dir
;
235 alternative_dir
= desktop_file_dir_get_alternative_dir (dir
);
236 do_nothing
= alternative_dir
&& g_str_equal (dir
->alternatively_watching
, alternative_dir
);
237 g_free (alternative_dir
);
241 desktop_file_dir_reset (dir
);
243 g_mutex_unlock (&desktop_file_dir_lock
);
245 /* Notify anyone else who may be interested */
247 g_app_info_monitor_fire ();
250 /* Internal utility functions {{{2 */
253 * desktop_file_dir_app_name_is_masked:
254 * @dir: a #DesktopFileDir
255 * @app_name: an application ID
257 * Checks if @app_name is masked for @dir.
259 * An application is masked if a similarly-named desktop file exists in
260 * a desktop file directory with higher precedence. Masked desktop
261 * files should be ignored.
264 desktop_file_dir_app_name_is_masked (DesktopFileDir
*dir
,
265 const gchar
*app_name
)
267 while (dir
> desktop_file_dirs
)
271 if (dir
->app_names
&& g_hash_table_contains (dir
->app_names
, app_name
))
278 static const gchar
* const *
279 get_lowercase_current_desktops (void)
281 static gchar
**result
;
283 if (g_once_init_enter (&result
))
288 envvar
= g_getenv ("XDG_CURRENT_DESKTOP");
294 tmp
= g_strsplit (envvar
, G_SEARCHPATH_SEPARATOR_S
, 0);
296 for (i
= 0; tmp
[i
]; i
++)
297 for (j
= 0; tmp
[i
][j
]; j
++)
298 tmp
[i
][j
] = g_ascii_tolower (tmp
[i
][j
]);
301 tmp
= g_new0 (gchar
*, 0 + 1);
303 g_once_init_leave (&result
, tmp
);
306 return (const gchar
**) result
;
309 static const gchar
* const *
310 get_current_desktops (const gchar
*value
)
312 static gchar
**result
;
314 if (g_once_init_enter (&result
))
319 value
= g_getenv ("XDG_CURRENT_DESKTOP");
324 tmp
= g_strsplit (value
, ":", 0);
326 g_once_init_leave (&result
, tmp
);
329 return (const gchar
**) result
;
333 * add_to_table_if_appropriate:
334 * @apps: a string to GDesktopAppInfo hash table
335 * @app_name: the name of the application
336 * @info: a #GDesktopAppInfo, or NULL
338 * If @info is non-%NULL and non-hidden, then add it to @apps, using
339 * @app_name as a key.
341 * If @info is non-%NULL then this function will consume the passed-in
345 add_to_table_if_appropriate (GHashTable
*apps
,
346 const gchar
*app_name
,
347 GDesktopAppInfo
*info
)
354 g_object_unref (info
);
358 g_free (info
->desktop_id
);
359 info
->desktop_id
= g_strdup (app_name
);
361 g_hash_table_insert (apps
, g_strdup (info
->desktop_id
), info
);
368 DESKTOP_KEY_GenericName
,
369 DESKTOP_KEY_Keywords
,
371 DESKTOP_KEY_X_GNOME_FullName
,
376 const gchar desktop_key_match_category
[N_DESKTOP_KEYS
] = {
377 /* Note: lower numbers are a better match.
379 * In case we want two keys to match at the same level, we can just
380 * use the same number for the two different keys.
382 [DESKTOP_KEY_Name
] = 1,
383 [DESKTOP_KEY_Exec
] = 2,
384 [DESKTOP_KEY_Keywords
] = 3,
385 [DESKTOP_KEY_GenericName
] = 4,
386 [DESKTOP_KEY_X_GNOME_FullName
] = 5,
387 [DESKTOP_KEY_Comment
] = 6
391 desktop_key_get_name (guint key_id
)
395 case DESKTOP_KEY_Comment
:
397 case DESKTOP_KEY_Exec
:
399 case DESKTOP_KEY_GenericName
:
400 return GENERIC_NAME_KEY
;
401 case DESKTOP_KEY_Keywords
:
403 case DESKTOP_KEY_Name
:
405 case DESKTOP_KEY_X_GNOME_FullName
:
406 return FULL_NAME_KEY
;
408 g_assert_not_reached ();
412 /* Search global state {{{2
414 * We only ever search under a global lock, so we can use (and reuse)
415 * some global data to reduce allocations made while searching.
417 * In short, we keep around arrays of results that we expand as needed
418 * (and never shrink).
420 * static_token_results: this is where we append the results for each
421 * token within a given desktop directory, as we handle it (which is
422 * a union of all matches for this term)
424 * static_search_results: this is where we build the complete results
425 * for a single directory (which is an intersection of the matches
426 * found for each term)
428 * static_total_results: this is where we build the complete results
429 * across all directories (which is a union of the matches found in
432 * The app_names that enter these tables are always pointer-unique (in
433 * the sense that string equality is the same as pointer equality).
434 * This can be guaranteed for two reasons:
436 * - we mask appids so that a given appid will only ever appear within
437 * the highest-precedence directory that contains it. We never
438 * return search results from a lower-level directory if a desktop
439 * file exists in a higher-level one.
441 * - within a given directory, the string is unique because it's the
442 * key in the hashtable of all app_ids for that directory.
444 * We perform a merging of the results in merge_token_results(). This
445 * works by ordering the two lists and moving through each of them (at
446 * the same time) looking for common elements, rejecting uncommon ones.
447 * "Order" here need not mean any particular thing, as long as it is
448 * some order. Because of the uniqueness of our strings, we can use
449 * pointer order. That's what's going on in compare_results() below.
453 const gchar
*app_name
;
457 static struct search_result
*static_token_results
;
458 static gint static_token_results_size
;
459 static gint static_token_results_allocated
;
460 static struct search_result
*static_search_results
;
461 static gint static_search_results_size
;
462 static gint static_search_results_allocated
;
463 static struct search_result
*static_total_results
;
464 static gint static_total_results_size
;
465 static gint static_total_results_allocated
;
467 /* And some functions for performing nice operations against it */
469 compare_results (gconstpointer a
,
472 const struct search_result
*ra
= a
;
473 const struct search_result
*rb
= b
;
475 if (ra
->app_name
< rb
->app_name
)
478 else if (ra
->app_name
> rb
->app_name
)
482 return ra
->category
- rb
->category
;
486 compare_categories (gconstpointer a
,
489 const struct search_result
*ra
= a
;
490 const struct search_result
*rb
= b
;
492 return ra
->category
- rb
->category
;
496 add_token_result (const gchar
*app_name
,
499 if G_UNLIKELY (static_token_results_size
== static_token_results_allocated
)
501 static_token_results_allocated
= MAX (16, static_token_results_allocated
* 2);
502 static_token_results
= g_renew (struct search_result
, static_token_results
, static_token_results_allocated
);
505 static_token_results
[static_token_results_size
].app_name
= app_name
;
506 static_token_results
[static_token_results_size
].category
= category
;
507 static_token_results_size
++;
511 merge_token_results (gboolean first
)
513 if (static_token_results_size
!= 0)
514 qsort (static_token_results
, static_token_results_size
, sizeof (struct search_result
), compare_results
);
516 /* If this is the first token then we are basically merging a list with
517 * itself -- we only perform de-duplication.
519 * If this is not the first token then we are doing a real merge.
523 const gchar
*last_name
= NULL
;
526 /* We must de-duplicate, but we do so by taking the best category
529 * The final list can be as large as the input here, so make sure
530 * we have enough room (even if it's too much room).
533 if G_UNLIKELY (static_search_results_allocated
< static_token_results_size
)
535 static_search_results_allocated
= static_token_results_allocated
;
536 static_search_results
= g_renew (struct search_result
,
537 static_search_results
,
538 static_search_results_allocated
);
541 for (i
= 0; i
< static_token_results_size
; i
++)
543 /* The list is sorted so that the best match for a given id
544 * will be at the front, so once we have copied an id, skip
545 * the rest of the entries for the same id.
547 if (static_token_results
[i
].app_name
== last_name
)
550 last_name
= static_token_results
[i
].app_name
;
552 static_search_results
[static_search_results_size
++] = static_token_results
[i
];
557 const gchar
*last_name
= NULL
;
561 /* We only ever remove items from the results list, so no need to
562 * resize to ensure that we have enough room.
564 for (i
= 0; i
< static_token_results_size
; i
++)
566 if (static_token_results
[i
].app_name
== last_name
)
569 last_name
= static_token_results
[i
].app_name
;
571 /* Now we only want to have a result in static_search_results
572 * if we already have it there *and* we have it in
573 * static_token_results as well. The category will be the
576 * Skip past the results in static_search_results that are not
577 * going to be matches.
579 while (k
< static_search_results_size
&&
580 static_search_results
[k
].app_name
< static_token_results
[i
].app_name
)
583 if (k
< static_search_results_size
&&
584 static_search_results
[k
].app_name
== static_token_results
[i
].app_name
)
588 * Category should be the worse of the two (ie:
589 * numerically larger).
591 static_search_results
[j
].app_name
= static_search_results
[k
].app_name
;
592 static_search_results
[j
].category
= MAX (static_search_results
[k
].category
,
593 static_token_results
[i
].category
);
598 static_search_results_size
= j
;
601 /* Clear it out for next time... */
602 static_token_results_size
= 0;
606 reset_total_search_results (void)
608 static_total_results_size
= 0;
612 sort_total_search_results (void)
614 if (static_total_results_size
!= 0)
615 qsort (static_total_results
, static_total_results_size
, sizeof (struct search_result
), compare_categories
);
619 merge_directory_results (void)
621 if G_UNLIKELY (static_total_results_size
+ static_search_results_size
> static_total_results_allocated
)
623 static_total_results_allocated
= MAX (16, static_total_results_allocated
);
624 while (static_total_results_allocated
< static_total_results_size
+ static_search_results_size
)
625 static_total_results_allocated
*= 2;
626 static_total_results
= g_renew (struct search_result
, static_total_results
, static_total_results_allocated
);
629 if (static_total_results
+ static_total_results_size
!= 0)
630 memcpy (static_total_results
+ static_total_results_size
,
631 static_search_results
,
632 static_search_results_size
* sizeof (struct search_result
));
634 static_total_results_size
+= static_search_results_size
;
636 /* Clear it out for next time... */
637 static_search_results_size
= 0;
640 /* Support for unindexed DesktopFileDirs {{{2 */
642 get_apps_from_dir (GHashTable
**apps
,
646 const char *basename
;
649 dir
= g_dir_open (dirname
, 0, NULL
);
654 while ((basename
= g_dir_read_name (dir
)) != NULL
)
658 filename
= g_build_filename (dirname
, basename
, NULL
);
660 if (g_str_has_suffix (basename
, ".desktop"))
664 app_name
= g_strconcat (prefix
, basename
, NULL
);
667 *apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
669 g_hash_table_insert (*apps
, app_name
, g_strdup (filename
));
671 else if (g_file_test (filename
, G_FILE_TEST_IS_DIR
))
675 subprefix
= g_strconcat (prefix
, basename
, "-", NULL
);
676 get_apps_from_dir (apps
, filename
, subprefix
);
691 } UnindexedMimeTweaks
;
694 free_mime_tweaks (gpointer data
)
696 UnindexedMimeTweaks
*tweaks
= data
;
698 g_strfreev (tweaks
->additions
);
699 g_strfreev (tweaks
->removals
);
700 g_strfreev (tweaks
->defaults
);
702 g_slice_free (UnindexedMimeTweaks
, tweaks
);
705 static UnindexedMimeTweaks
*
706 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir
*dir
,
707 const gchar
*mime_type
)
709 UnindexedMimeTweaks
*tweaks
;
710 gchar
*unaliased_type
;
712 unaliased_type
= _g_unix_content_type_unalias (mime_type
);
713 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, unaliased_type
);
717 tweaks
= g_slice_new0 (UnindexedMimeTweaks
);
718 g_hash_table_insert (dir
->mime_tweaks
, unaliased_type
, tweaks
);
721 g_free (unaliased_type
);
726 /* consumes 'to_add' */
728 expand_strv (gchar
***strv_ptr
,
730 gchar
* const *blacklist
)
732 guint strv_len
, add_len
;
743 strv_len
= g_strv_length (strv
);
744 add_len
= g_strv_length (to_add
);
745 strv
= g_renew (gchar
*, strv
, strv_len
+ add_len
+ 1);
747 for (i
= 0; to_add
[i
]; i
++)
749 /* Don't add blacklisted strings */
751 for (j
= 0; blacklist
[j
]; j
++)
752 if (g_str_equal (to_add
[i
], blacklist
[j
]))
755 /* Don't add duplicates already in the list */
756 for (j
= 0; j
< strv_len
; j
++)
757 if (g_str_equal (to_add
[i
], strv
[j
]))
760 strv
[strv_len
++] = to_add
[i
];
767 strv
[strv_len
] = NULL
;
774 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir
*dir
,
775 const gchar
*filename
,
776 const gchar
*added_group
,
777 gboolean tweaks_permitted
)
779 UnindexedMimeTweaks
*tweaks
;
780 char **desktop_file_ids
;
785 key_file
= g_key_file_new ();
786 if (!g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
))
788 g_key_file_free (key_file
);
792 mime_types
= g_key_file_get_keys (key_file
, added_group
, NULL
, NULL
);
794 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
796 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
797 "mimeapps.list file may add or remove associations.", filename
, added_group
);
798 g_strfreev (mime_types
);
802 if (mime_types
!= NULL
)
804 for (i
= 0; mime_types
[i
] != NULL
; i
++)
806 desktop_file_ids
= g_key_file_get_string_list (key_file
, added_group
, mime_types
[i
], NULL
, NULL
);
808 if (desktop_file_ids
)
810 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
811 expand_strv (&tweaks
->additions
, desktop_file_ids
, tweaks
->removals
);
815 g_strfreev (mime_types
);
818 mime_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
820 if G_UNLIKELY (mime_types
!= NULL
&& !tweaks_permitted
)
822 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
823 "mimeapps.list file may add or remove associations.", filename
, REMOVED_ASSOCIATIONS_GROUP
);
824 g_strfreev (mime_types
);
828 if (mime_types
!= NULL
)
830 for (i
= 0; mime_types
[i
] != NULL
; i
++)
832 desktop_file_ids
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
834 if (desktop_file_ids
)
836 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
837 expand_strv (&tweaks
->removals
, desktop_file_ids
, tweaks
->additions
);
841 g_strfreev (mime_types
);
844 mime_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
846 if (mime_types
!= NULL
)
848 for (i
= 0; mime_types
[i
] != NULL
; i
++)
850 desktop_file_ids
= g_key_file_get_string_list (key_file
, DEFAULT_APPLICATIONS_GROUP
, mime_types
[i
], NULL
, NULL
);
852 if (desktop_file_ids
)
854 tweaks
= desktop_file_dir_unindexed_get_tweaks (dir
, mime_types
[i
]);
855 expand_strv (&tweaks
->defaults
, desktop_file_ids
, NULL
);
859 g_strfreev (mime_types
);
862 g_key_file_free (key_file
);
866 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir
*dir
)
868 const gchar
* const *desktops
;
872 dir
->mime_tweaks
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, free_mime_tweaks
);
874 /* We process in order of precedence, using a blacklisting approach to
875 * avoid recording later instructions that conflict with ones we found
878 * We first start with the XDG_CURRENT_DESKTOP files, in precedence
881 desktops
= get_lowercase_current_desktops ();
882 for (i
= 0; desktops
[i
]; i
++)
884 filename
= g_strdup_printf ("%s/%s-mimeapps.list", dir
->path
, desktops
[i
]);
885 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
889 /* Next, the non-desktop-specific mimeapps.list */
890 filename
= g_strdup_printf ("%s/mimeapps.list", dir
->path
);
891 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, TRUE
);
894 /* The remaining files are only checked for in directories that might
895 * contain desktop files (ie: not the config dirs).
900 /* We have 'defaults.list' which was only ever understood by GLib. It
901 * exists widely, but it has never been part of any spec and it should
902 * be treated as deprecated. This will be removed in a future
905 filename
= g_strdup_printf ("%s/defaults.list", dir
->path
);
906 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, ADDED_ASSOCIATIONS_GROUP
, FALSE
);
909 /* Finally, the mimeinfo.cache, which is just a cached copy of what we
910 * would find in the MimeTypes= lines of all of the desktop files.
912 filename
= g_strdup_printf ("%s/mimeinfo.cache", dir
->path
);
913 desktop_file_dir_unindexed_read_mimeapps_list (dir
, filename
, MIME_CACHE_GROUP
, TRUE
);
918 desktop_file_dir_unindexed_init (DesktopFileDir
*dir
)
921 get_apps_from_dir (&dir
->app_names
, dir
->path
, "");
923 desktop_file_dir_unindexed_read_mimeapps_lists (dir
);
926 static GDesktopAppInfo
*
927 desktop_file_dir_unindexed_get_app (DesktopFileDir
*dir
,
928 const gchar
*desktop_id
)
930 const gchar
*filename
;
932 filename
= g_hash_table_lookup (dir
->app_names
, desktop_id
);
937 return g_desktop_app_info_new_from_filename (filename
);
941 desktop_file_dir_unindexed_get_all (DesktopFileDir
*dir
,
948 if (dir
->app_names
== NULL
)
951 g_hash_table_iter_init (&iter
, dir
->app_names
);
952 while (g_hash_table_iter_next (&iter
, &app_name
, &filename
))
954 if (desktop_file_dir_app_name_is_masked (dir
, app_name
))
957 add_to_table_if_appropriate (apps
, app_name
, g_desktop_app_info_new_from_filename (filename
));
961 typedef struct _MemoryIndexEntry MemoryIndexEntry
;
962 typedef GHashTable MemoryIndex
;
964 struct _MemoryIndexEntry
966 const gchar
*app_name
; /* pointer to the hashtable key */
968 MemoryIndexEntry
*next
;
972 memory_index_entry_free (gpointer data
)
974 MemoryIndexEntry
*mie
= data
;
978 MemoryIndexEntry
*next
= mie
->next
;
980 g_slice_free (MemoryIndexEntry
, mie
);
986 memory_index_add_token (MemoryIndex
*mi
,
989 const gchar
*app_name
)
991 MemoryIndexEntry
*mie
, *first
;
993 mie
= g_slice_new (MemoryIndexEntry
);
994 mie
->app_name
= app_name
;
995 mie
->match_category
= match_category
;
997 first
= g_hash_table_lookup (mi
, token
);
1001 mie
->next
= first
->next
;
1007 g_hash_table_insert (mi
, g_strdup (token
), mie
);
1012 memory_index_add_string (MemoryIndex
*mi
,
1013 const gchar
*string
,
1014 gint match_category
,
1015 const gchar
*app_name
)
1017 gchar
**tokens
, **alternates
;
1020 tokens
= g_str_tokenize_and_fold (string
, NULL
, &alternates
);
1022 for (i
= 0; tokens
[i
]; i
++)
1023 memory_index_add_token (mi
, tokens
[i
], match_category
, app_name
);
1025 for (i
= 0; alternates
[i
]; i
++)
1026 memory_index_add_token (mi
, alternates
[i
], match_category
, app_name
);
1028 g_strfreev (alternates
);
1029 g_strfreev (tokens
);
1032 static MemoryIndex
*
1033 memory_index_new (void)
1035 return g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, memory_index_entry_free
);
1039 desktop_file_dir_unindexed_setup_search (DesktopFileDir
*dir
)
1041 GHashTableIter iter
;
1044 dir
->memory_index
= memory_index_new ();
1045 dir
->memory_implementations
= memory_index_new ();
1047 /* Nothing to search? */
1048 if (dir
->app_names
== NULL
)
1051 g_hash_table_iter_init (&iter
, dir
->app_names
);
1052 while (g_hash_table_iter_next (&iter
, &app
, &path
))
1056 if (desktop_file_dir_app_name_is_masked (dir
, app
))
1059 key_file
= g_key_file_new ();
1061 if (g_key_file_load_from_file (key_file
, path
, G_KEY_FILE_NONE
, NULL
) &&
1062 !g_key_file_get_boolean (key_file
, "Desktop Entry", "Hidden", NULL
))
1064 /* Index the interesting keys... */
1068 for (i
= 0; i
< G_N_ELEMENTS (desktop_key_match_category
); i
++)
1073 if (!desktop_key_match_category
[i
])
1076 raw
= g_key_file_get_locale_string (key_file
, "Desktop Entry", desktop_key_get_name (i
), NULL
, NULL
);
1079 if (i
== DESKTOP_KEY_Exec
&& raw
!= NULL
)
1081 /* Special handling: only match basename of first field */
1085 /* Remove extra arguments, if any */
1086 space
= raw
+ strcspn (raw
, " \t\n"); /* IFS */
1089 /* Skip the pathname, if any */
1090 if ((slash
= strrchr (raw
, '/')))
1095 memory_index_add_string (dir
->memory_index
, value
, desktop_key_match_category
[i
], app
);
1100 /* Make note of the Implements= line */
1101 implements
= g_key_file_get_string_list (key_file
, "Desktop Entry", "Implements", NULL
, NULL
);
1102 for (i
= 0; implements
&& implements
[i
]; i
++)
1103 memory_index_add_token (dir
->memory_implementations
, implements
[i
], 0, app
);
1104 g_strfreev (implements
);
1107 g_key_file_free (key_file
);
1112 desktop_file_dir_unindexed_search (DesktopFileDir
*dir
,
1113 const gchar
*search_token
)
1115 GHashTableIter iter
;
1116 gpointer key
, value
;
1118 if (!dir
->memory_index
)
1119 desktop_file_dir_unindexed_setup_search (dir
);
1121 g_hash_table_iter_init (&iter
, dir
->memory_index
);
1122 while (g_hash_table_iter_next (&iter
, &key
, &value
))
1124 MemoryIndexEntry
*mie
= value
;
1126 if (!g_str_has_prefix (key
, search_token
))
1131 add_token_result (mie
->app_name
, mie
->match_category
);
1138 array_contains (GPtrArray
*array
,
1143 for (i
= 0; i
< array
->len
; i
++)
1144 if (g_str_equal (array
->pdata
[i
], str
))
1151 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir
*dir
,
1152 const gchar
*mime_type
,
1154 GPtrArray
*blacklist
)
1156 UnindexedMimeTweaks
*tweaks
;
1159 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1164 if (tweaks
->additions
)
1166 for (i
= 0; tweaks
->additions
[i
]; i
++)
1168 gchar
*app_name
= tweaks
->additions
[i
];
1170 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1171 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1172 g_ptr_array_add (hits
, app_name
);
1176 if (tweaks
->removals
)
1178 for (i
= 0; tweaks
->removals
[i
]; i
++)
1180 gchar
*app_name
= tweaks
->removals
[i
];
1182 if (!desktop_file_dir_app_name_is_masked (dir
, app_name
) &&
1183 !array_contains (blacklist
, app_name
) && !array_contains (hits
, app_name
))
1184 g_ptr_array_add (blacklist
, app_name
);
1190 desktop_file_dir_unindexed_default_lookup (DesktopFileDir
*dir
,
1191 const gchar
*mime_type
,
1194 UnindexedMimeTweaks
*tweaks
;
1197 tweaks
= g_hash_table_lookup (dir
->mime_tweaks
, mime_type
);
1199 if (!tweaks
|| !tweaks
->defaults
)
1202 for (i
= 0; tweaks
->defaults
[i
]; i
++)
1204 gchar
*app_name
= tweaks
->defaults
[i
];
1206 if (!array_contains (results
, app_name
))
1207 g_ptr_array_add (results
, app_name
);
1212 desktop_file_dir_unindexed_get_implementations (DesktopFileDir
*dir
,
1214 const gchar
*interface
)
1216 MemoryIndexEntry
*mie
;
1218 if (!dir
->memory_index
)
1219 desktop_file_dir_unindexed_setup_search (dir
);
1221 for (mie
= g_hash_table_lookup (dir
->memory_implementations
, interface
); mie
; mie
= mie
->next
)
1222 *results
= g_list_prepend (*results
, g_strdup (mie
->app_name
));
1225 /* DesktopFileDir "API" {{{2 */
1228 * desktop_file_dir_create:
1229 * @array: the #GArray to add a new item to
1230 * @data_dir: an XDG_DATA_DIR
1232 * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
1236 desktop_file_dir_create (GArray
*array
,
1237 const gchar
*data_dir
)
1239 DesktopFileDir dir
= { 0, };
1241 dir
.path
= g_build_filename (data_dir
, "applications", NULL
);
1243 g_array_append_val (array
, dir
);
1247 * desktop_file_dir_create:
1248 * @array: the #GArray to add a new item to
1249 * @config_dir: an XDG_CONFIG_DIR
1251 * Just the same as desktop_file_dir_create() except that it does not
1252 * add the "applications" directory. It also marks the directory as
1253 * config-only, which prevents us from attempting to find desktop files
1257 desktop_file_dir_create_for_config (GArray
*array
,
1258 const gchar
*config_dir
)
1260 DesktopFileDir dir
= { 0, };
1262 dir
.path
= g_strdup (config_dir
);
1263 dir
.is_config
= TRUE
;
1265 g_array_append_val (array
, dir
);
1269 * desktop_file_dir_reset:
1270 * @dir: a #DesktopFileDir
1272 * Cleans up @dir, releasing most resources that it was using.
1275 desktop_file_dir_reset (DesktopFileDir
*dir
)
1277 if (dir
->alternatively_watching
)
1279 g_free (dir
->alternatively_watching
);
1280 dir
->alternatively_watching
= NULL
;
1285 g_signal_handlers_disconnect_by_func (dir
->monitor
, desktop_file_dir_changed
, dir
);
1286 g_object_unref (dir
->monitor
);
1287 dir
->monitor
= NULL
;
1292 g_hash_table_unref (dir
->app_names
);
1293 dir
->app_names
= NULL
;
1296 if (dir
->memory_index
)
1298 g_hash_table_unref (dir
->memory_index
);
1299 dir
->memory_index
= NULL
;
1302 if (dir
->mime_tweaks
)
1304 g_hash_table_unref (dir
->mime_tweaks
);
1305 dir
->mime_tweaks
= NULL
;
1308 if (dir
->memory_implementations
)
1310 g_hash_table_unref (dir
->memory_implementations
);
1311 dir
->memory_implementations
= NULL
;
1314 dir
->is_setup
= FALSE
;
1318 * desktop_file_dir_init:
1319 * @dir: a #DesktopFileDir
1321 * Does initial setup for @dir
1323 * You should only call this if @dir is not already setup.
1326 desktop_file_dir_init (DesktopFileDir
*dir
)
1328 const gchar
*watch_dir
;
1330 g_assert (!dir
->is_setup
);
1332 g_assert (!dir
->alternatively_watching
);
1333 g_assert (!dir
->monitor
);
1335 dir
->alternatively_watching
= desktop_file_dir_get_alternative_dir (dir
);
1336 watch_dir
= dir
->alternatively_watching
? dir
->alternatively_watching
: dir
->path
;
1338 /* There is a very thin race here if the watch_dir has been _removed_
1339 * between when we checked for it and when we establish the watch.
1340 * Removes probably don't happen in usual operation, and even if it
1341 * does (and we catch the unlikely race), the only degradation is that
1342 * we will fall back to polling.
1344 dir
->monitor
= g_local_file_monitor_new_in_worker (watch_dir
, TRUE
, G_FILE_MONITOR_NONE
,
1345 desktop_file_dir_changed
, dir
, NULL
);
1347 desktop_file_dir_unindexed_init (dir
);
1349 dir
->is_setup
= TRUE
;
1353 * desktop_file_dir_get_app:
1354 * @dir: a DesktopFileDir
1355 * @desktop_id: the desktop ID to load
1357 * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1358 * within @dir, even if it is hidden.
1360 * This function does not check if @desktop_id would be masked by a
1361 * directory with higher precedence. The caller must do so.
1363 static GDesktopAppInfo
*
1364 desktop_file_dir_get_app (DesktopFileDir
*dir
,
1365 const gchar
*desktop_id
)
1367 if (!dir
->app_names
)
1370 return desktop_file_dir_unindexed_get_app (dir
, desktop_id
);
1374 * desktop_file_dir_get_all:
1375 * @dir: a DesktopFileDir
1376 * @apps: a #GHashTable<string, GDesktopAppInfo>
1378 * Loads all desktop files in @dir and adds them to @apps, careful to
1379 * ensure we don't add any files masked by a similarly-named file in a
1380 * higher-precedence directory.
1383 desktop_file_dir_get_all (DesktopFileDir
*dir
,
1386 desktop_file_dir_unindexed_get_all (dir
, apps
);
1390 * desktop_file_dir_mime_lookup:
1391 * @dir: a #DesktopFileDir
1392 * @mime_type: the mime type to look up
1393 * @hits: the array to store the hits
1394 * @blacklist: the array to store the blacklist
1396 * Does a lookup of a mimetype against one desktop file directory,
1397 * recording any hits and blacklisting and "Removed" associations (so
1398 * later directories don't record them as hits).
1400 * The items added to @hits are duplicated, but the ones in @blacklist
1401 * are weak pointers. This facilitates simply freeing the blacklist
1402 * (which is only used for internal bookkeeping) but using the pdata of
1403 * @hits as the result of the operation.
1406 desktop_file_dir_mime_lookup (DesktopFileDir
*dir
,
1407 const gchar
*mime_type
,
1409 GPtrArray
*blacklist
)
1411 desktop_file_dir_unindexed_mime_lookup (dir
, mime_type
, hits
, blacklist
);
1415 * desktop_file_dir_default_lookup:
1416 * @dir: a #DesktopFileDir
1417 * @mime_type: the mime type to look up
1418 * @results: an array to store the results in
1420 * Collects the "default" applications for a given mime type from @dir.
1423 desktop_file_dir_default_lookup (DesktopFileDir
*dir
,
1424 const gchar
*mime_type
,
1427 desktop_file_dir_unindexed_default_lookup (dir
, mime_type
, results
);
1431 * desktop_file_dir_search:
1432 * @dir: a #DesktopFileDir
1433 * @term: a normalised and casefolded search term
1435 * Finds the names of applications in @dir that match @term.
1438 desktop_file_dir_search (DesktopFileDir
*dir
,
1439 const gchar
*search_token
)
1441 desktop_file_dir_unindexed_search (dir
, search_token
);
1445 desktop_file_dir_get_implementations (DesktopFileDir
*dir
,
1447 const gchar
*interface
)
1449 desktop_file_dir_unindexed_get_implementations (dir
, results
, interface
);
1452 /* Lock/unlock and global setup API {{{2 */
1455 desktop_file_dirs_lock (void)
1459 g_mutex_lock (&desktop_file_dir_lock
);
1461 if (desktop_file_dirs
== NULL
)
1463 const char * const *dirs
;
1467 tmp
= g_array_new (FALSE
, FALSE
, sizeof (DesktopFileDir
));
1469 /* First, the configs. Highest priority: the user's ~/.config */
1470 desktop_file_dir_create_for_config (tmp
, g_get_user_config_dir ());
1472 /* Next, the system configs (/etc/xdg, and so on). */
1473 dirs
= g_get_system_config_dirs ();
1474 for (i
= 0; dirs
[i
]; i
++)
1475 desktop_file_dir_create_for_config (tmp
, dirs
[i
]);
1477 /* Now the data. Highest priority: the user's ~/.local/share/applications */
1478 desktop_file_dir_user_data_index
= tmp
->len
;
1479 desktop_file_dir_create (tmp
, g_get_user_data_dir ());
1481 /* Following that, XDG_DATA_DIRS/applications, in order */
1482 dirs
= g_get_system_data_dirs ();
1483 for (i
= 0; dirs
[i
]; i
++)
1484 desktop_file_dir_create (tmp
, dirs
[i
]);
1486 /* The list of directories will never change after this. */
1487 desktop_file_dirs
= (DesktopFileDir
*) tmp
->data
;
1488 n_desktop_file_dirs
= tmp
->len
;
1490 g_array_free (tmp
, FALSE
);
1493 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1494 if (!desktop_file_dirs
[i
].is_setup
)
1495 desktop_file_dir_init (&desktop_file_dirs
[i
]);
1499 desktop_file_dirs_unlock (void)
1501 g_mutex_unlock (&desktop_file_dir_lock
);
1505 desktop_file_dirs_invalidate_user_config (void)
1507 g_mutex_lock (&desktop_file_dir_lock
);
1509 if (n_desktop_file_dirs
)
1510 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_config_index
]);
1512 g_mutex_unlock (&desktop_file_dir_lock
);
1516 desktop_file_dirs_invalidate_user_data (void)
1518 g_mutex_lock (&desktop_file_dir_lock
);
1520 if (n_desktop_file_dirs
)
1521 desktop_file_dir_reset (&desktop_file_dirs
[desktop_file_dir_user_data_index
]);
1523 g_mutex_unlock (&desktop_file_dir_lock
);
1526 /* GDesktopAppInfo implementation {{{1 */
1527 /* GObject implementation {{{2 */
1529 g_desktop_app_info_finalize (GObject
*object
)
1531 GDesktopAppInfo
*info
;
1533 info
= G_DESKTOP_APP_INFO (object
);
1535 g_free (info
->desktop_id
);
1536 g_free (info
->filename
);
1539 g_key_file_unref (info
->keyfile
);
1541 g_free (info
->name
);
1542 g_free (info
->generic_name
);
1543 g_free (info
->fullname
);
1544 g_free (info
->comment
);
1545 g_free (info
->icon_name
);
1547 g_object_unref (info
->icon
);
1548 g_strfreev (info
->keywords
);
1549 g_strfreev (info
->only_show_in
);
1550 g_strfreev (info
->not_show_in
);
1551 g_free (info
->try_exec
);
1552 g_free (info
->exec
);
1553 g_free (info
->binary
);
1554 g_free (info
->path
);
1555 g_free (info
->categories
);
1556 g_free (info
->startup_wm_class
);
1557 g_strfreev (info
->mime_types
);
1558 g_free (info
->app_id
);
1559 g_strfreev (info
->actions
);
1561 G_OBJECT_CLASS (g_desktop_app_info_parent_class
)->finalize (object
);
1565 g_desktop_app_info_set_property (GObject
*object
,
1567 const GValue
*value
,
1570 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1575 self
->filename
= g_value_dup_string (value
);
1579 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1585 g_desktop_app_info_get_property (GObject
*object
,
1590 GDesktopAppInfo
*self
= G_DESKTOP_APP_INFO (object
);
1595 g_value_set_string (value
, self
->filename
);
1598 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
1604 g_desktop_app_info_class_init (GDesktopAppInfoClass
*klass
)
1606 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
1608 gobject_class
->get_property
= g_desktop_app_info_get_property
;
1609 gobject_class
->set_property
= g_desktop_app_info_set_property
;
1610 gobject_class
->finalize
= g_desktop_app_info_finalize
;
1613 * GDesktopAppInfo:filename:
1615 * The origin filename of this #GDesktopAppInfo
1617 g_object_class_install_property (gobject_class
,
1619 g_param_spec_string ("filename", "Filename", "", NULL
,
1620 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1624 g_desktop_app_info_init (GDesktopAppInfo
*local
)
1628 /* Construction... {{{2 */
1632 * @exec: an exec line
1634 * Returns the first word in an exec line (ie: the binary name).
1636 * If @exec is " progname --foo %F" then returns "progname".
1639 binary_from_exec (const char *exec
)
1641 const char *p
, *start
;
1647 while (*p
!= ' ' && *p
!= 0)
1650 return g_strndup (start
, p
- start
);
1654 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo
*info
,
1661 gboolean bus_activatable
;
1663 start_group
= g_key_file_get_start_group (key_file
);
1664 if (start_group
== NULL
|| strcmp (start_group
, G_KEY_FILE_DESKTOP_GROUP
) != 0)
1666 g_free (start_group
);
1669 g_free (start_group
);
1671 type
= g_key_file_get_string (key_file
,
1672 G_KEY_FILE_DESKTOP_GROUP
,
1673 G_KEY_FILE_DESKTOP_KEY_TYPE
,
1675 if (type
== NULL
|| strcmp (type
, G_KEY_FILE_DESKTOP_TYPE_APPLICATION
) != 0)
1682 try_exec
= g_key_file_get_string (key_file
,
1683 G_KEY_FILE_DESKTOP_GROUP
,
1684 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC
,
1686 if (try_exec
&& try_exec
[0] != '\0')
1689 t
= g_find_program_in_path (try_exec
);
1698 exec
= g_key_file_get_string (key_file
,
1699 G_KEY_FILE_DESKTOP_GROUP
,
1700 G_KEY_FILE_DESKTOP_KEY_EXEC
,
1702 if (exec
&& exec
[0] != '\0')
1706 if (!g_shell_parse_argv (exec
, &argc
, &argv
, NULL
))
1715 t
= g_find_program_in_path (argv
[0]);
1728 info
->name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NAME
, NULL
, NULL
);
1729 info
->generic_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, GENERIC_NAME_KEY
, NULL
, NULL
);
1730 info
->fullname
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, FULL_NAME_KEY
, NULL
, NULL
);
1731 info
->keywords
= g_key_file_get_locale_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, KEYWORDS_KEY
, NULL
, NULL
, NULL
);
1732 info
->comment
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_COMMENT
, NULL
, NULL
);
1733 info
->nodisplay
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, NULL
) != FALSE
;
1734 info
->icon_name
= g_key_file_get_locale_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ICON
, NULL
, NULL
);
1735 info
->only_show_in
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN
, NULL
, NULL
);
1736 info
->not_show_in
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN
, NULL
, NULL
);
1737 info
->try_exec
= try_exec
;
1739 info
->path
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_PATH
, NULL
);
1740 info
->terminal
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_TERMINAL
, NULL
) != FALSE
;
1741 info
->startup_notify
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY
, NULL
) != FALSE
;
1742 info
->no_fuse
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, "X-GIO-NoFuse", NULL
) != FALSE
;
1743 info
->hidden
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_HIDDEN
, NULL
) != FALSE
;
1744 info
->categories
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_CATEGORIES
, NULL
);
1745 info
->startup_wm_class
= g_key_file_get_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
, STARTUP_WM_CLASS_KEY
, NULL
);
1746 info
->mime_types
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE
, NULL
, NULL
);
1747 bus_activatable
= g_key_file_get_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE
, NULL
);
1748 info
->actions
= g_key_file_get_string_list (key_file
, G_KEY_FILE_DESKTOP_GROUP
, G_KEY_FILE_DESKTOP_KEY_ACTIONS
, NULL
, NULL
);
1750 /* Remove the special-case: no Actions= key just means 0 extra actions */
1751 if (info
->actions
== NULL
)
1752 info
->actions
= g_new0 (gchar
*, 0 + 1);
1755 if (info
->icon_name
)
1757 if (g_path_is_absolute (info
->icon_name
))
1761 file
= g_file_new_for_path (info
->icon_name
);
1762 info
->icon
= g_file_icon_new (file
);
1763 g_object_unref (file
);
1769 /* Work around a common mistake in desktop files */
1770 if ((p
= strrchr (info
->icon_name
, '.')) != NULL
&&
1771 (strcmp (p
, ".png") == 0 ||
1772 strcmp (p
, ".xpm") == 0 ||
1773 strcmp (p
, ".svg") == 0))
1776 info
->icon
= g_themed_icon_new (info
->icon_name
);
1781 info
->binary
= binary_from_exec (info
->exec
);
1783 if (info
->path
&& info
->path
[0] == '\0')
1785 g_free (info
->path
);
1789 /* Can only be DBusActivatable if we know the filename, which means
1790 * that this won't work for the load-from-keyfile case.
1792 if (bus_activatable
&& info
->filename
)
1797 basename
= g_path_get_basename (info
->filename
);
1798 last_dot
= strrchr (basename
, '.');
1800 if (last_dot
&& g_str_equal (last_dot
, ".desktop"))
1804 if (g_dbus_is_name (basename
) && basename
[0] != ':')
1805 info
->app_id
= g_strdup (basename
);
1811 info
->keyfile
= g_key_file_ref (key_file
);
1817 g_desktop_app_info_load_file (GDesktopAppInfo
*self
)
1820 gboolean retval
= FALSE
;
1822 g_return_val_if_fail (self
->filename
!= NULL
, FALSE
);
1824 self
->desktop_id
= g_path_get_basename (self
->filename
);
1826 key_file
= g_key_file_new ();
1828 if (g_key_file_load_from_file (key_file
, self
->filename
, G_KEY_FILE_NONE
, NULL
))
1829 retval
= g_desktop_app_info_load_from_keyfile (self
, key_file
);
1831 g_key_file_unref (key_file
);
1836 * g_desktop_app_info_new_from_keyfile:
1837 * @key_file: an opened #GKeyFile
1839 * Creates a new #GDesktopAppInfo.
1841 * Returns: a new #GDesktopAppInfo or %NULL on error.
1846 g_desktop_app_info_new_from_keyfile (GKeyFile
*key_file
)
1848 GDesktopAppInfo
*info
;
1850 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1851 info
->filename
= NULL
;
1852 if (!g_desktop_app_info_load_from_keyfile (info
, key_file
))
1854 g_object_unref (info
);
1861 * g_desktop_app_info_new_from_filename:
1862 * @filename: (type filename): the path of a desktop file, in the GLib
1865 * Creates a new #GDesktopAppInfo.
1867 * Returns: a new #GDesktopAppInfo or %NULL on error.
1870 g_desktop_app_info_new_from_filename (const char *filename
)
1872 GDesktopAppInfo
*info
= NULL
;
1874 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, "filename", filename
, NULL
);
1875 if (!g_desktop_app_info_load_file (info
))
1877 g_object_unref (info
);
1884 * g_desktop_app_info_new:
1885 * @desktop_id: the desktop file id
1887 * Creates a new #GDesktopAppInfo based on a desktop file id.
1889 * A desktop file id is the basename of the desktop file, including the
1890 * .desktop extension. GIO is looking for a desktop file with this name
1891 * in the `applications` subdirectories of the XDG
1892 * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1893 * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1894 * prefix-to-subdirectory mapping that is described in the
1895 * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1896 * (i.e. a desktop id of kde-foo.desktop will match
1897 * `/usr/share/applications/kde/foo.desktop`).
1899 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
1902 g_desktop_app_info_new (const char *desktop_id
)
1904 GDesktopAppInfo
*appinfo
= NULL
;
1907 desktop_file_dirs_lock ();
1909 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
1911 appinfo
= desktop_file_dir_get_app (&desktop_file_dirs
[i
], desktop_id
);
1917 desktop_file_dirs_unlock ();
1919 if (appinfo
== NULL
)
1922 g_free (appinfo
->desktop_id
);
1923 appinfo
->desktop_id
= g_strdup (desktop_id
);
1925 if (g_desktop_app_info_get_is_hidden (appinfo
))
1927 g_object_unref (appinfo
);
1935 g_desktop_app_info_dup (GAppInfo
*appinfo
)
1937 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1938 GDesktopAppInfo
*new_info
;
1940 new_info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
1942 new_info
->filename
= g_strdup (info
->filename
);
1943 new_info
->desktop_id
= g_strdup (info
->desktop_id
);
1946 new_info
->keyfile
= g_key_file_ref (info
->keyfile
);
1948 new_info
->name
= g_strdup (info
->name
);
1949 new_info
->generic_name
= g_strdup (info
->generic_name
);
1950 new_info
->fullname
= g_strdup (info
->fullname
);
1951 new_info
->keywords
= g_strdupv (info
->keywords
);
1952 new_info
->comment
= g_strdup (info
->comment
);
1953 new_info
->nodisplay
= info
->nodisplay
;
1954 new_info
->icon_name
= g_strdup (info
->icon_name
);
1956 new_info
->icon
= g_object_ref (info
->icon
);
1957 new_info
->only_show_in
= g_strdupv (info
->only_show_in
);
1958 new_info
->not_show_in
= g_strdupv (info
->not_show_in
);
1959 new_info
->try_exec
= g_strdup (info
->try_exec
);
1960 new_info
->exec
= g_strdup (info
->exec
);
1961 new_info
->binary
= g_strdup (info
->binary
);
1962 new_info
->path
= g_strdup (info
->path
);
1963 new_info
->app_id
= g_strdup (info
->app_id
);
1964 new_info
->hidden
= info
->hidden
;
1965 new_info
->terminal
= info
->terminal
;
1966 new_info
->startup_notify
= info
->startup_notify
;
1968 return G_APP_INFO (new_info
);
1971 /* GAppInfo interface implementation functions {{{2 */
1974 g_desktop_app_info_equal (GAppInfo
*appinfo1
,
1977 GDesktopAppInfo
*info1
= G_DESKTOP_APP_INFO (appinfo1
);
1978 GDesktopAppInfo
*info2
= G_DESKTOP_APP_INFO (appinfo2
);
1980 if (info1
->desktop_id
== NULL
||
1981 info2
->desktop_id
== NULL
)
1982 return info1
== info2
;
1984 return strcmp (info1
->desktop_id
, info2
->desktop_id
) == 0;
1988 g_desktop_app_info_get_id (GAppInfo
*appinfo
)
1990 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
1992 return info
->desktop_id
;
1996 g_desktop_app_info_get_name (GAppInfo
*appinfo
)
1998 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2000 if (info
->name
== NULL
)
2001 return _("Unnamed");
2006 g_desktop_app_info_get_display_name (GAppInfo
*appinfo
)
2008 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2010 if (info
->fullname
== NULL
)
2011 return g_desktop_app_info_get_name (appinfo
);
2012 return info
->fullname
;
2016 * g_desktop_app_info_get_is_hidden:
2017 * @info: a #GDesktopAppInfo.
2019 * A desktop file is hidden if the Hidden key in it is
2022 * Returns: %TRUE if hidden, %FALSE otherwise.
2025 g_desktop_app_info_get_is_hidden (GDesktopAppInfo
*info
)
2027 return info
->hidden
;
2031 * g_desktop_app_info_get_filename:
2032 * @info: a #GDesktopAppInfo
2034 * When @info was created from a known filename, return it. In some
2035 * situations such as the #GDesktopAppInfo returned from
2036 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
2038 * Returns: (type filename): The full path to the file for @info,
2039 * or %NULL if not known.
2043 g_desktop_app_info_get_filename (GDesktopAppInfo
*info
)
2045 return info
->filename
;
2049 g_desktop_app_info_get_description (GAppInfo
*appinfo
)
2051 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2053 return info
->comment
;
2057 g_desktop_app_info_get_executable (GAppInfo
*appinfo
)
2059 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2061 return info
->binary
;
2065 g_desktop_app_info_get_commandline (GAppInfo
*appinfo
)
2067 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2073 g_desktop_app_info_get_icon (GAppInfo
*appinfo
)
2075 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2081 * g_desktop_app_info_get_categories:
2082 * @info: a #GDesktopAppInfo
2084 * Gets the categories from the desktop file.
2086 * Returns: The unparsed Categories key from the desktop file;
2087 * i.e. no attempt is made to split it by ';' or validate it.
2090 g_desktop_app_info_get_categories (GDesktopAppInfo
*info
)
2092 return info
->categories
;
2096 * g_desktop_app_info_get_keywords:
2097 * @info: a #GDesktopAppInfo
2099 * Gets the keywords from the desktop file.
2101 * Returns: (transfer none): The value of the Keywords key
2105 const char * const *
2106 g_desktop_app_info_get_keywords (GDesktopAppInfo
*info
)
2108 return (const char * const *)info
->keywords
;
2112 * g_desktop_app_info_get_generic_name:
2113 * @info: a #GDesktopAppInfo
2115 * Gets the generic name from the destkop file.
2117 * Returns: The value of the GenericName key
2120 g_desktop_app_info_get_generic_name (GDesktopAppInfo
*info
)
2122 return info
->generic_name
;
2126 * g_desktop_app_info_get_nodisplay:
2127 * @info: a #GDesktopAppInfo
2129 * Gets the value of the NoDisplay key, which helps determine if the
2130 * application info should be shown in menus. See
2131 * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2133 * Returns: The value of the NoDisplay key
2138 g_desktop_app_info_get_nodisplay (GDesktopAppInfo
*info
)
2140 return info
->nodisplay
;
2144 * g_desktop_app_info_get_show_in:
2145 * @info: a #GDesktopAppInfo
2146 * @desktop_env: (nullable): a string specifying a desktop name
2148 * Checks if the application info should be shown in menus that list available
2149 * applications for a specific name of the desktop, based on the
2150 * `OnlyShowIn` and `NotShowIn` keys.
2152 * @desktop_env should typically be given as %NULL, in which case the
2153 * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
2154 * to override the default mechanism then you may specify @desktop_env,
2155 * but this is not recommended.
2157 * Note that g_app_info_should_show() for @info will include this check (with
2158 * %NULL for @desktop_env) as well as additional checks.
2160 * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2161 * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2167 g_desktop_app_info_get_show_in (GDesktopAppInfo
*info
,
2168 const gchar
*desktop_env
)
2170 const gchar
*specified_envs
[] = { desktop_env
, NULL
};
2171 const gchar
* const *envs
;
2174 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
2177 envs
= specified_envs
;
2179 envs
= get_current_desktops (NULL
);
2181 for (i
= 0; envs
[i
]; i
++)
2185 if (info
->only_show_in
)
2186 for (j
= 0; info
->only_show_in
[j
]; j
++)
2187 if (g_str_equal (info
->only_show_in
[j
], envs
[i
]))
2190 if (info
->not_show_in
)
2191 for (j
= 0; info
->not_show_in
[j
]; j
++)
2192 if (g_str_equal (info
->not_show_in
[j
], envs
[i
]))
2196 return info
->only_show_in
== NULL
;
2199 /* Launching... {{{2 */
2202 expand_macro_single (char macro
, char *uri
)
2205 char *result
= NULL
;
2209 file
= g_file_new_for_uri (uri
);
2215 result
= g_shell_quote (uri
);
2219 path
= g_file_get_path (file
);
2221 result
= g_shell_quote (path
);
2225 path
= g_file_get_path (file
);
2228 name
= g_path_get_dirname (path
);
2229 result
= g_shell_quote (name
);
2235 path
= g_file_get_path (file
);
2238 name
= g_path_get_basename (path
);
2239 result
= g_shell_quote (name
);
2245 g_object_unref (file
);
2252 expand_macro (char macro
,
2254 GDesktopAppInfo
*info
,
2257 GList
*uris
= *uri_list
;
2259 gboolean force_file_uri
;
2260 char force_file_uri_macro
;
2263 g_return_if_fail (exec
!= NULL
);
2265 /* On %u and %U, pass POSIX file path pointing to the URI via
2266 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2267 * running or the URI doesn't have a POSIX file path via FUSE
2268 * we'll just pass the URI.
2270 force_file_uri_macro
= macro
;
2271 force_file_uri
= FALSE
;
2277 force_file_uri_macro
= 'f';
2278 force_file_uri
= TRUE
;
2281 force_file_uri_macro
= 'F';
2282 force_file_uri
= TRUE
;
2298 if (!force_file_uri
||
2299 /* Pass URI if it contains an anchor */
2300 strchr (uri
, '#') != NULL
)
2302 expanded
= expand_macro_single (macro
, uri
);
2306 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2307 if (expanded
== NULL
)
2308 expanded
= expand_macro_single (macro
, uri
);
2313 g_string_append (exec
, expanded
);
2329 if (!force_file_uri
||
2330 /* Pass URI if it contains an anchor */
2331 strchr (uri
, '#') != NULL
)
2333 expanded
= expand_macro_single (macro
, uri
);
2337 expanded
= expand_macro_single (force_file_uri_macro
, uri
);
2338 if (expanded
== NULL
)
2339 expanded
= expand_macro_single (macro
, uri
);
2344 g_string_append (exec
, expanded
);
2350 if (uris
!= NULL
&& expanded
)
2351 g_string_append_c (exec
, ' ');
2357 if (info
->icon_name
)
2359 g_string_append (exec
, "--icon ");
2360 expanded
= g_shell_quote (info
->icon_name
);
2361 g_string_append (exec
, expanded
);
2369 expanded
= g_shell_quote (info
->name
);
2370 g_string_append (exec
, expanded
);
2378 expanded
= g_shell_quote (info
->filename
);
2379 g_string_append (exec
, expanded
);
2384 case 'm': /* deprecated */
2388 g_string_append_c (exec
, '%');
2396 expand_application_parameters (GDesktopAppInfo
*info
,
2397 const gchar
*exec_line
,
2403 GList
*uri_list
= *uris
;
2404 const char *p
= exec_line
;
2405 GString
*expanded_exec
;
2408 if (exec_line
== NULL
)
2410 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2411 _("Desktop file didn’t specify Exec field"));
2415 expanded_exec
= g_string_new (NULL
);
2419 if (p
[0] == '%' && p
[1] != '\0')
2421 expand_macro (p
[1], expanded_exec
, info
, uris
);
2425 g_string_append_c (expanded_exec
, *p
);
2430 /* No file substitutions */
2431 if (uri_list
== *uris
&& uri_list
!= NULL
)
2433 /* If there is no macro default to %f. This is also what KDE does */
2434 g_string_append_c (expanded_exec
, ' ');
2435 expand_macro ('f', expanded_exec
, info
, uris
);
2438 res
= g_shell_parse_argv (expanded_exec
->str
, argc
, argv
, error
);
2439 g_string_free (expanded_exec
, TRUE
);
2444 prepend_terminal_to_vector (int *argc
,
2451 char **term_argv
= NULL
;
2456 g_return_val_if_fail (argc
!= NULL
, FALSE
);
2457 g_return_val_if_fail (argv
!= NULL
, FALSE
);
2465 /* compute size if not given */
2468 for (i
= 0; the_argv
[i
] != NULL
; i
++)
2474 term_argv
= g_new0 (char *, 3);
2476 check
= g_find_program_in_path ("gnome-terminal");
2479 term_argv
[0] = check
;
2480 /* Note that gnome-terminal takes -x and
2481 * as -e in gnome-terminal is broken we use that. */
2482 term_argv
[1] = g_strdup ("-x");
2487 check
= g_find_program_in_path ("nxterm");
2489 check
= g_find_program_in_path ("color-xterm");
2491 check
= g_find_program_in_path ("rxvt");
2493 check
= g_find_program_in_path ("dtterm");
2496 check
= g_strdup ("xterm");
2497 g_warning ("couldn't find a terminal, falling back to xterm");
2499 term_argv
[0] = check
;
2500 term_argv
[1] = g_strdup ("-e");
2503 real_argc
= term_argc
+ *argc
;
2504 real_argv
= g_new (char *, real_argc
+ 1);
2506 for (i
= 0; i
< term_argc
; i
++)
2507 real_argv
[i
] = term_argv
[i
];
2509 for (j
= 0; j
< *argc
; j
++, i
++)
2510 real_argv
[i
] = (char *)the_argv
[j
];
2512 real_argv
[i
] = NULL
;
2518 /* we use g_free here as we sucked all the inner strings
2519 * out from it into real_argv */
2524 #endif /* G_OS_WIN32 */
2528 create_files_for_uris (GList
*uris
)
2535 for (iter
= uris
; iter
; iter
= iter
->next
)
2537 GFile
*file
= g_file_new_for_uri ((char *)iter
->data
);
2538 res
= g_list_prepend (res
, file
);
2541 return g_list_reverse (res
);
2546 GSpawnChildSetupFunc user_setup
;
2547 gpointer user_setup_data
;
2553 child_setup (gpointer user_data
)
2555 ChildSetupData
*data
= user_data
;
2557 if (data
->pid_envvar
)
2559 pid_t pid
= getpid ();
2563 /* Write the pid into the space already reserved for it in the
2564 * environment array. We can't use sprintf because it might
2565 * malloc, so we do it by hand. It's simplest to write the pid
2566 * out backwards first, then copy it over.
2568 for (i
= 0; pid
; i
++, pid
/= 10)
2569 buf
[i
] = (pid
% 10) + '0';
2570 for (i
--; i
>= 0; i
--)
2571 *(data
->pid_envvar
++) = buf
[i
];
2572 *data
->pid_envvar
= '\0';
2575 if (data
->user_setup
)
2576 data
->user_setup (data
->user_setup_data
);
2580 notify_desktop_launch (GDBusConnection
*session_bus
,
2581 GDesktopAppInfo
*info
,
2583 const char *display
,
2588 GVariantBuilder uri_variant
;
2589 GVariantBuilder extras_variant
;
2591 const char *desktop_file_id
;
2592 const char *gio_desktop_file
;
2594 if (session_bus
== NULL
)
2597 g_variant_builder_init (&uri_variant
, G_VARIANT_TYPE ("as"));
2598 for (iter
= uris
; iter
; iter
= iter
->next
)
2599 g_variant_builder_add (&uri_variant
, "s", iter
->data
);
2601 g_variant_builder_init (&extras_variant
, G_VARIANT_TYPE ("a{sv}"));
2602 if (sn_id
!= NULL
&& g_utf8_validate (sn_id
, -1, NULL
))
2603 g_variant_builder_add (&extras_variant
, "{sv}",
2607 gio_desktop_file
= g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2608 if (gio_desktop_file
!= NULL
)
2609 g_variant_builder_add (&extras_variant
, "{sv}",
2610 "origin-desktop-file",
2611 g_variant_new_bytestring (gio_desktop_file
));
2612 if (g_get_prgname () != NULL
)
2613 g_variant_builder_add (&extras_variant
, "{sv}",
2615 g_variant_new_bytestring (g_get_prgname ()));
2616 g_variant_builder_add (&extras_variant
, "{sv}",
2619 (gint64
)getpid ()));
2622 desktop_file_id
= info
->filename
;
2623 else if (info
->desktop_id
)
2624 desktop_file_id
= info
->desktop_id
;
2626 desktop_file_id
= "";
2628 msg
= g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2629 "org.gtk.gio.DesktopAppInfo",
2631 g_dbus_message_set_body (msg
, g_variant_new ("(@aysxasa{sv})",
2632 g_variant_new_bytestring (desktop_file_id
),
2633 display
? display
: "",
2637 g_dbus_connection_send_message (session_bus
,
2641 g_object_unref (msg
);
2644 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2647 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo
*info
,
2648 GDBusConnection
*session_bus
,
2649 const gchar
*exec_line
,
2651 GAppLaunchContext
*launch_context
,
2652 GSpawnFlags spawn_flags
,
2653 GSpawnChildSetupFunc user_setup
,
2654 gpointer user_setup_data
,
2655 GDesktopAppLaunchCallback pid_callback
,
2656 gpointer pid_callback_data
,
2659 gboolean completed
= FALSE
;
2661 char **argv
, **envp
;
2663 ChildSetupData data
;
2665 g_return_val_if_fail (info
!= NULL
, FALSE
);
2670 envp
= g_app_launch_context_get_environment (launch_context
);
2672 envp
= g_get_environ ();
2677 GList
*launched_uris
;
2682 if (!expand_application_parameters (info
, exec_line
, &uris
, &argc
, &argv
, error
))
2685 /* Get the subset of URIs we're launching with this process */
2686 launched_uris
= NULL
;
2687 for (iter
= old_uris
; iter
!= NULL
&& iter
!= uris
; iter
= iter
->next
)
2688 launched_uris
= g_list_prepend (launched_uris
, iter
->data
);
2689 launched_uris
= g_list_reverse (launched_uris
);
2691 if (info
->terminal
&& !prepend_terminal_to_vector (&argc
, &argv
))
2693 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
2694 _("Unable to find terminal required for application"));
2698 data
.user_setup
= user_setup
;
2699 data
.user_setup_data
= user_setup_data
;
2703 envp
= g_environ_setenv (envp
,
2704 "GIO_LAUNCHED_DESKTOP_FILE",
2707 envp
= g_environ_setenv (envp
,
2708 "GIO_LAUNCHED_DESKTOP_FILE_PID",
2709 "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
2711 data
.pid_envvar
= (char *)g_environ_getenv (envp
, "GIO_LAUNCHED_DESKTOP_FILE_PID");
2715 data
.pid_envvar
= NULL
;
2721 GList
*launched_files
= create_files_for_uris (launched_uris
);
2723 if (info
->startup_notify
)
2725 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
,
2729 envp
= g_environ_setenv (envp
, "DESKTOP_STARTUP_ID", sn_id
, TRUE
);
2732 g_list_free_full (launched_files
, g_object_unref
);
2735 if (!g_spawn_async (info
->path
,
2745 g_app_launch_context_launch_failed (launch_context
, sn_id
);
2748 g_list_free (launched_uris
);
2753 if (pid_callback
!= NULL
)
2754 pid_callback (info
, pid
, pid_callback_data
);
2756 if (launch_context
!= NULL
)
2758 GVariantBuilder builder
;
2759 GVariant
*platform_data
;
2761 g_variant_builder_init (&builder
, G_VARIANT_TYPE_ARRAY
);
2762 g_variant_builder_add (&builder
, "{sv}", "pid", g_variant_new_int32 (pid
));
2764 g_variant_builder_add (&builder
, "{sv}", "startup-notification-id", g_variant_new_string (sn_id
));
2765 platform_data
= g_variant_ref_sink (g_variant_builder_end (&builder
));
2766 g_signal_emit_by_name (launch_context
, "launched", info
, platform_data
);
2767 g_variant_unref (platform_data
);
2770 notify_desktop_launch (session_bus
,
2778 g_list_free (launched_uris
);
2783 while (uris
!= NULL
);
2795 object_path_from_appid (const gchar
*appid
)
2797 gchar
*appid_path
, *iter
;
2799 appid_path
= g_strconcat ("/", appid
, NULL
);
2800 for (iter
= appid_path
; *iter
; iter
++)
2813 g_desktop_app_info_make_platform_data (GDesktopAppInfo
*info
,
2815 GAppLaunchContext
*launch_context
)
2817 GVariantBuilder builder
;
2819 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
2823 GList
*launched_files
= create_files_for_uris (uris
);
2825 if (info
->startup_notify
)
2829 sn_id
= g_app_launch_context_get_startup_notify_id (launch_context
, G_APP_INFO (info
), launched_files
);
2831 g_variant_builder_add (&builder
, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id
));
2834 g_list_free_full (launched_files
, g_object_unref
);
2837 return g_variant_builder_end (&builder
);
2841 launch_uris_with_dbus (GDesktopAppInfo
*info
,
2842 GDBusConnection
*session_bus
,
2844 GAppLaunchContext
*launch_context
)
2846 GVariantBuilder builder
;
2849 g_variant_builder_init (&builder
, G_VARIANT_TYPE_TUPLE
);
2855 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
2856 for (iter
= uris
; iter
; iter
= iter
->next
)
2857 g_variant_builder_add (&builder
, "s", iter
->data
);
2858 g_variant_builder_close (&builder
);
2861 g_variant_builder_add_value (&builder
, g_desktop_app_info_make_platform_data (info
, uris
, launch_context
));
2863 /* This is non-blocking API. Similar to launching via fork()/exec()
2864 * we don't wait around to see if the program crashed during startup.
2865 * This is what startup-notification's job is...
2867 object_path
= object_path_from_appid (info
->app_id
);
2868 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
, "org.freedesktop.Application",
2869 uris
? "Open" : "Activate", g_variant_builder_end (&builder
),
2870 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
2871 g_free (object_path
);
2875 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo
*info
,
2876 GDBusConnection
*session_bus
,
2878 GAppLaunchContext
*launch_context
)
2880 GList
*ruris
= uris
;
2881 g_autofree
char *app_id
= NULL
;
2883 g_return_val_if_fail (info
!= NULL
, FALSE
);
2886 app_id
= g_desktop_app_info_get_string (info
, "X-Flatpak");
2887 if (app_id
&& *app_id
)
2889 ruris
= g_document_portal_add_documents (uris
, app_id
, NULL
);
2895 launch_uris_with_dbus (info
, session_bus
, ruris
, launch_context
);
2898 g_list_free_full (ruris
, g_free
);
2904 g_desktop_app_info_launch_uris_internal (GAppInfo
*appinfo
,
2906 GAppLaunchContext
*launch_context
,
2907 GSpawnFlags spawn_flags
,
2908 GSpawnChildSetupFunc user_setup
,
2909 gpointer user_setup_data
,
2910 GDesktopAppLaunchCallback pid_callback
,
2911 gpointer pid_callback_data
,
2914 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2915 GDBusConnection
*session_bus
;
2916 gboolean success
= TRUE
;
2918 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
2920 if (session_bus
&& info
->app_id
)
2921 g_desktop_app_info_launch_uris_with_dbus (info
, session_bus
, uris
, launch_context
);
2923 success
= g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, info
->exec
, uris
, launch_context
,
2924 spawn_flags
, user_setup
, user_setup_data
,
2925 pid_callback
, pid_callback_data
, error
);
2927 if (session_bus
!= NULL
)
2929 /* This asynchronous flush holds a reference until it completes,
2930 * which ensures that the following unref won't immediately kill
2931 * the connection if we were the initial owner.
2933 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
2934 g_object_unref (session_bus
);
2941 g_desktop_app_info_launch_uris (GAppInfo
*appinfo
,
2943 GAppLaunchContext
*launch_context
,
2946 return g_desktop_app_info_launch_uris_internal (appinfo
, uris
,
2948 _SPAWN_FLAGS_DEFAULT
,
2949 NULL
, NULL
, NULL
, NULL
,
2954 g_desktop_app_info_supports_uris (GAppInfo
*appinfo
)
2956 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2958 return info
->exec
&&
2959 ((strstr (info
->exec
, "%u") != NULL
) ||
2960 (strstr (info
->exec
, "%U") != NULL
));
2964 g_desktop_app_info_supports_files (GAppInfo
*appinfo
)
2966 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
2968 return info
->exec
&&
2969 ((strstr (info
->exec
, "%f") != NULL
) ||
2970 (strstr (info
->exec
, "%F") != NULL
));
2974 g_desktop_app_info_launch (GAppInfo
*appinfo
,
2976 GAppLaunchContext
*launch_context
,
2986 uri
= g_file_get_uri (files
->data
);
2987 uris
= g_list_prepend (uris
, uri
);
2988 files
= files
->next
;
2991 uris
= g_list_reverse (uris
);
2993 res
= g_desktop_app_info_launch_uris (appinfo
, uris
, launch_context
, error
);
2995 g_list_free_full (uris
, g_free
);
3001 * g_desktop_app_info_launch_uris_as_manager:
3002 * @appinfo: a #GDesktopAppInfo
3003 * @uris: (element-type utf8): List of URIs
3004 * @launch_context: (nullable): a #GAppLaunchContext
3005 * @spawn_flags: #GSpawnFlags, used for each process
3006 * @user_setup: (scope call) (nullable): a #GSpawnChildSetupFunc, used once
3008 * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup
3009 * @pid_callback: (scope call) (nullable): Callback for child processes
3010 * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback
3011 * @error: return location for a #GError, or %NULL
3013 * This function performs the equivalent of g_app_info_launch_uris(),
3014 * but is intended primarily for operating system components that
3015 * launch applications. Ordinary applications should use
3016 * g_app_info_launch_uris().
3018 * If the application is launched via traditional UNIX fork()/exec()
3019 * then @spawn_flags, @user_setup and @user_setup_data are used for the
3020 * call to g_spawn_async(). Additionally, @pid_callback (with
3021 * @pid_callback_data) will be called to inform about the PID of the
3024 * If application launching occurs via some other mechanism (eg: D-Bus
3025 * activation) then @spawn_flags, @user_setup, @user_setup_data,
3026 * @pid_callback and @pid_callback_data are ignored.
3028 * Returns: %TRUE on successful launch, %FALSE otherwise.
3031 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo
*appinfo
,
3033 GAppLaunchContext
*launch_context
,
3034 GSpawnFlags spawn_flags
,
3035 GSpawnChildSetupFunc user_setup
,
3036 gpointer user_setup_data
,
3037 GDesktopAppLaunchCallback pid_callback
,
3038 gpointer pid_callback_data
,
3041 return g_desktop_app_info_launch_uris_internal ((GAppInfo
*)appinfo
,
3052 /* OnlyShowIn API support {{{2 */
3055 * g_desktop_app_info_set_desktop_env:
3056 * @desktop_env: a string specifying what desktop this is
3058 * Sets the name of the desktop that the application is running in.
3059 * This is used by g_app_info_should_show() and
3060 * g_desktop_app_info_get_show_in() to evaluate the
3061 * `OnlyShowIn` and `NotShowIn`
3062 * desktop entry fields.
3064 * Should be called only once; subsequent calls are ignored.
3066 * Deprecated:2.42:do not use this API. Since 2.42 the value of the
3067 * `XDG_CURRENT_DESKTOP` environment variable will be used.
3070 g_desktop_app_info_set_desktop_env (const gchar
*desktop_env
)
3072 get_current_desktops (desktop_env
);
3076 g_desktop_app_info_should_show (GAppInfo
*appinfo
)
3078 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3080 if (info
->nodisplay
)
3083 return g_desktop_app_info_get_show_in (info
, NULL
);
3086 /* mime types/default apps support {{{2 */
3095 ensure_dir (DirType type
,
3098 char *path
, *display_name
;
3104 path
= g_build_filename (g_get_user_config_dir (), NULL
);
3108 path
= g_build_filename (g_get_user_data_dir (), "applications", NULL
);
3112 path
= g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL
);
3116 g_assert_not_reached ();
3120 if (g_mkdir_with_parents (path
, 0700) == 0)
3124 display_name
= g_filename_display_name (path
);
3125 if (type
== APP_DIR
)
3126 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3127 _("Can’t create user application configuration folder %s: %s"),
3128 display_name
, g_strerror (errsv
));
3130 g_set_error (error
, G_IO_ERROR
, g_io_error_from_errno (errsv
),
3131 _("Can’t create user MIME configuration folder %s: %s"),
3132 display_name
, g_strerror (errsv
));
3134 g_free (display_name
);
3141 update_mimeapps_list (const char *desktop_id
,
3142 const char *content_type
,
3143 UpdateMimeFlags flags
,
3146 char *dirname
, *filename
, *string
;
3148 gboolean load_succeeded
, res
;
3149 char **old_list
, **list
;
3150 gsize length
, data_size
;
3153 char **content_types
;
3155 /* Don't add both at start and end */
3156 g_assert (!((flags
& UPDATE_MIME_SET_DEFAULT
) &&
3157 (flags
& UPDATE_MIME_SET_NON_DEFAULT
)));
3159 dirname
= ensure_dir (CONF_DIR
, error
);
3163 filename
= g_build_filename (dirname
, "mimeapps.list", NULL
);
3166 key_file
= g_key_file_new ();
3167 load_succeeded
= g_key_file_load_from_file (key_file
, filename
, G_KEY_FILE_NONE
, NULL
);
3168 if (!load_succeeded
||
3169 (!g_key_file_has_group (key_file
, ADDED_ASSOCIATIONS_GROUP
) &&
3170 !g_key_file_has_group (key_file
, REMOVED_ASSOCIATIONS_GROUP
) &&
3171 !g_key_file_has_group (key_file
, DEFAULT_APPLICATIONS_GROUP
)))
3173 g_key_file_free (key_file
);
3174 key_file
= g_key_file_new ();
3179 content_types
= g_new (char *, 2);
3180 content_types
[0] = g_strdup (content_type
);
3181 content_types
[1] = NULL
;
3185 content_types
= g_key_file_get_keys (key_file
, DEFAULT_APPLICATIONS_GROUP
, NULL
, NULL
);
3188 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3190 /* set as default, if requested so */
3191 string
= g_key_file_get_string (key_file
,
3192 DEFAULT_APPLICATIONS_GROUP
,
3196 if (g_strcmp0 (string
, desktop_id
) != 0 &&
3197 (flags
& UPDATE_MIME_SET_DEFAULT
))
3200 string
= g_strdup (desktop_id
);
3202 /* add in the non-default list too, if it's not already there */
3203 flags
|= UPDATE_MIME_SET_NON_DEFAULT
;
3206 if (string
== NULL
|| desktop_id
== NULL
)
3207 g_key_file_remove_key (key_file
,
3208 DEFAULT_APPLICATIONS_GROUP
,
3212 g_key_file_set_string (key_file
,
3213 DEFAULT_APPLICATIONS_GROUP
,
3222 /* reuse the list from above */
3226 g_strfreev (content_types
);
3227 content_types
= g_key_file_get_keys (key_file
, ADDED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3230 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3232 /* Add to the right place in the list */
3235 old_list
= g_key_file_get_string_list (key_file
, ADDED_ASSOCIATIONS_GROUP
,
3236 content_types
[k
], &length
, NULL
);
3238 list
= g_new (char *, 1 + length
+ 1);
3242 /* if we're adding a last-used hint, just put the application in front of the list */
3243 if (flags
& UPDATE_MIME_SET_LAST_USED
)
3245 /* avoid adding this again as non-default later */
3246 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3247 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3249 list
[i
++] = g_strdup (desktop_id
);
3254 for (j
= 0; old_list
[j
] != NULL
; j
++)
3256 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3258 /* rewrite other entries if they're different from the new one */
3259 list
[i
++] = g_strdup (old_list
[j
]);
3261 else if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3263 /* we encountered an old entry which is equal to the one we're adding as non-default,
3264 * don't change its position in the list.
3266 flags
^= UPDATE_MIME_SET_NON_DEFAULT
;
3267 list
[i
++] = g_strdup (old_list
[j
]);
3272 /* add it at the end of the list */
3273 if (flags
& UPDATE_MIME_SET_NON_DEFAULT
)
3274 list
[i
++] = g_strdup (desktop_id
);
3278 g_strfreev (old_list
);
3280 if (list
[0] == NULL
|| desktop_id
== NULL
)
3281 g_key_file_remove_key (key_file
,
3282 ADDED_ASSOCIATIONS_GROUP
,
3286 g_key_file_set_string_list (key_file
,
3287 ADDED_ASSOCIATIONS_GROUP
,
3289 (const char * const *)list
, i
);
3296 /* reuse the list from above */
3300 g_strfreev (content_types
);
3301 content_types
= g_key_file_get_keys (key_file
, REMOVED_ASSOCIATIONS_GROUP
, NULL
, NULL
);
3304 for (k
= 0; content_types
&& content_types
[k
]; k
++)
3306 /* Remove from removed associations group (unless remove) */
3309 old_list
= g_key_file_get_string_list (key_file
, REMOVED_ASSOCIATIONS_GROUP
,
3310 content_types
[k
], &length
, NULL
);
3312 list
= g_new (char *, 1 + length
+ 1);
3315 if (flags
& UPDATE_MIME_REMOVE
)
3316 list
[i
++] = g_strdup (desktop_id
);
3319 for (j
= 0; old_list
[j
] != NULL
; j
++)
3321 if (g_strcmp0 (old_list
[j
], desktop_id
) != 0)
3322 list
[i
++] = g_strdup (old_list
[j
]);
3327 g_strfreev (old_list
);
3329 if (list
[0] == NULL
|| desktop_id
== NULL
)
3330 g_key_file_remove_key (key_file
,
3331 REMOVED_ASSOCIATIONS_GROUP
,
3335 g_key_file_set_string_list (key_file
,
3336 REMOVED_ASSOCIATIONS_GROUP
,
3338 (const char * const *)list
, i
);
3343 g_strfreev (content_types
);
3345 data
= g_key_file_to_data (key_file
, &data_size
, error
);
3346 g_key_file_free (key_file
);
3348 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3350 desktop_file_dirs_invalidate_user_config ();
3359 g_desktop_app_info_set_as_last_used_for_type (GAppInfo
*appinfo
,
3360 const char *content_type
,
3363 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3365 if (!g_desktop_app_info_ensure_saved (info
, error
))
3368 if (!info
->desktop_id
)
3370 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3371 _("Application information lacks an identifier"));
3375 /* both add support for the content type and set as last used */
3376 return update_mimeapps_list (info
->desktop_id
, content_type
,
3377 UPDATE_MIME_SET_NON_DEFAULT
|
3378 UPDATE_MIME_SET_LAST_USED
,
3383 g_desktop_app_info_set_as_default_for_type (GAppInfo
*appinfo
,
3384 const char *content_type
,
3387 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3389 if (!g_desktop_app_info_ensure_saved (info
, error
))
3392 if (!info
->desktop_id
)
3394 g_set_error_literal (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3395 _("Application information lacks an identifier"));
3399 return update_mimeapps_list (info
->desktop_id
, content_type
,
3400 UPDATE_MIME_SET_DEFAULT
,
3405 update_program_done (GPid pid
,
3409 /* Did the application exit correctly */
3410 if (g_spawn_check_exit_status (status
, NULL
))
3412 /* Here we could clean out any caches in use */
3417 run_update_command (char *command
,
3426 GError
*error
= NULL
;
3429 argv
[1] = g_build_filename (g_get_user_data_dir (), subdir
, NULL
);
3431 if (g_spawn_async ("/", argv
,
3433 G_SPAWN_SEARCH_PATH
|
3434 G_SPAWN_STDOUT_TO_DEV_NULL
|
3435 G_SPAWN_STDERR_TO_DEV_NULL
|
3436 G_SPAWN_DO_NOT_REAP_CHILD
,
3437 NULL
, NULL
, /* No setup function */
3440 g_child_watch_add (pid
, update_program_done
, NULL
);
3443 /* If we get an error at this point, it's quite likely the user doesn't
3444 * have an installed copy of either 'update-mime-database' or
3445 * 'update-desktop-database'. I don't think we want to popup an error
3446 * dialog at this point, so we just do a g_warning to give the user a
3447 * chance of debugging it.
3449 g_warning ("%s", error
->message
);
3450 g_error_free (error
);
3457 g_desktop_app_info_set_as_default_for_extension (GAppInfo
*appinfo
,
3458 const char *extension
,
3461 char *filename
, *basename
, *mimetype
;
3465 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo
), error
))
3468 dirname
= ensure_dir (MIMETYPE_DIR
, error
);
3472 basename
= g_strdup_printf ("user-extension-%s.xml", extension
);
3473 filename
= g_build_filename (dirname
, basename
, NULL
);
3477 mimetype
= g_strdup_printf ("application/x-extension-%s", extension
);
3479 if (!g_file_test (filename
, G_FILE_TEST_EXISTS
))
3484 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3485 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3486 " <mime-type type=\"%s\">\n"
3487 " <comment>%s document</comment>\n"
3488 " <glob pattern=\"*.%s\"/>\n"
3490 "</mime-info>\n", mimetype
, extension
, extension
);
3492 g_file_set_contents (filename
, contents
, -1, NULL
);
3495 run_update_command ("update-mime-database", "mime");
3499 res
= g_desktop_app_info_set_as_default_for_type (appinfo
,
3509 g_desktop_app_info_add_supports_type (GAppInfo
*appinfo
,
3510 const char *content_type
,
3513 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3515 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3518 return update_mimeapps_list (info
->desktop_id
, content_type
,
3519 UPDATE_MIME_SET_NON_DEFAULT
,
3524 g_desktop_app_info_can_remove_supports_type (GAppInfo
*appinfo
)
3530 g_desktop_app_info_remove_supports_type (GAppInfo
*appinfo
,
3531 const char *content_type
,
3534 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3536 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info
), error
))
3539 return update_mimeapps_list (info
->desktop_id
, content_type
,
3544 static const char **
3545 g_desktop_app_info_get_supported_types (GAppInfo
*appinfo
)
3547 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3549 return (const char**) info
->mime_types
;
3552 /* Saving and deleting {{{2 */
3555 g_desktop_app_info_ensure_saved (GDesktopAppInfo
*info
,
3561 char *data
, *desktop_id
;
3566 if (info
->filename
!= NULL
)
3569 /* This is only used for object created with
3570 * g_app_info_create_from_commandline. All other
3571 * object should have a filename
3574 dirname
= ensure_dir (APP_DIR
, error
);
3578 key_file
= g_key_file_new ();
3580 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3581 "Encoding", "UTF-8");
3582 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3583 G_KEY_FILE_DESKTOP_KEY_VERSION
, "1.0");
3584 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3585 G_KEY_FILE_DESKTOP_KEY_TYPE
,
3586 G_KEY_FILE_DESKTOP_TYPE_APPLICATION
);
3588 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3589 G_KEY_FILE_DESKTOP_KEY_TERMINAL
, TRUE
);
3590 if (info
->nodisplay
)
3591 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3592 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3594 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3595 G_KEY_FILE_DESKTOP_KEY_EXEC
, info
->exec
);
3597 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3598 G_KEY_FILE_DESKTOP_KEY_NAME
, info
->name
);
3600 if (info
->generic_name
!= NULL
)
3601 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3602 GENERIC_NAME_KEY
, info
->generic_name
);
3604 if (info
->fullname
!= NULL
)
3605 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3606 FULL_NAME_KEY
, info
->fullname
);
3608 g_key_file_set_string (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3609 G_KEY_FILE_DESKTOP_KEY_COMMENT
, info
->comment
);
3611 g_key_file_set_boolean (key_file
, G_KEY_FILE_DESKTOP_GROUP
,
3612 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY
, TRUE
);
3614 data
= g_key_file_to_data (key_file
, &data_size
, NULL
);
3615 g_key_file_free (key_file
);
3617 desktop_id
= g_strdup_printf ("userapp-%s-XXXXXX.desktop", info
->name
);
3618 filename
= g_build_filename (dirname
, desktop_id
, NULL
);
3619 g_free (desktop_id
);
3622 fd
= g_mkstemp (filename
);
3627 display_name
= g_filename_display_name (filename
);
3628 g_set_error (error
, G_IO_ERROR
, G_IO_ERROR_FAILED
,
3629 _("Can’t create user desktop file %s"), display_name
);
3630 g_free (display_name
);
3636 desktop_id
= g_path_get_basename (filename
);
3638 /* FIXME - actually handle error */
3639 (void) g_close (fd
, NULL
);
3641 res
= g_file_set_contents (filename
, data
, data_size
, error
);
3645 g_free (desktop_id
);
3650 info
->filename
= filename
;
3651 info
->desktop_id
= desktop_id
;
3653 run_update_command ("update-desktop-database", "applications");
3655 /* We just dropped a file in the user's desktop file directory. Save
3656 * the monitor the bother of having to notice it and invalidate
3659 * This means that calls directly following this will be able to see
3660 * the results immediately.
3662 desktop_file_dirs_invalidate_user_data ();
3668 g_desktop_app_info_can_delete (GAppInfo
*appinfo
)
3670 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3674 if (strstr (info
->filename
, "/userapp-"))
3675 return g_access (info
->filename
, W_OK
) == 0;
3682 g_desktop_app_info_delete (GAppInfo
*appinfo
)
3684 GDesktopAppInfo
*info
= G_DESKTOP_APP_INFO (appinfo
);
3688 if (g_remove (info
->filename
) == 0)
3690 update_mimeapps_list (info
->desktop_id
, NULL
,
3694 g_free (info
->filename
);
3695 info
->filename
= NULL
;
3696 g_free (info
->desktop_id
);
3697 info
->desktop_id
= NULL
;
3706 /* Create for commandline {{{2 */
3708 * g_app_info_create_from_commandline:
3709 * @commandline: the commandline to use
3710 * @application_name: (nullable): the application name, or %NULL to use @commandline
3711 * @flags: flags that can specify details of the created #GAppInfo
3712 * @error: a #GError location to store the error occurring, %NULL to ignore.
3714 * Creates a new #GAppInfo from the given information.
3716 * Note that for @commandline, the quoting rules of the Exec key of the
3717 * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3718 * are applied. For example, if the @commandline contains
3719 * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3720 * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
3722 * Returns: (transfer full): new #GAppInfo for given command.
3725 g_app_info_create_from_commandline (const char *commandline
,
3726 const char *application_name
,
3727 GAppInfoCreateFlags flags
,
3732 GDesktopAppInfo
*info
;
3734 g_return_val_if_fail (commandline
, NULL
);
3736 info
= g_object_new (G_TYPE_DESKTOP_APP_INFO
, NULL
);
3738 info
->filename
= NULL
;
3739 info
->desktop_id
= NULL
;
3741 info
->terminal
= (flags
& G_APP_INFO_CREATE_NEEDS_TERMINAL
) != 0;
3742 info
->startup_notify
= (flags
& G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION
) != 0;
3743 info
->hidden
= FALSE
;
3744 if ((flags
& G_APP_INFO_CREATE_SUPPORTS_URIS
) != 0)
3745 info
->exec
= g_strconcat (commandline
, " %u", NULL
);
3747 info
->exec
= g_strconcat (commandline
, " %f", NULL
);
3748 info
->nodisplay
= TRUE
;
3749 info
->binary
= binary_from_exec (info
->exec
);
3751 if (application_name
)
3752 info
->name
= g_strdup (application_name
);
3755 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
3756 split
= g_strsplit (commandline
, " ", 2);
3757 basename
= split
[0] ? g_path_get_basename (split
[0]) : NULL
;
3759 info
->name
= basename
;
3760 if (info
->name
== NULL
)
3761 info
->name
= g_strdup ("custom");
3763 info
->comment
= g_strdup_printf (_("Custom definition for %s"), info
->name
);
3765 return G_APP_INFO (info
);
3768 /* GAppInfo interface init */
3771 g_desktop_app_info_iface_init (GAppInfoIface
*iface
)
3773 iface
->dup
= g_desktop_app_info_dup
;
3774 iface
->equal
= g_desktop_app_info_equal
;
3775 iface
->get_id
= g_desktop_app_info_get_id
;
3776 iface
->get_name
= g_desktop_app_info_get_name
;
3777 iface
->get_description
= g_desktop_app_info_get_description
;
3778 iface
->get_executable
= g_desktop_app_info_get_executable
;
3779 iface
->get_icon
= g_desktop_app_info_get_icon
;
3780 iface
->launch
= g_desktop_app_info_launch
;
3781 iface
->supports_uris
= g_desktop_app_info_supports_uris
;
3782 iface
->supports_files
= g_desktop_app_info_supports_files
;
3783 iface
->launch_uris
= g_desktop_app_info_launch_uris
;
3784 iface
->should_show
= g_desktop_app_info_should_show
;
3785 iface
->set_as_default_for_type
= g_desktop_app_info_set_as_default_for_type
;
3786 iface
->set_as_default_for_extension
= g_desktop_app_info_set_as_default_for_extension
;
3787 iface
->add_supports_type
= g_desktop_app_info_add_supports_type
;
3788 iface
->can_remove_supports_type
= g_desktop_app_info_can_remove_supports_type
;
3789 iface
->remove_supports_type
= g_desktop_app_info_remove_supports_type
;
3790 iface
->can_delete
= g_desktop_app_info_can_delete
;
3791 iface
->do_delete
= g_desktop_app_info_delete
;
3792 iface
->get_commandline
= g_desktop_app_info_get_commandline
;
3793 iface
->get_display_name
= g_desktop_app_info_get_display_name
;
3794 iface
->set_as_last_used_for_type
= g_desktop_app_info_set_as_last_used_for_type
;
3795 iface
->get_supported_types
= g_desktop_app_info_get_supported_types
;
3798 /* Recommended applications {{{2 */
3800 /* Converts content_type into a list of itself with all of its parent
3801 * types (if include_fallback is enabled) or just returns a single-item
3802 * list with the unaliased content type.
3805 get_list_of_mimetypes (const gchar
*content_type
,
3806 gboolean include_fallback
)
3811 array
= g_ptr_array_new ();
3812 unaliased
= _g_unix_content_type_unalias (content_type
);
3813 g_ptr_array_add (array
, unaliased
);
3815 if (include_fallback
)
3819 /* Iterate the array as we grow it, until we have nothing more to add */
3820 for (i
= 0; i
< array
->len
; i
++)
3822 gchar
**parents
= _g_unix_content_type_get_parents (g_ptr_array_index (array
, i
));
3825 for (j
= 0; parents
[j
]; j
++)
3826 /* Don't add duplicates */
3827 if (!array_contains (array
, parents
[j
]))
3828 g_ptr_array_add (array
, parents
[j
]);
3830 g_free (parents
[j
]);
3832 /* We already stole or freed each element. Free the container. */
3837 g_ptr_array_add (array
, NULL
);
3839 return (gchar
**) g_ptr_array_free (array
, FALSE
);
3843 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar
*content_type
,
3844 gboolean include_fallback
)
3846 GPtrArray
*hits
, *blacklist
;
3850 hits
= g_ptr_array_new ();
3851 blacklist
= g_ptr_array_new ();
3853 types
= get_list_of_mimetypes (content_type
, include_fallback
);
3855 desktop_file_dirs_lock ();
3857 for (i
= 0; types
[i
]; i
++)
3858 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
3859 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], hits
, blacklist
);
3861 /* We will keep the hits past unlocking, so we must dup them */
3862 for (i
= 0; i
< hits
->len
; i
++)
3863 hits
->pdata
[i
] = g_strdup (hits
->pdata
[i
]);
3865 desktop_file_dirs_unlock ();
3867 g_ptr_array_add (hits
, NULL
);
3869 g_ptr_array_free (blacklist
, TRUE
);
3872 return (gchar
**) g_ptr_array_free (hits
, FALSE
);
3876 * g_app_info_get_recommended_for_type:
3877 * @content_type: the content type to find a #GAppInfo for
3879 * Gets a list of recommended #GAppInfos for a given content type, i.e.
3880 * those applications which claim to support the given content type exactly,
3881 * and not by MIME type subclassing.
3882 * Note that the first application of the list is the last used one, i.e.
3883 * the last one for which g_app_info_set_as_last_used_for_type() has been
3886 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3887 * for given @content_type or %NULL on error.
3892 g_app_info_get_recommended_for_type (const gchar
*content_type
)
3894 gchar
**desktop_ids
;
3898 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3900 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3903 for (i
= 0; desktop_ids
[i
]; i
++)
3905 GDesktopAppInfo
*info
;
3907 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3909 infos
= g_list_prepend (infos
, info
);
3912 g_strfreev (desktop_ids
);
3914 return g_list_reverse (infos
);
3918 * g_app_info_get_fallback_for_type:
3919 * @content_type: the content type to find a #GAppInfo for
3921 * Gets a list of fallback #GAppInfos for a given content type, i.e.
3922 * those applications which claim to support the given content type
3923 * by MIME type subclassing and not directly.
3925 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3926 * for given @content_type or %NULL on error.
3931 g_app_info_get_fallback_for_type (const gchar
*content_type
)
3933 gchar
**recommended_ids
;
3938 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3940 recommended_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, FALSE
);
3941 all_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3944 for (i
= 0; all_ids
[i
]; i
++)
3946 GDesktopAppInfo
*info
;
3949 /* Don't return the ones on the recommended list */
3950 for (j
= 0; recommended_ids
[j
]; j
++)
3951 if (g_str_equal (all_ids
[i
], recommended_ids
[j
]))
3954 if (recommended_ids
[j
])
3957 info
= g_desktop_app_info_new (all_ids
[i
]);
3960 infos
= g_list_prepend (infos
, info
);
3963 g_strfreev (recommended_ids
);
3964 g_strfreev (all_ids
);
3966 return g_list_reverse (infos
);
3970 * g_app_info_get_all_for_type:
3971 * @content_type: the content type to find a #GAppInfo for
3973 * Gets a list of all #GAppInfos for a given content type,
3974 * including the recommended and fallback #GAppInfos. See
3975 * g_app_info_get_recommended_for_type() and
3976 * g_app_info_get_fallback_for_type().
3978 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3979 * for given @content_type or %NULL on error.
3982 g_app_info_get_all_for_type (const char *content_type
)
3984 gchar
**desktop_ids
;
3988 g_return_val_if_fail (content_type
!= NULL
, NULL
);
3990 desktop_ids
= g_desktop_app_info_get_desktop_ids_for_content_type (content_type
, TRUE
);
3993 for (i
= 0; desktop_ids
[i
]; i
++)
3995 GDesktopAppInfo
*info
;
3997 info
= g_desktop_app_info_new (desktop_ids
[i
]);
3999 infos
= g_list_prepend (infos
, info
);
4002 g_strfreev (desktop_ids
);
4004 return g_list_reverse (infos
);
4008 * g_app_info_reset_type_associations:
4009 * @content_type: a content type
4011 * Removes all changes to the type associations done by
4012 * g_app_info_set_as_default_for_type(),
4013 * g_app_info_set_as_default_for_extension(),
4014 * g_app_info_add_supports_type() or
4015 * g_app_info_remove_supports_type().
4020 g_app_info_reset_type_associations (const char *content_type
)
4022 update_mimeapps_list (NULL
, content_type
,
4028 * g_app_info_get_default_for_type:
4029 * @content_type: the content type to find a #GAppInfo for
4030 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
4033 * Gets the default #GAppInfo for a given content type.
4035 * Returns: (transfer full): #GAppInfo for given @content_type or
4039 g_app_info_get_default_for_type (const char *content_type
,
4040 gboolean must_support_uris
)
4042 GPtrArray
*blacklist
;
4048 g_return_val_if_fail (content_type
!= NULL
, NULL
);
4050 types
= get_list_of_mimetypes (content_type
, TRUE
);
4052 blacklist
= g_ptr_array_new ();
4053 results
= g_ptr_array_new ();
4056 desktop_file_dirs_lock ();
4058 for (i
= 0; types
[i
]; i
++)
4060 /* Collect all the default apps for this type */
4061 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4062 desktop_file_dir_default_lookup (&desktop_file_dirs
[j
], types
[i
], results
);
4064 /* Consider the associations as well... */
4065 for (j
= 0; j
< n_desktop_file_dirs
; j
++)
4066 desktop_file_dir_mime_lookup (&desktop_file_dirs
[j
], types
[i
], results
, blacklist
);
4068 /* (If any), see if one of those apps is installed... */
4069 for (j
= 0; j
< results
->len
; j
++)
4071 const gchar
*desktop_id
= g_ptr_array_index (results
, j
);
4073 for (k
= 0; k
< n_desktop_file_dirs
; k
++)
4075 info
= (GAppInfo
*) desktop_file_dir_get_app (&desktop_file_dirs
[k
], desktop_id
);
4079 if (!must_support_uris
|| g_app_info_supports_uris (info
))
4082 g_clear_object (&info
);
4087 /* Reset the list, ready to try again with the next (parent)
4088 * mimetype, but keep the blacklist in place.
4090 g_ptr_array_set_size (results
, 0);
4094 desktop_file_dirs_unlock ();
4096 g_ptr_array_unref (blacklist
);
4097 g_ptr_array_unref (results
);
4104 * g_app_info_get_default_for_uri_scheme:
4105 * @uri_scheme: a string containing a URI scheme.
4107 * Gets the default application for handling URIs with
4108 * the given URI scheme. A URI scheme is the initial part
4109 * of the URI, up to but not including the ':', e.g. "http",
4112 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4115 g_app_info_get_default_for_uri_scheme (const char *uri_scheme
)
4118 char *content_type
, *scheme_down
;
4120 scheme_down
= g_ascii_strdown (uri_scheme
, -1);
4121 content_type
= g_strdup_printf ("x-scheme-handler/%s", scheme_down
);
4122 g_free (scheme_down
);
4123 app_info
= g_app_info_get_default_for_type (content_type
, FALSE
);
4124 g_free (content_type
);
4129 /* "Get all" API {{{2 */
4132 * g_desktop_app_info_get_implementations:
4133 * @interface: the name of the interface
4135 * Gets all applications that implement @interface.
4137 * An application implements an interface if that interface is listed in
4138 * the Implements= line of the desktop file of the application.
4140 * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo
4146 g_desktop_app_info_get_implementations (const gchar
*interface
)
4148 GList
*result
= NULL
;
4152 desktop_file_dirs_lock ();
4154 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4155 desktop_file_dir_get_implementations (&desktop_file_dirs
[i
], &result
, interface
);
4157 desktop_file_dirs_unlock ();
4162 gchar
*name
= (*ptr
)->data
;
4163 GDesktopAppInfo
*app
;
4165 app
= g_desktop_app_info_new (name
);
4171 ptr
= &(*ptr
)->next
;
4174 *ptr
= g_list_delete_link (*ptr
, *ptr
);
4181 * g_desktop_app_info_search:
4182 * @search_string: the search string to use
4184 * Searches desktop files for ones that match @search_string.
4186 * The return value is an array of strvs. Each strv contains a list of
4187 * applications that matched @search_string with an equal score. The
4188 * outer list is sorted by score so that the first strv contains the
4189 * best-matching applications, and so on.
4190 * The algorithm for determining matches is undefined and may change at
4193 * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4194 * list of strvs. Free each item with g_strfreev() and free the outer
4195 * list with g_free().
4198 g_desktop_app_info_search (const gchar
*search_string
)
4200 gchar
**search_tokens
;
4201 gint last_category
= -1;
4203 gint n_categories
= 0;
4204 gint start_of_category
;
4207 search_tokens
= g_str_tokenize_and_fold (search_string
, NULL
, NULL
);
4209 desktop_file_dirs_lock ();
4211 reset_total_search_results ();
4213 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4215 for (j
= 0; search_tokens
[j
]; j
++)
4217 desktop_file_dir_search (&desktop_file_dirs
[i
], search_tokens
[j
]);
4218 merge_token_results (j
== 0);
4220 merge_directory_results ();
4223 sort_total_search_results ();
4225 /* Count the total number of unique categories */
4226 for (i
= 0; i
< static_total_results_size
; i
++)
4227 if (static_total_results
[i
].category
!= last_category
)
4229 last_category
= static_total_results
[i
].category
;
4233 results
= g_new (gchar
**, n_categories
+ 1);
4235 /* Start loading into the results list */
4236 start_of_category
= 0;
4237 for (i
= 0; i
< n_categories
; i
++)
4239 gint n_items_in_category
= 0;
4243 this_category
= static_total_results
[start_of_category
].category
;
4245 while (start_of_category
+ n_items_in_category
< static_total_results_size
&&
4246 static_total_results
[start_of_category
+ n_items_in_category
].category
== this_category
)
4247 n_items_in_category
++;
4249 results
[i
] = g_new (gchar
*, n_items_in_category
+ 1);
4250 for (j
= 0; j
< n_items_in_category
; j
++)
4251 results
[i
][j
] = g_strdup (static_total_results
[start_of_category
+ j
].app_name
);
4252 results
[i
][j
] = NULL
;
4254 start_of_category
+= n_items_in_category
;
4258 desktop_file_dirs_unlock ();
4260 g_strfreev (search_tokens
);
4266 * g_app_info_get_all:
4268 * Gets a list of all of the applications currently registered
4271 * For desktop files, this includes applications that have
4272 * `NoDisplay=true` set or are excluded from display by means
4273 * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4274 * The returned list does not include applications which have
4275 * the `Hidden` key set.
4277 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4280 g_app_info_get_all (void)
4283 GHashTableIter iter
;
4288 apps
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
4290 desktop_file_dirs_lock ();
4292 for (i
= 0; i
< n_desktop_file_dirs
; i
++)
4293 desktop_file_dir_get_all (&desktop_file_dirs
[i
], apps
);
4295 desktop_file_dirs_unlock ();
4298 g_hash_table_iter_init (&iter
, apps
);
4299 while (g_hash_table_iter_next (&iter
, NULL
, &value
))
4302 infos
= g_list_prepend (infos
, value
);
4305 g_hash_table_destroy (apps
);
4310 /* GDesktopAppInfoLookup interface {{{2 */
4313 * GDesktopAppInfoLookup:
4315 * #GDesktopAppInfoLookup is an opaque data structure and can only be accessed
4316 * using the following functions.
4319 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4321 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface
;
4322 G_DEFINE_INTERFACE (GDesktopAppInfoLookup
, g_desktop_app_info_lookup
, G_TYPE_OBJECT
)
4325 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface
*iface
)
4329 /* "Get for mime type" APIs {{{2 */
4332 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4333 * @lookup: a #GDesktopAppInfoLookup
4334 * @uri_scheme: a string containing a URI scheme.
4336 * Gets the default application for launching applications
4337 * using this URI scheme for a particular GDesktopAppInfoLookup
4340 * The GDesktopAppInfoLookup interface and this function is used
4341 * to implement g_app_info_get_default_for_uri_scheme() backends
4342 * in a GIO module. There is no reason for applications to use it
4343 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4345 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4347 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
4350 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup
*lookup
,
4351 const char *uri_scheme
)
4353 GDesktopAppInfoLookupIface
*iface
;
4355 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup
), NULL
);
4357 iface
= G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup
);
4359 return (* iface
->get_default_for_uri_scheme
) (lookup
, uri_scheme
);
4362 G_GNUC_END_IGNORE_DEPRECATIONS
4364 /* Misc getter APIs {{{2 */
4367 * g_desktop_app_info_get_startup_wm_class:
4368 * @info: a #GDesktopAppInfo that supports startup notify
4370 * Retrieves the StartupWMClass field from @info. This represents the
4371 * WM_CLASS property of the main window of the application, if launched
4374 * Returns: (transfer none): the startup WM class, or %NULL if none is set
4375 * in the desktop file.
4380 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo
*info
)
4382 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4384 return info
->startup_wm_class
;
4388 * g_desktop_app_info_get_string:
4389 * @info: a #GDesktopAppInfo
4390 * @key: the key to look up
4392 * Looks up a string value in the keyfile backing @info.
4394 * The @key is looked up in the "Desktop Entry" group.
4396 * Returns: a newly allocated string, or %NULL if the key
4402 g_desktop_app_info_get_string (GDesktopAppInfo
*info
,
4405 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4407 return g_key_file_get_string (info
->keyfile
,
4408 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4412 * g_desktop_app_info_get_boolean:
4413 * @info: a #GDesktopAppInfo
4414 * @key: the key to look up
4416 * Looks up a boolean value in the keyfile backing @info.
4418 * The @key is looked up in the "Desktop Entry" group.
4420 * Returns: the boolean value, or %FALSE if the key
4426 g_desktop_app_info_get_boolean (GDesktopAppInfo
*info
,
4429 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4431 return g_key_file_get_boolean (info
->keyfile
,
4432 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4436 * g_desktop_app_info_has_key:
4437 * @info: a #GDesktopAppInfo
4438 * @key: the key to look up
4440 * Returns whether @key exists in the "Desktop Entry" group
4441 * of the keyfile backing @info.
4443 * Returns: %TRUE if the @key exists
4448 g_desktop_app_info_has_key (GDesktopAppInfo
*info
,
4451 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), FALSE
);
4453 return g_key_file_has_key (info
->keyfile
,
4454 G_KEY_FILE_DESKTOP_GROUP
, key
, NULL
);
4457 /* Desktop actions support {{{2 */
4460 * g_desktop_app_info_list_actions:
4461 * @info: a #GDesktopAppInfo
4463 * Returns the list of "additional application actions" supported on the
4464 * desktop file, as per the desktop file specification.
4466 * As per the specification, this is the list of actions that are
4467 * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4469 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4473 const gchar
* const *
4474 g_desktop_app_info_list_actions (GDesktopAppInfo
*info
)
4476 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4478 return (const gchar
**) info
->actions
;
4482 app_info_has_action (GDesktopAppInfo
*info
,
4483 const gchar
*action_name
)
4487 for (i
= 0; info
->actions
[i
]; i
++)
4488 if (g_str_equal (info
->actions
[i
], action_name
))
4495 * g_desktop_app_info_get_action_name:
4496 * @info: a #GDesktopAppInfo
4497 * @action_name: the name of the action as from
4498 * g_desktop_app_info_list_actions()
4500 * Gets the user-visible display name of the "additional application
4501 * action" specified by @action_name.
4503 * This corresponds to the "Name" key within the keyfile group for the
4506 * Returns: (transfer full): the locale-specific action name
4511 g_desktop_app_info_get_action_name (GDesktopAppInfo
*info
,
4512 const gchar
*action_name
)
4517 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info
), NULL
);
4518 g_return_val_if_fail (action_name
!= NULL
, NULL
);
4519 g_return_val_if_fail (app_info_has_action (info
, action_name
), NULL
);
4521 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4522 result
= g_key_file_get_locale_string (info
->keyfile
, group_name
, "Name", NULL
, NULL
);
4523 g_free (group_name
);
4525 /* The spec says that the Name field must be given.
4527 * If it's not, let's follow the behaviour of our get_name()
4528 * implementation above and never return %NULL.
4531 result
= g_strdup (_("Unnamed"));
4537 * g_desktop_app_info_launch_action:
4538 * @info: a #GDesktopAppInfo
4539 * @action_name: the name of the action as from
4540 * g_desktop_app_info_list_actions()
4541 * @launch_context: (nullable): a #GAppLaunchContext
4543 * Activates the named application action.
4545 * You may only call this function on action names that were
4546 * returned from g_desktop_app_info_list_actions().
4548 * Note that if the main entry of the desktop file indicates that the
4549 * application supports startup notification, and @launch_context is
4550 * non-%NULL, then startup notification will be used when activating the
4551 * action (and as such, invocation of the action on the receiving side
4552 * must signal the end of startup notification when it is completed).
4553 * This is the expected behaviour of applications declaring additional
4554 * actions, as per the desktop file specification.
4556 * As with g_app_info_launch() there is no way to detect failures that
4557 * occur while using this function.
4562 g_desktop_app_info_launch_action (GDesktopAppInfo
*info
,
4563 const gchar
*action_name
,
4564 GAppLaunchContext
*launch_context
)
4566 GDBusConnection
*session_bus
;
4568 g_return_if_fail (G_IS_DESKTOP_APP_INFO (info
));
4569 g_return_if_fail (action_name
!= NULL
);
4570 g_return_if_fail (app_info_has_action (info
, action_name
));
4572 session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, NULL
, NULL
);
4574 if (session_bus
&& info
->app_id
)
4578 object_path
= object_path_from_appid (info
->app_id
);
4579 g_dbus_connection_call (session_bus
, info
->app_id
, object_path
,
4580 "org.freedesktop.Application", "ActivateAction",
4581 g_variant_new ("(sav@a{sv})", action_name
, NULL
,
4582 g_desktop_app_info_make_platform_data (info
, NULL
, launch_context
)),
4583 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
4584 g_free (object_path
);
4591 group_name
= g_strdup_printf ("Desktop Action %s", action_name
);
4592 exec_line
= g_key_file_get_string (info
->keyfile
, group_name
, "Exec", NULL
);
4593 g_free (group_name
);
4596 g_desktop_app_info_launch_uris_with_spawn (info
, session_bus
, exec_line
, NULL
, launch_context
,
4597 _SPAWN_FLAGS_DEFAULT
, NULL
, NULL
, NULL
, NULL
, NULL
);
4600 if (session_bus
!= NULL
)
4602 g_dbus_connection_flush (session_bus
, NULL
, NULL
, NULL
);
4603 g_object_unref (session_bus
);
4608 /* vim:set foldmethod=marker: */