unicode: Move gscripttable.h generation into main script
[glib.git] / gio / gdesktopappinfo.c
blob1d38d4af311e082c45812b5467f56a7b38e09875
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 * Author: Alexander Larsson <alexl@redhat.com>
20 * Ryan Lortie <desrt@desrt.ca>
23 /* Prelude {{{1 */
25 #include "config.h"
27 #include <errno.h>
28 #include <string.h>
29 #include <unistd.h>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
33 #endif
35 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
37 #ifdef G_OS_UNIX
38 #include "glib-unix.h"
39 #endif
40 #include "gfile.h"
41 #include "gioerror.h"
42 #include "gthemedicon.h"
43 #include "gfileicon.h"
44 #include <glib/gstdio.h>
45 #include "glibintl.h"
46 #include "giomodule-priv.h"
47 #include "gappinfo.h"
48 #include "gappinfoprivate.h"
49 #include "glocaldirectorymonitor.h"
52 /**
53 * SECTION:gdesktopappinfo
54 * @title: GDesktopAppInfo
55 * @short_description: Application information from desktop files
56 * @include: gio/gdesktopappinfo.h
58 * #GDesktopAppInfo is an implementation of #GAppInfo based on
59 * desktop files.
61 * Note that `<gio/gdesktopappinfo.h>` belongs to the UNIX-specific
62 * GIO interfaces, thus you have to use the `gio-unix-2.0.pc` pkg-config
63 * file when using it.
66 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
67 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
68 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
69 #define MIME_CACHE_GROUP "MIME Cache"
70 #define GENERIC_NAME_KEY "GenericName"
71 #define FULL_NAME_KEY "X-GNOME-FullName"
72 #define KEYWORDS_KEY "Keywords"
73 #define STARTUP_WM_CLASS_KEY "StartupWMClass"
75 enum {
76 PROP_0,
77 PROP_FILENAME
80 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
81 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
82 GError **error);
84 /**
85 * GDesktopAppInfo:
87 * Information about an installed application from a desktop file.
89 struct _GDesktopAppInfo
91 GObject parent_instance;
93 char *desktop_id;
94 char *filename;
95 char *app_id;
97 GKeyFile *keyfile;
99 char *name;
100 char *generic_name;
101 char *fullname;
102 char *comment;
103 char *icon_name;
104 GIcon *icon;
105 char **keywords;
106 char **only_show_in;
107 char **not_show_in;
108 char *try_exec;
109 char *exec;
110 char *binary;
111 char *path;
112 char *categories;
113 char *startup_wm_class;
114 char **mime_types;
115 char **actions;
117 guint nodisplay : 1;
118 guint hidden : 1;
119 guint terminal : 1;
120 guint startup_notify : 1;
121 guint no_fuse : 1;
124 typedef enum {
125 UPDATE_MIME_NONE = 1 << 0,
126 UPDATE_MIME_SET_DEFAULT = 1 << 1,
127 UPDATE_MIME_SET_NON_DEFAULT = 1 << 2,
128 UPDATE_MIME_REMOVE = 1 << 3,
129 UPDATE_MIME_SET_LAST_USED = 1 << 4,
130 } UpdateMimeFlags;
132 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
133 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO, g_desktop_app_info_iface_init))
135 /* DesktopFileDir implementation {{{1 */
137 typedef struct
139 gchar *path;
140 gboolean is_config;
141 gboolean is_setup;
142 GLocalDirectoryMonitor *monitor;
143 GHashTable *app_names;
144 GHashTable *mime_tweaks;
145 GHashTable *memory_index;
146 GHashTable *memory_implementations;
147 } DesktopFileDir;
149 static DesktopFileDir *desktop_file_dirs;
150 static guint n_desktop_file_dirs;
151 static const guint desktop_file_dir_user_config_index = 0;
152 static guint desktop_file_dir_user_data_index;
153 static GMutex desktop_file_dir_lock;
155 /* Monitor 'changed' signal handler {{{2 */
156 static void desktop_file_dir_reset (DesktopFileDir *dir);
158 static void
159 desktop_file_dir_changed (GFileMonitor *monitor,
160 GFile *file,
161 GFile *other_file,
162 GFileMonitorEvent event_type,
163 gpointer user_data)
165 DesktopFileDir *dir = user_data;
167 /* We are not interested in receiving notifications forever just
168 * because someone asked about one desktop file once.
170 * After we receive the first notification, reset the dir, destroying
171 * the monitor. We will take this as a hint, next time that we are
172 * asked, that we need to check if everything is up to date.
174 g_mutex_lock (&desktop_file_dir_lock);
176 desktop_file_dir_reset (dir);
178 g_mutex_unlock (&desktop_file_dir_lock);
180 /* Notify anyone else who may be interested */
181 g_app_info_monitor_fire ();
184 /* Internal utility functions {{{2 */
186 /*< internal >
187 * desktop_file_dir_app_name_is_masked:
188 * @dir: a #DesktopFileDir
189 * @app_name: an application ID
191 * Checks if @app_name is masked for @dir.
193 * An application is masked if a similarly-named desktop file exists in
194 * a desktop file directory with higher precedence. Masked desktop
195 * files should be ignored.
197 static gboolean
198 desktop_file_dir_app_name_is_masked (DesktopFileDir *dir,
199 const gchar *app_name)
201 while (dir > desktop_file_dirs)
203 dir--;
205 if (dir->app_names && g_hash_table_contains (dir->app_names, app_name))
206 return TRUE;
209 return FALSE;
212 static const gchar * const *
213 get_lowercase_current_desktops (void)
215 static gchar **result;
217 if (g_once_init_enter (&result))
219 const gchar *envvar;
220 gchar **tmp;
222 envvar = g_getenv ("XDG_CURRENT_DESKTOP");
224 if (envvar)
226 gint i, j;
228 tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0);
230 for (i = 0; tmp[i]; i++)
231 for (j = 0; tmp[i][j]; j++)
232 tmp[i][j] = g_ascii_tolower (tmp[i][j]);
234 else
235 tmp = g_new0 (gchar *, 0 + 1);
237 g_once_init_leave (&result, tmp);
240 return (const gchar **) result;
243 static const gchar * const *
244 get_current_desktops (const gchar *value)
246 static gchar **result;
248 if (g_once_init_enter (&result))
250 gchar **tmp;
252 if (!value)
253 value = g_getenv ("XDG_CURRENT_DESKTOP");
255 if (!value)
256 value = "";
258 tmp = g_strsplit (value, ":", 0);
260 g_once_init_leave (&result, tmp);
263 return (const gchar **) result;
266 /*< internal >
267 * add_to_table_if_appropriate:
268 * @apps: a string to GDesktopAppInfo hash table
269 * @app_name: the name of the application
270 * @info: a #GDesktopAppInfo, or NULL
272 * If @info is non-%NULL and non-hidden, then add it to @apps, using
273 * @app_name as a key.
275 * If @info is non-%NULL then this function will consume the passed-in
276 * reference.
278 static void
279 add_to_table_if_appropriate (GHashTable *apps,
280 const gchar *app_name,
281 GDesktopAppInfo *info)
283 if (!info)
284 return;
286 if (info->hidden)
288 g_object_unref (info);
289 return;
292 g_free (info->desktop_id);
293 info->desktop_id = g_strdup (app_name);
295 g_hash_table_insert (apps, g_strdup (info->desktop_id), info);
298 enum
300 DESKTOP_KEY_Comment,
301 DESKTOP_KEY_Exec,
302 DESKTOP_KEY_GenericName,
303 DESKTOP_KEY_Keywords,
304 DESKTOP_KEY_Name,
305 DESKTOP_KEY_X_GNOME_FullName,
307 N_DESKTOP_KEYS
310 const gchar desktop_key_match_category[N_DESKTOP_KEYS] = {
311 /* Note: lower numbers are a better match.
313 * In case we want two keys to match at the same level, we can just
314 * use the same number for the two different keys.
316 [DESKTOP_KEY_Name] = 1,
317 [DESKTOP_KEY_Exec] = 2,
318 [DESKTOP_KEY_Keywords] = 3,
319 [DESKTOP_KEY_GenericName] = 4,
320 [DESKTOP_KEY_X_GNOME_FullName] = 5,
321 [DESKTOP_KEY_Comment] = 6
324 static gchar *
325 desktop_key_get_name (guint key_id)
327 switch (key_id)
329 case DESKTOP_KEY_Comment:
330 return "Comment";
331 case DESKTOP_KEY_Exec:
332 return "Exec";
333 case DESKTOP_KEY_GenericName:
334 return "GenericName";
335 case DESKTOP_KEY_Keywords:
336 return "Keywords";
337 case DESKTOP_KEY_Name:
338 return "Name";
339 case DESKTOP_KEY_X_GNOME_FullName:
340 return "X-GNOME-FullName";
341 default:
342 g_assert_not_reached ();
346 /* Search global state {{{2
348 * We only ever search under a global lock, so we can use (and reuse)
349 * some global data to reduce allocations made while searching.
351 * In short, we keep around arrays of results that we expand as needed
352 * (and never shrink).
354 * static_token_results: this is where we append the results for each
355 * token within a given desktop directory, as we handle it (which is
356 * a union of all matches for this term)
358 * static_search_results: this is where we build the complete results
359 * for a single directory (which is an intersection of the matches
360 * found for each term)
362 * static_total_results: this is where we build the complete results
363 * across all directories (which is a union of the matches found in
364 * each directory)
366 * The app_names that enter these tables are always pointer-unique (in
367 * the sense that string equality is the same as pointer equality).
368 * This can be guaranteed for two reasons:
370 * - we mask appids so that a given appid will only ever appear within
371 * the highest-precedence directory that contains it. We never
372 * return search results from a lower-level directory if a desktop
373 * file exists in a higher-level one.
375 * - within a given directory, the string is unique because it's the
376 * key in the hashtable of all app_ids for that directory.
378 * We perform a merging of the results in merge_token_results(). This
379 * works by ordering the two lists and moving through each of them (at
380 * the same time) looking for common elements, rejecting uncommon ones.
381 * "Order" here need not mean any particular thing, as long as it is
382 * some order. Because of the uniqueness of our strings, we can use
383 * pointer order. That's what's going on in compare_results() below.
385 struct search_result
387 const gchar *app_name;
388 gint category;
391 static struct search_result *static_token_results;
392 static gint static_token_results_size;
393 static gint static_token_results_allocated;
394 static struct search_result *static_search_results;
395 static gint static_search_results_size;
396 static gint static_search_results_allocated;
397 static struct search_result *static_total_results;
398 static gint static_total_results_size;
399 static gint static_total_results_allocated;
401 /* And some functions for performing nice operations against it */
402 static gint
403 compare_results (gconstpointer a,
404 gconstpointer b)
406 const struct search_result *ra = a;
407 const struct search_result *rb = b;
409 if (ra->app_name < rb->app_name)
410 return -1;
412 else if (ra->app_name > rb->app_name)
413 return 1;
415 else
416 return ra->category - rb->category;
419 static gint
420 compare_categories (gconstpointer a,
421 gconstpointer b)
423 const struct search_result *ra = a;
424 const struct search_result *rb = b;
426 return ra->category - rb->category;
429 static void
430 add_token_result (const gchar *app_name,
431 guint16 category)
433 if G_UNLIKELY (static_token_results_size == static_token_results_allocated)
435 static_token_results_allocated = MAX (16, static_token_results_allocated * 2);
436 static_token_results = g_renew (struct search_result, static_token_results, static_token_results_allocated);
439 static_token_results[static_token_results_size].app_name = app_name;
440 static_token_results[static_token_results_size].category = category;
441 static_token_results_size++;
444 static void
445 merge_token_results (gboolean first)
447 qsort (static_token_results, static_token_results_size, sizeof (struct search_result), compare_results);
449 /* If this is the first token then we are basically merging a list with
450 * itself -- we only perform de-duplication.
452 * If this is not the first token then we are doing a real merge.
454 if (first)
456 const gchar *last_name = NULL;
457 gint i;
459 /* We must de-duplicate, but we do so by taking the best category
460 * in each case.
462 * The final list can be as large as the input here, so make sure
463 * we have enough room (even if it's too much room).
466 if G_UNLIKELY (static_search_results_allocated < static_token_results_size)
468 static_search_results_allocated = static_token_results_allocated;
469 static_search_results = g_renew (struct search_result,
470 static_search_results,
471 static_search_results_allocated);
474 for (i = 0; i < static_token_results_size; i++)
476 /* The list is sorted so that the best match for a given id
477 * will be at the front, so once we have copied an id, skip
478 * the rest of the entries for the same id.
480 if (static_token_results[i].app_name == last_name)
481 continue;
483 last_name = static_token_results[i].app_name;
485 static_search_results[static_search_results_size++] = static_token_results[i];
488 else
490 const gchar *last_name = NULL;
491 gint i, j = 0;
492 gint k = 0;
494 /* We only ever remove items from the results list, so no need to
495 * resize to ensure that we have enough room.
497 for (i = 0; i < static_token_results_size; i++)
499 if (static_token_results[i].app_name == last_name)
500 continue;
502 last_name = static_token_results[i].app_name;
504 /* Now we only want to have a result in static_search_results
505 * if we already have it there *and* we have it in
506 * static_token_results as well. The category will be the
507 * lesser of the two.
509 * Skip past the results in static_search_results that are not
510 * going to be matches.
512 while (k < static_search_results_size &&
513 static_search_results[k].app_name < static_token_results[i].app_name)
514 k++;
516 if (k < static_search_results_size &&
517 static_search_results[k].app_name == static_token_results[i].app_name)
519 /* We have a match.
521 * Category should be the worse of the two (ie:
522 * numerically larger).
524 static_search_results[j].app_name = static_search_results[k].app_name;
525 static_search_results[j].category = MAX (static_search_results[k].category,
526 static_token_results[i].category);
527 j++;
531 static_search_results_size = j;
534 /* Clear it out for next time... */
535 static_token_results_size = 0;
538 static void
539 reset_total_search_results (void)
541 static_total_results_size = 0;
544 static void
545 sort_total_search_results (void)
547 qsort (static_total_results, static_total_results_size, sizeof (struct search_result), compare_categories);
550 static void
551 merge_directory_results (void)
553 if G_UNLIKELY (static_total_results_size + static_search_results_size > static_total_results_allocated)
555 static_total_results_allocated = MAX (16, static_total_results_allocated);
556 while (static_total_results_allocated < static_total_results_size + static_search_results_size)
557 static_total_results_allocated *= 2;
558 static_total_results = g_renew (struct search_result, static_total_results, static_total_results_allocated);
561 memcpy (static_total_results + static_total_results_size,
562 static_search_results,
563 static_search_results_size * sizeof (struct search_result));
565 static_total_results_size += static_search_results_size;
567 /* Clear it out for next time... */
568 static_search_results_size = 0;
571 /* Support for unindexed DesktopFileDirs {{{2 */
572 static void
573 get_apps_from_dir (GHashTable **apps,
574 const char *dirname,
575 const char *prefix)
577 const char *basename;
578 GDir *dir;
580 dir = g_dir_open (dirname, 0, NULL);
582 if (dir == NULL)
583 return;
585 while ((basename = g_dir_read_name (dir)) != NULL)
587 gchar *filename;
589 filename = g_build_filename (dirname, basename, NULL);
591 if (g_str_has_suffix (basename, ".desktop"))
593 gchar *app_name;
595 app_name = g_strconcat (prefix, basename, NULL);
597 if (*apps == NULL)
598 *apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
600 g_hash_table_insert (*apps, app_name, g_strdup (filename));
602 else if (g_file_test (filename, G_FILE_TEST_IS_DIR))
604 gchar *subprefix;
606 subprefix = g_strconcat (prefix, basename, "-", NULL);
607 get_apps_from_dir (apps, filename, subprefix);
608 g_free (subprefix);
611 g_free (filename);
614 g_dir_close (dir);
617 typedef struct
619 gchar **additions;
620 gchar **removals;
621 gchar **defaults;
622 } UnindexedMimeTweaks;
624 static void
625 free_mime_tweaks (gpointer data)
627 UnindexedMimeTweaks *tweaks = data;
629 g_strfreev (tweaks->additions);
630 g_strfreev (tweaks->removals);
631 g_strfreev (tweaks->defaults);
633 g_slice_free (UnindexedMimeTweaks, tweaks);
636 static UnindexedMimeTweaks *
637 desktop_file_dir_unindexed_get_tweaks (DesktopFileDir *dir,
638 const gchar *mime_type)
640 UnindexedMimeTweaks *tweaks;
641 gchar *unaliased_type;
643 unaliased_type = _g_unix_content_type_unalias (mime_type);
644 tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type);
646 if (tweaks == NULL)
648 tweaks = g_slice_new0 (UnindexedMimeTweaks);
649 g_hash_table_insert (dir->mime_tweaks, unaliased_type, tweaks);
651 else
652 g_free (unaliased_type);
654 return tweaks;
657 /* consumes 'to_add' */
658 static void
659 expand_strv (gchar ***strv_ptr,
660 gchar **to_add,
661 gchar * const *blacklist)
663 guint strv_len, add_len;
664 gchar **strv;
665 guint i, j;
667 if (!*strv_ptr)
669 *strv_ptr = to_add;
670 return;
673 strv = *strv_ptr;
674 strv_len = g_strv_length (strv);
675 add_len = g_strv_length (to_add);
676 strv = g_renew (gchar *, strv, strv_len + add_len + 1);
678 for (i = 0; to_add[i]; i++)
680 /* Don't add blacklisted strings */
681 if (blacklist)
682 for (j = 0; blacklist[j]; j++)
683 if (g_str_equal (to_add[i], blacklist[j]))
684 goto no_add;
686 /* Don't add duplicates already in the list */
687 for (j = 0; j < strv_len; j++)
688 if (g_str_equal (to_add[i], strv[j]))
689 goto no_add;
691 strv[strv_len++] = to_add[i];
692 continue;
694 no_add:
695 g_free (to_add[i]);
698 strv[strv_len] = NULL;
699 *strv_ptr = strv;
701 g_free (to_add);
704 static void
705 desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir,
706 const gchar *filename,
707 const gchar *added_group,
708 gboolean tweaks_permitted)
710 const gchar default_group[] = "Default Applications";
711 const gchar removed_group[] = "Removed Assocations";
712 UnindexedMimeTweaks *tweaks;
713 char **desktop_file_ids;
714 GKeyFile *key_file;
715 gchar **mime_types;
716 int i;
718 key_file = g_key_file_new ();
719 if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL))
721 g_key_file_free (key_file);
722 return;
725 mime_types = g_key_file_get_keys (key_file, added_group, NULL, NULL);
727 if G_UNLIKELY (mime_types != NULL && !tweaks_permitted)
729 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
730 "mimeapps.list file may add or remove associations.", filename, added_group);
731 g_strfreev (mime_types);
732 mime_types = NULL;
735 if (mime_types != NULL)
737 for (i = 0; mime_types[i] != NULL; i++)
739 desktop_file_ids = g_key_file_get_string_list (key_file, added_group, mime_types[i], NULL, NULL);
741 if (desktop_file_ids)
743 tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
744 expand_strv (&tweaks->additions, desktop_file_ids, tweaks->removals);
748 g_strfreev (mime_types);
751 mime_types = g_key_file_get_keys (key_file, removed_group, NULL, NULL);
753 if G_UNLIKELY (mime_types != NULL && !tweaks_permitted)
755 g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific "
756 "mimeapps.list file may add or remove associations.", filename, removed_group);
757 g_strfreev (mime_types);
758 mime_types = NULL;
761 if (mime_types != NULL)
763 for (i = 0; mime_types[i] != NULL; i++)
765 desktop_file_ids = g_key_file_get_string_list (key_file, removed_group, mime_types[i], NULL, NULL);
767 if (desktop_file_ids)
769 tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
770 expand_strv (&tweaks->removals, desktop_file_ids, tweaks->additions);
774 g_strfreev (mime_types);
777 mime_types = g_key_file_get_keys (key_file, default_group, NULL, NULL);
779 if (mime_types != NULL)
781 for (i = 0; mime_types[i] != NULL; i++)
783 desktop_file_ids = g_key_file_get_string_list (key_file, default_group, mime_types[i], NULL, NULL);
785 if (desktop_file_ids)
787 tweaks = desktop_file_dir_unindexed_get_tweaks (dir, mime_types[i]);
788 expand_strv (&tweaks->defaults, desktop_file_ids, NULL);
792 g_strfreev (mime_types);
795 g_key_file_free (key_file);
798 static void
799 desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir *dir)
801 const gchar * const *desktops;
802 gchar *filename;
803 gint i;
805 dir->mime_tweaks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_mime_tweaks);
807 /* We process in order of precedence, using a blacklisting approach to
808 * avoid recording later instructions that conflict with ones we found
809 * earlier.
811 * We first start with the XDG_CURRENT_DESKTOP files, in precedence
812 * order.
814 desktops = get_lowercase_current_desktops ();
815 for (i = 0; desktops[i]; i++)
817 filename = g_strdup_printf ("%s/%s-mimeapps.list", dir->path, desktops[i]);
818 desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", FALSE);
819 g_free (filename);
822 /* Next, the non-desktop-specific mimeapps.list */
823 filename = g_strdup_printf ("%s/mimeapps.list", dir->path);
824 desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", TRUE);
825 g_free (filename);
827 /* The remaining files are only checked for in directories that might
828 * contain desktop files (ie: not the config dirs).
830 if (dir->is_config)
831 return;
833 /* We have 'defaults.list' which was only ever understood by GLib. It
834 * exists widely, but it has never been part of any spec and it should
835 * be treated as deprecated. This will be removed in a future
836 * version.
838 filename = g_strdup_printf ("%s/defaults.list", dir->path);
839 desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", FALSE);
840 g_free (filename);
842 /* Finally, the mimeinfo.cache, which is just a cached copy of what we
843 * would find in the MimeTypes= lines of all of the desktop files.
845 filename = g_strdup_printf ("%s/mimeinfo.cache", dir->path);
846 desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "MIME Cache", TRUE);
847 g_free (filename);
850 static void
851 desktop_file_dir_unindexed_init (DesktopFileDir *dir)
853 if (!dir->is_config)
854 get_apps_from_dir (&dir->app_names, dir->path, "");
856 desktop_file_dir_unindexed_read_mimeapps_lists (dir);
859 static GDesktopAppInfo *
860 desktop_file_dir_unindexed_get_app (DesktopFileDir *dir,
861 const gchar *desktop_id)
863 const gchar *filename;
865 filename = g_hash_table_lookup (dir->app_names, desktop_id);
867 if (!filename)
868 return NULL;
870 return g_desktop_app_info_new_from_filename (filename);
873 static void
874 desktop_file_dir_unindexed_get_all (DesktopFileDir *dir,
875 GHashTable *apps)
877 GHashTableIter iter;
878 gpointer app_name;
879 gpointer filename;
881 if (dir->app_names == NULL)
882 return;
884 g_hash_table_iter_init (&iter, dir->app_names);
885 while (g_hash_table_iter_next (&iter, &app_name, &filename))
887 if (desktop_file_dir_app_name_is_masked (dir, app_name))
888 continue;
890 add_to_table_if_appropriate (apps, app_name, g_desktop_app_info_new_from_filename (filename));
894 typedef struct _MemoryIndexEntry MemoryIndexEntry;
895 typedef GHashTable MemoryIndex;
897 struct _MemoryIndexEntry
899 const gchar *app_name; /* pointer to the hashtable key */
900 gint match_category;
901 MemoryIndexEntry *next;
904 static void
905 memory_index_entry_free (gpointer data)
907 MemoryIndexEntry *mie = data;
909 while (mie)
911 MemoryIndexEntry *next = mie->next;
913 g_slice_free (MemoryIndexEntry, mie);
914 mie = next;
918 static void
919 memory_index_add_token (MemoryIndex *mi,
920 const gchar *token,
921 gint match_category,
922 const gchar *app_name)
924 MemoryIndexEntry *mie, *first;
926 mie = g_slice_new (MemoryIndexEntry);
927 mie->app_name = app_name;
928 mie->match_category = match_category;
930 first = g_hash_table_lookup (mi, token);
932 if (first)
934 mie->next = first->next;
935 first->next = mie;
937 else
939 mie->next = NULL;
940 g_hash_table_insert (mi, g_strdup (token), mie);
944 static void
945 memory_index_add_string (MemoryIndex *mi,
946 const gchar *string,
947 gint match_category,
948 const gchar *app_name)
950 gchar **tokens, **alternates;
951 gint i;
953 tokens = g_str_tokenize_and_fold (string, NULL, &alternates);
955 for (i = 0; tokens[i]; i++)
956 memory_index_add_token (mi, tokens[i], match_category, app_name);
958 for (i = 0; alternates[i]; i++)
959 memory_index_add_token (mi, alternates[i], match_category, app_name);
961 g_strfreev (alternates);
962 g_strfreev (tokens);
965 static MemoryIndex *
966 memory_index_new (void)
968 return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, memory_index_entry_free);
971 static void
972 desktop_file_dir_unindexed_setup_search (DesktopFileDir *dir)
974 GHashTableIter iter;
975 gpointer app, path;
977 dir->memory_index = memory_index_new ();
978 dir->memory_implementations = memory_index_new ();
980 /* Nothing to search? */
981 if (dir->app_names == NULL)
982 return;
984 g_hash_table_iter_init (&iter, dir->app_names);
985 while (g_hash_table_iter_next (&iter, &app, &path))
987 GKeyFile *key_file;
989 if (desktop_file_dir_app_name_is_masked (dir, app))
990 continue;
992 key_file = g_key_file_new ();
994 if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL) &&
995 !g_key_file_get_boolean (key_file, "Desktop Entry", "Hidden", NULL))
997 /* Index the interesting keys... */
998 gchar **implements;
999 gint i;
1001 for (i = 0; i < G_N_ELEMENTS (desktop_key_match_category); i++)
1003 const gchar *value;
1004 gchar *raw;
1006 if (!desktop_key_match_category[i])
1007 continue;
1009 raw = g_key_file_get_locale_string (key_file, "Desktop Entry", desktop_key_get_name (i), NULL, NULL);
1010 value = raw;
1012 if (i == DESKTOP_KEY_Exec && raw != NULL)
1014 /* Special handling: only match basename of first field */
1015 gchar *space;
1016 gchar *slash;
1018 /* Remove extra arguments, if any */
1019 space = raw + strcspn (raw, " \t\n"); /* IFS */
1020 *space = '\0';
1022 /* Skip the pathname, if any */
1023 if ((slash = strrchr (raw, '/')))
1024 value = slash + 1;
1027 if (value)
1028 memory_index_add_string (dir->memory_index, value, desktop_key_match_category[i], app);
1030 g_free (raw);
1033 /* Make note of the Implements= line */
1034 implements = g_key_file_get_string_list (key_file, "Desktop Entry", "Implements", NULL, NULL);
1035 for (i = 0; implements && implements[i]; i++)
1036 memory_index_add_token (dir->memory_implementations, implements[i], 0, app);
1037 g_strfreev (implements);
1040 g_key_file_free (key_file);
1044 static void
1045 desktop_file_dir_unindexed_search (DesktopFileDir *dir,
1046 const gchar *search_token)
1048 GHashTableIter iter;
1049 gpointer key, value;
1051 if (!dir->memory_index)
1052 desktop_file_dir_unindexed_setup_search (dir);
1054 g_hash_table_iter_init (&iter, dir->memory_index);
1055 while (g_hash_table_iter_next (&iter, &key, &value))
1057 MemoryIndexEntry *mie = value;
1059 if (!g_str_has_prefix (key, search_token))
1060 continue;
1062 while (mie)
1064 add_token_result (mie->app_name, mie->match_category);
1065 mie = mie->next;
1070 static gboolean
1071 array_contains (GPtrArray *array,
1072 const gchar *str)
1074 gint i;
1076 for (i = 0; i < array->len; i++)
1077 if (g_str_equal (array->pdata[i], str))
1078 return TRUE;
1080 return FALSE;
1083 static void
1084 desktop_file_dir_unindexed_mime_lookup (DesktopFileDir *dir,
1085 const gchar *mime_type,
1086 GPtrArray *hits,
1087 GPtrArray *blacklist)
1089 UnindexedMimeTweaks *tweaks;
1090 gint i;
1092 tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type);
1094 if (!tweaks)
1095 return;
1097 if (tweaks->additions)
1099 for (i = 0; tweaks->additions[i]; i++)
1101 gchar *app_name = tweaks->additions[i];
1103 if (!desktop_file_dir_app_name_is_masked (dir, app_name) &&
1104 !array_contains (blacklist, app_name) && !array_contains (hits, app_name))
1105 g_ptr_array_add (hits, g_strdup (app_name));
1109 if (tweaks->removals)
1111 for (i = 0; tweaks->removals[i]; i++)
1113 gchar *app_name = tweaks->removals[i];
1115 if (!desktop_file_dir_app_name_is_masked (dir, app_name) &&
1116 !array_contains (blacklist, app_name) && !array_contains (hits, app_name))
1117 g_ptr_array_add (blacklist, app_name);
1122 static void
1123 desktop_file_dir_unindexed_default_lookup (DesktopFileDir *dir,
1124 const gchar *mime_type,
1125 GPtrArray *results)
1127 UnindexedMimeTweaks *tweaks;
1128 gint i;
1130 tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type);
1132 if (!tweaks || !tweaks->defaults)
1133 return;
1135 for (i = 0; tweaks->defaults[i]; i++)
1137 gchar *app_name = tweaks->defaults[i];
1139 if (!array_contains (results, app_name))
1140 g_ptr_array_add (results, g_strdup (app_name));
1144 static void
1145 desktop_file_dir_unindexed_get_implementations (DesktopFileDir *dir,
1146 GList **results,
1147 const gchar *interface)
1149 MemoryIndexEntry *mie;
1151 if (!dir->memory_index)
1152 desktop_file_dir_unindexed_setup_search (dir);
1154 for (mie = g_hash_table_lookup (dir->memory_implementations, interface); mie; mie = mie->next)
1155 *results = g_list_prepend (*results, g_strdup (mie->app_name));
1158 /* DesktopFileDir "API" {{{2 */
1160 /*< internal >
1161 * desktop_file_dir_create:
1162 * @array: the #GArray to add a new item to
1163 * @data_dir: an XDG_DATA_DIR
1165 * Creates a #DesktopFileDir for the corresponding @data_dir, adding it
1166 * to @array.
1168 static void
1169 desktop_file_dir_create (GArray *array,
1170 const gchar *data_dir)
1172 DesktopFileDir dir = { 0, };
1174 dir.path = g_build_filename (data_dir, "applications", NULL);
1176 g_array_append_val (array, dir);
1179 /*< internal >
1180 * desktop_file_dir_create:
1181 * @array: the #GArray to add a new item to
1182 * @config_dir: an XDG_CONFIG_DIR
1184 * Just the same as desktop_file_dir_create() except that it does not
1185 * add the "applications" directory. It also marks the directory as
1186 * config-only, which prevents us from attempting to find desktop files
1187 * here.
1189 static void
1190 desktop_file_dir_create_for_config (GArray *array,
1191 const gchar *config_dir)
1193 DesktopFileDir dir = { 0, };
1195 dir.path = g_strdup (config_dir);
1196 dir.is_config = TRUE;
1198 g_array_append_val (array, dir);
1201 /*< internal >
1202 * desktop_file_dir_reset:
1203 * @dir: a #DesktopFileDir
1205 * Cleans up @dir, releasing most resources that it was using.
1207 static void
1208 desktop_file_dir_reset (DesktopFileDir *dir)
1210 if (dir->monitor)
1212 g_signal_handlers_disconnect_by_func (dir->monitor, desktop_file_dir_changed, dir);
1213 g_object_unref (dir->monitor);
1214 dir->monitor = NULL;
1217 if (dir->app_names)
1219 g_hash_table_unref (dir->app_names);
1220 dir->app_names = NULL;
1223 if (dir->memory_index)
1225 g_hash_table_unref (dir->memory_index);
1226 dir->memory_index = NULL;
1229 if (dir->mime_tweaks)
1231 g_hash_table_unref (dir->mime_tweaks);
1232 dir->mime_tweaks = NULL;
1235 if (dir->memory_implementations)
1237 g_hash_table_unref (dir->memory_implementations);
1238 dir->memory_implementations = NULL;
1241 dir->is_setup = FALSE;
1244 /*< internal >
1245 * desktop_file_dir_init:
1246 * @dir: a #DesktopFileDir
1248 * Does initial setup for @dir
1250 * You should only call this if @dir is not already setup.
1252 static void
1253 desktop_file_dir_init (DesktopFileDir *dir)
1255 g_assert (!dir->is_setup);
1257 g_assert (!dir->monitor);
1258 dir->monitor = g_local_directory_monitor_new_in_worker (dir->path, G_FILE_MONITOR_NONE, NULL);
1260 if (dir->monitor)
1262 g_signal_connect (dir->monitor, "changed", G_CALLBACK (desktop_file_dir_changed), dir);
1263 g_local_directory_monitor_start (dir->monitor);
1266 desktop_file_dir_unindexed_init (dir);
1268 dir->is_setup = TRUE;
1271 /*< internal >
1272 * desktop_file_dir_get_app:
1273 * @dir: a DesktopFileDir
1274 * @desktop_id: the desktop ID to load
1276 * Creates the #GDesktopAppInfo for the given @desktop_id if it exists
1277 * within @dir, even if it is hidden.
1279 * This function does not check if @desktop_id would be masked by a
1280 * directory with higher precedence. The caller must do so.
1282 static GDesktopAppInfo *
1283 desktop_file_dir_get_app (DesktopFileDir *dir,
1284 const gchar *desktop_id)
1286 if (!dir->app_names)
1287 return NULL;
1289 return desktop_file_dir_unindexed_get_app (dir, desktop_id);
1292 /*< internal >
1293 * desktop_file_dir_get_all:
1294 * @dir: a DesktopFileDir
1295 * @apps: a #GHashTable<string, GDesktopAppInfo>
1297 * Loads all desktop files in @dir and adds them to @apps, careful to
1298 * ensure we don't add any files masked by a similarly-named file in a
1299 * higher-precedence directory.
1301 static void
1302 desktop_file_dir_get_all (DesktopFileDir *dir,
1303 GHashTable *apps)
1305 desktop_file_dir_unindexed_get_all (dir, apps);
1308 /*< internal >
1309 * desktop_file_dir_mime_lookup:
1310 * @dir: a #DesktopFileDir
1311 * @mime_type: the mime type to look up
1312 * @hits: the array to store the hits
1313 * @blacklist: the array to store the blacklist
1315 * Does a lookup of a mimetype against one desktop file directory,
1316 * recording any hits and blacklisting and "Removed" associations (so
1317 * later directories don't record them as hits).
1319 * The items added to @hits are duplicated, but the ones in @blacklist
1320 * are weak pointers. This facilitates simply freeing the blacklist
1321 * (which is only used for internal bookkeeping) but using the pdata of
1322 * @hits as the result of the operation.
1324 static void
1325 desktop_file_dir_mime_lookup (DesktopFileDir *dir,
1326 const gchar *mime_type,
1327 GPtrArray *hits,
1328 GPtrArray *blacklist)
1330 desktop_file_dir_unindexed_mime_lookup (dir, mime_type, hits, blacklist);
1333 /*< internal >
1334 * desktop_file_dir_default_lookup:
1335 * @dir: a #DesktopFileDir
1336 * @mime_type: the mime type to look up
1337 * @results: an array to store the results in
1339 * Collects the "default" applications for a given mime type from @dir.
1341 static void
1342 desktop_file_dir_default_lookup (DesktopFileDir *dir,
1343 const gchar *mime_type,
1344 GPtrArray *results)
1346 desktop_file_dir_unindexed_default_lookup (dir, mime_type, results);
1349 /*< internal >
1350 * desktop_file_dir_search:
1351 * @dir: a #DesktopFileDir
1352 * @term: a normalised and casefolded search term
1354 * Finds the names of applications in @dir that match @term.
1356 static void
1357 desktop_file_dir_search (DesktopFileDir *dir,
1358 const gchar *search_token)
1360 desktop_file_dir_unindexed_search (dir, search_token);
1363 static void
1364 desktop_file_dir_get_implementations (DesktopFileDir *dir,
1365 GList **results,
1366 const gchar *interface)
1368 desktop_file_dir_unindexed_get_implementations (dir, results, interface);
1371 /* Lock/unlock and global setup API {{{2 */
1373 static void
1374 desktop_file_dirs_lock (void)
1376 gint i;
1378 g_mutex_lock (&desktop_file_dir_lock);
1380 if (desktop_file_dirs == NULL)
1382 const char * const *dirs;
1383 GArray *tmp;
1384 gint i;
1386 tmp = g_array_new (FALSE, FALSE, sizeof (DesktopFileDir));
1388 /* First, the configs. Highest priority: the user's ~/.config */
1389 desktop_file_dir_create_for_config (tmp, g_get_user_config_dir ());
1391 /* Next, the system configs (/etc/xdg, and so on). */
1392 dirs = g_get_system_config_dirs ();
1393 for (i = 0; dirs[i]; i++)
1394 desktop_file_dir_create_for_config (tmp, dirs[i]);
1396 /* Now the data. Highest priority: the user's ~/.local/share/applications */
1397 desktop_file_dir_user_data_index = tmp->len;
1398 desktop_file_dir_create (tmp, g_get_user_data_dir ());
1400 /* Following that, XDG_DATA_DIRS/applications, in order */
1401 dirs = g_get_system_data_dirs ();
1402 for (i = 0; dirs[i]; i++)
1403 desktop_file_dir_create (tmp, dirs[i]);
1405 /* The list of directories will never change after this. */
1406 desktop_file_dirs = (DesktopFileDir *) tmp->data;
1407 n_desktop_file_dirs = tmp->len;
1409 g_array_free (tmp, FALSE);
1412 for (i = 0; i < n_desktop_file_dirs; i++)
1413 if (!desktop_file_dirs[i].is_setup)
1414 desktop_file_dir_init (&desktop_file_dirs[i]);
1417 static void
1418 desktop_file_dirs_unlock (void)
1420 g_mutex_unlock (&desktop_file_dir_lock);
1423 static void
1424 desktop_file_dirs_invalidate_user_config (void)
1426 g_mutex_lock (&desktop_file_dir_lock);
1428 if (n_desktop_file_dirs)
1429 desktop_file_dir_reset (&desktop_file_dirs[desktop_file_dir_user_config_index]);
1431 g_mutex_unlock (&desktop_file_dir_lock);
1434 static void
1435 desktop_file_dirs_invalidate_user_data (void)
1437 g_mutex_lock (&desktop_file_dir_lock);
1439 if (n_desktop_file_dirs)
1440 desktop_file_dir_reset (&desktop_file_dirs[desktop_file_dir_user_data_index]);
1442 g_mutex_unlock (&desktop_file_dir_lock);
1445 /* GDesktopAppInfo implementation {{{1 */
1446 /* GObject implementation {{{2 */
1447 static void
1448 g_desktop_app_info_finalize (GObject *object)
1450 GDesktopAppInfo *info;
1452 info = G_DESKTOP_APP_INFO (object);
1454 g_free (info->desktop_id);
1455 g_free (info->filename);
1457 if (info->keyfile)
1458 g_key_file_unref (info->keyfile);
1460 g_free (info->name);
1461 g_free (info->generic_name);
1462 g_free (info->fullname);
1463 g_free (info->comment);
1464 g_free (info->icon_name);
1465 if (info->icon)
1466 g_object_unref (info->icon);
1467 g_strfreev (info->keywords);
1468 g_strfreev (info->only_show_in);
1469 g_strfreev (info->not_show_in);
1470 g_free (info->try_exec);
1471 g_free (info->exec);
1472 g_free (info->binary);
1473 g_free (info->path);
1474 g_free (info->categories);
1475 g_free (info->startup_wm_class);
1476 g_strfreev (info->mime_types);
1477 g_free (info->app_id);
1478 g_strfreev (info->actions);
1480 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
1483 static void
1484 g_desktop_app_info_set_property (GObject *object,
1485 guint prop_id,
1486 const GValue *value,
1487 GParamSpec *pspec)
1489 GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
1491 switch (prop_id)
1493 case PROP_FILENAME:
1494 self->filename = g_value_dup_string (value);
1495 break;
1497 default:
1498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1499 break;
1503 static void
1504 g_desktop_app_info_get_property (GObject *object,
1505 guint prop_id,
1506 GValue *value,
1507 GParamSpec *pspec)
1509 GDesktopAppInfo *self = G_DESKTOP_APP_INFO (object);
1511 switch (prop_id)
1513 case PROP_FILENAME:
1514 g_value_set_string (value, self->filename);
1515 break;
1516 default:
1517 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1518 break;
1522 static void
1523 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
1525 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1527 gobject_class->get_property = g_desktop_app_info_get_property;
1528 gobject_class->set_property = g_desktop_app_info_set_property;
1529 gobject_class->finalize = g_desktop_app_info_finalize;
1532 * GDesktopAppInfo:filename:
1534 * The origin filename of this #GDesktopAppInfo
1536 g_object_class_install_property (gobject_class,
1537 PROP_FILENAME,
1538 g_param_spec_string ("filename", "Filename", "", NULL,
1539 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1542 static void
1543 g_desktop_app_info_init (GDesktopAppInfo *local)
1547 /* Construction... {{{2 */
1549 /*< internal >
1550 * binary_from_exec:
1551 * @exec: an exec line
1553 * Returns the first word in an exec line (ie: the binary name).
1555 * If @exec is " progname --foo %F" then returns "progname".
1557 static char *
1558 binary_from_exec (const char *exec)
1560 const char *p, *start;
1562 p = exec;
1563 while (*p == ' ')
1564 p++;
1565 start = p;
1566 while (*p != ' ' && *p != 0)
1567 p++;
1569 return g_strndup (start, p - start);
1572 static gboolean
1573 g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
1574 GKeyFile *key_file)
1576 char *start_group;
1577 char *type;
1578 char *try_exec;
1579 char *exec;
1580 gboolean bus_activatable;
1582 start_group = g_key_file_get_start_group (key_file);
1583 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
1585 g_free (start_group);
1586 return FALSE;
1588 g_free (start_group);
1590 type = g_key_file_get_string (key_file,
1591 G_KEY_FILE_DESKTOP_GROUP,
1592 G_KEY_FILE_DESKTOP_KEY_TYPE,
1593 NULL);
1594 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
1596 g_free (type);
1597 return FALSE;
1599 g_free (type);
1601 try_exec = g_key_file_get_string (key_file,
1602 G_KEY_FILE_DESKTOP_GROUP,
1603 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
1604 NULL);
1605 if (try_exec && try_exec[0] != '\0')
1607 char *t;
1608 t = g_find_program_in_path (try_exec);
1609 if (t == NULL)
1611 g_free (try_exec);
1612 return FALSE;
1614 g_free (t);
1617 exec = g_key_file_get_string (key_file,
1618 G_KEY_FILE_DESKTOP_GROUP,
1619 G_KEY_FILE_DESKTOP_KEY_EXEC,
1620 NULL);
1621 if (exec && exec[0] != '\0')
1623 gint argc;
1624 char **argv;
1625 if (!g_shell_parse_argv (exec, &argc, &argv, NULL))
1627 g_free (exec);
1628 g_free (try_exec);
1629 return FALSE;
1631 else
1633 char *t;
1634 t = g_find_program_in_path (argv[0]);
1635 g_strfreev (argv);
1637 if (t == NULL)
1639 g_free (exec);
1640 g_free (try_exec);
1641 return FALSE;
1643 g_free (t);
1647 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
1648 info->generic_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, GENERIC_NAME_KEY, NULL, NULL);
1649 info->fullname = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, FULL_NAME_KEY, NULL, NULL);
1650 info->keywords = g_key_file_get_locale_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, KEYWORDS_KEY, NULL, NULL, NULL);
1651 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
1652 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
1653 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
1654 info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
1655 info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
1656 info->try_exec = try_exec;
1657 info->exec = exec;
1658 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
1659 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
1660 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
1661 info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
1662 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
1663 info->categories = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL);
1664 info->startup_wm_class = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, STARTUP_WM_CLASS_KEY, NULL);
1665 info->mime_types = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL, NULL);
1666 bus_activatable = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE, NULL);
1667 info->actions = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ACTIONS, NULL, NULL);
1669 /* Remove the special-case: no Actions= key just means 0 extra actions */
1670 if (info->actions == NULL)
1671 info->actions = g_new0 (gchar *, 0 + 1);
1673 info->icon = NULL;
1674 if (info->icon_name)
1676 if (g_path_is_absolute (info->icon_name))
1678 GFile *file;
1680 file = g_file_new_for_path (info->icon_name);
1681 info->icon = g_file_icon_new (file);
1682 g_object_unref (file);
1684 else
1686 char *p;
1688 /* Work around a common mistake in desktop files */
1689 if ((p = strrchr (info->icon_name, '.')) != NULL &&
1690 (strcmp (p, ".png") == 0 ||
1691 strcmp (p, ".xpm") == 0 ||
1692 strcmp (p, ".svg") == 0))
1693 *p = 0;
1695 info->icon = g_themed_icon_new (info->icon_name);
1699 if (info->exec)
1700 info->binary = binary_from_exec (info->exec);
1702 if (info->path && info->path[0] == '\0')
1704 g_free (info->path);
1705 info->path = NULL;
1708 /* Can only be DBusActivatable if we know the filename, which means
1709 * that this won't work for the load-from-keyfile case.
1711 if (bus_activatable && info->filename)
1713 gchar *basename;
1714 gchar *last_dot;
1716 basename = g_path_get_basename (info->filename);
1717 last_dot = strrchr (basename, '.');
1719 if (last_dot && g_str_equal (last_dot, ".desktop"))
1721 *last_dot = '\0';
1723 if (g_dbus_is_interface_name (basename))
1724 info->app_id = g_strdup (basename);
1727 g_free (basename);
1730 info->keyfile = g_key_file_ref (key_file);
1732 return TRUE;
1735 static gboolean
1736 g_desktop_app_info_load_file (GDesktopAppInfo *self)
1738 GKeyFile *key_file;
1739 gboolean retval = FALSE;
1741 g_return_val_if_fail (self->filename != NULL, FALSE);
1743 self->desktop_id = g_path_get_basename (self->filename);
1745 key_file = g_key_file_new ();
1747 if (g_key_file_load_from_file (key_file, self->filename, G_KEY_FILE_NONE, NULL))
1748 retval = g_desktop_app_info_load_from_keyfile (self, key_file);
1750 g_key_file_unref (key_file);
1751 return retval;
1755 * g_desktop_app_info_new_from_keyfile:
1756 * @key_file: an opened #GKeyFile
1758 * Creates a new #GDesktopAppInfo.
1760 * Returns: a new #GDesktopAppInfo or %NULL on error.
1762 * Since: 2.18
1764 GDesktopAppInfo *
1765 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
1767 GDesktopAppInfo *info;
1769 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1770 info->filename = NULL;
1771 if (!g_desktop_app_info_load_from_keyfile (info, key_file))
1773 g_object_unref (info);
1774 return NULL;
1776 return info;
1780 * g_desktop_app_info_new_from_filename:
1781 * @filename: the path of a desktop file, in the GLib filename encoding
1783 * Creates a new #GDesktopAppInfo.
1785 * Returns: a new #GDesktopAppInfo or %NULL on error.
1787 GDesktopAppInfo *
1788 g_desktop_app_info_new_from_filename (const char *filename)
1790 GDesktopAppInfo *info = NULL;
1792 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, "filename", filename, NULL);
1793 if (!g_desktop_app_info_load_file (info))
1795 g_object_unref (info);
1796 return NULL;
1798 return info;
1802 * g_desktop_app_info_new:
1803 * @desktop_id: the desktop file id
1805 * Creates a new #GDesktopAppInfo based on a desktop file id.
1807 * A desktop file id is the basename of the desktop file, including the
1808 * .desktop extension. GIO is looking for a desktop file with this name
1809 * in the `applications` subdirectories of the XDG
1810 * data directories (i.e. the directories specified in the `XDG_DATA_HOME`
1811 * and `XDG_DATA_DIRS` environment variables). GIO also supports the
1812 * prefix-to-subdirectory mapping that is described in the
1813 * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/)
1814 * (i.e. a desktop id of kde-foo.desktop will match
1815 * `/usr/share/applications/kde/foo.desktop`).
1817 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
1819 GDesktopAppInfo *
1820 g_desktop_app_info_new (const char *desktop_id)
1822 GDesktopAppInfo *appinfo = NULL;
1823 guint i;
1825 desktop_file_dirs_lock ();
1827 for (i = 0; i < n_desktop_file_dirs; i++)
1829 appinfo = desktop_file_dir_get_app (&desktop_file_dirs[i], desktop_id);
1831 if (appinfo)
1832 break;
1835 desktop_file_dirs_unlock ();
1837 if (appinfo == NULL)
1838 return NULL;
1840 g_free (appinfo->desktop_id);
1841 appinfo->desktop_id = g_strdup (desktop_id);
1843 if (g_desktop_app_info_get_is_hidden (appinfo))
1845 g_object_unref (appinfo);
1846 appinfo = NULL;
1849 return appinfo;
1852 static GAppInfo *
1853 g_desktop_app_info_dup (GAppInfo *appinfo)
1855 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1856 GDesktopAppInfo *new_info;
1858 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1860 new_info->filename = g_strdup (info->filename);
1861 new_info->desktop_id = g_strdup (info->desktop_id);
1863 if (info->keyfile)
1864 new_info->keyfile = g_key_file_ref (info->keyfile);
1866 new_info->name = g_strdup (info->name);
1867 new_info->generic_name = g_strdup (info->generic_name);
1868 new_info->fullname = g_strdup (info->fullname);
1869 new_info->keywords = g_strdupv (info->keywords);
1870 new_info->comment = g_strdup (info->comment);
1871 new_info->nodisplay = info->nodisplay;
1872 new_info->icon_name = g_strdup (info->icon_name);
1873 if (info->icon)
1874 new_info->icon = g_object_ref (info->icon);
1875 new_info->only_show_in = g_strdupv (info->only_show_in);
1876 new_info->not_show_in = g_strdupv (info->not_show_in);
1877 new_info->try_exec = g_strdup (info->try_exec);
1878 new_info->exec = g_strdup (info->exec);
1879 new_info->binary = g_strdup (info->binary);
1880 new_info->path = g_strdup (info->path);
1881 new_info->app_id = g_strdup (info->app_id);
1882 new_info->hidden = info->hidden;
1883 new_info->terminal = info->terminal;
1884 new_info->startup_notify = info->startup_notify;
1886 return G_APP_INFO (new_info);
1889 /* GAppInfo interface implementation functions {{{2 */
1891 static gboolean
1892 g_desktop_app_info_equal (GAppInfo *appinfo1,
1893 GAppInfo *appinfo2)
1895 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
1896 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
1898 if (info1->desktop_id == NULL ||
1899 info2->desktop_id == NULL)
1900 return info1 == info2;
1902 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
1905 static const char *
1906 g_desktop_app_info_get_id (GAppInfo *appinfo)
1908 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1910 return info->desktop_id;
1913 static const char *
1914 g_desktop_app_info_get_name (GAppInfo *appinfo)
1916 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1918 if (info->name == NULL)
1919 return _("Unnamed");
1920 return info->name;
1923 static const char *
1924 g_desktop_app_info_get_display_name (GAppInfo *appinfo)
1926 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1928 if (info->fullname == NULL)
1929 return g_desktop_app_info_get_name (appinfo);
1930 return info->fullname;
1934 * g_desktop_app_info_get_is_hidden:
1935 * @info: a #GDesktopAppInfo.
1937 * A desktop file is hidden if the Hidden key in it is
1938 * set to True.
1940 * Returns: %TRUE if hidden, %FALSE otherwise.
1942 gboolean
1943 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
1945 return info->hidden;
1949 * g_desktop_app_info_get_filename:
1950 * @info: a #GDesktopAppInfo
1952 * When @info was created from a known filename, return it. In some
1953 * situations such as the #GDesktopAppInfo returned from
1954 * g_desktop_app_info_new_from_keyfile(), this function will return %NULL.
1956 * Returns: The full path to the file for @info, or %NULL if not known.
1957 * Since: 2.24
1959 const char *
1960 g_desktop_app_info_get_filename (GDesktopAppInfo *info)
1962 return info->filename;
1965 static const char *
1966 g_desktop_app_info_get_description (GAppInfo *appinfo)
1968 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1970 return info->comment;
1973 static const char *
1974 g_desktop_app_info_get_executable (GAppInfo *appinfo)
1976 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1978 return info->binary;
1981 static const char *
1982 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
1984 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1986 return info->exec;
1989 static GIcon *
1990 g_desktop_app_info_get_icon (GAppInfo *appinfo)
1992 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1994 return info->icon;
1998 * g_desktop_app_info_get_categories:
1999 * @info: a #GDesktopAppInfo
2001 * Gets the categories from the desktop file.
2003 * Returns: The unparsed Categories key from the desktop file;
2004 * i.e. no attempt is made to split it by ';' or validate it.
2006 const char *
2007 g_desktop_app_info_get_categories (GDesktopAppInfo *info)
2009 return info->categories;
2013 * g_desktop_app_info_get_keywords:
2014 * @info: a #GDesktopAppInfo
2016 * Gets the keywords from the desktop file.
2018 * Returns: (transfer none): The value of the Keywords key
2020 * Since: 2.32
2022 const char * const *
2023 g_desktop_app_info_get_keywords (GDesktopAppInfo *info)
2025 return (const char * const *)info->keywords;
2029 * g_desktop_app_info_get_generic_name:
2030 * @info: a #GDesktopAppInfo
2032 * Gets the generic name from the destkop file.
2034 * Returns: The value of the GenericName key
2036 const char *
2037 g_desktop_app_info_get_generic_name (GDesktopAppInfo *info)
2039 return info->generic_name;
2043 * g_desktop_app_info_get_nodisplay:
2044 * @info: a #GDesktopAppInfo
2046 * Gets the value of the NoDisplay key, which helps determine if the
2047 * application info should be shown in menus. See
2048 * #G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show().
2050 * Returns: The value of the NoDisplay key
2052 * Since: 2.30
2054 gboolean
2055 g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info)
2057 return info->nodisplay;
2061 * g_desktop_app_info_get_show_in:
2062 * @info: a #GDesktopAppInfo
2063 * @desktop_env: (nullable): a string specifying a desktop name
2065 * Checks if the application info should be shown in menus that list available
2066 * applications for a specific name of the desktop, based on the
2067 * `OnlyShowIn` and `NotShowIn` keys.
2069 * @desktop_env should typically be given as %NULL, in which case the
2070 * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want
2071 * to override the default mechanism then you may specify @desktop_env,
2072 * but this is not recommended.
2074 * Note that g_app_info_should_show() for @info will include this check (with
2075 * %NULL for @desktop_env) as well as additional checks.
2077 * Returns: %TRUE if the @info should be shown in @desktop_env according to the
2078 * `OnlyShowIn` and `NotShowIn` keys, %FALSE
2079 * otherwise.
2081 * Since: 2.30
2083 gboolean
2084 g_desktop_app_info_get_show_in (GDesktopAppInfo *info,
2085 const gchar *desktop_env)
2087 const gchar *specified_envs[] = { desktop_env, NULL };
2088 const gchar * const *envs;
2089 gint i;
2091 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
2093 if (desktop_env)
2094 envs = specified_envs;
2095 else
2096 envs = get_current_desktops (NULL);
2098 for (i = 0; envs[i]; i++)
2100 gint j;
2102 if (info->only_show_in)
2103 for (j = 0; info->only_show_in[j]; j++)
2104 if (g_str_equal (info->only_show_in[j], envs[i]))
2105 return TRUE;
2107 if (info->not_show_in)
2108 for (j = 0; info->not_show_in[j]; j++)
2109 if (g_str_equal (info->not_show_in[j], envs[i]))
2110 return FALSE;
2113 return info->only_show_in == NULL;
2116 /* Launching... {{{2 */
2118 static char *
2119 expand_macro_single (char macro, char *uri)
2121 GFile *file;
2122 char *result = NULL;
2123 char *path = NULL;
2124 char *name;
2126 file = g_file_new_for_uri (uri);
2128 switch (macro)
2130 case 'u':
2131 case 'U':
2132 result = g_shell_quote (uri);
2133 break;
2134 case 'f':
2135 case 'F':
2136 path = g_file_get_path (file);
2137 if (path)
2138 result = g_shell_quote (path);
2139 break;
2140 case 'd':
2141 case 'D':
2142 path = g_file_get_path (file);
2143 if (path)
2145 name = g_path_get_dirname (path);
2146 result = g_shell_quote (name);
2147 g_free (name);
2149 break;
2150 case 'n':
2151 case 'N':
2152 path = g_file_get_path (file);
2153 if (path)
2155 name = g_path_get_basename (path);
2156 result = g_shell_quote (name);
2157 g_free (name);
2159 break;
2162 g_object_unref (file);
2163 g_free (path);
2165 return result;
2168 static void
2169 expand_macro (char macro,
2170 GString *exec,
2171 GDesktopAppInfo *info,
2172 GList **uri_list)
2174 GList *uris = *uri_list;
2175 char *expanded;
2176 gboolean force_file_uri;
2177 char force_file_uri_macro;
2178 char *uri;
2180 g_return_if_fail (exec != NULL);
2182 /* On %u and %U, pass POSIX file path pointing to the URI via
2183 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
2184 * running or the URI doesn't have a POSIX file path via FUSE
2185 * we'll just pass the URI.
2187 force_file_uri_macro = macro;
2188 force_file_uri = FALSE;
2189 if (!info->no_fuse)
2191 switch (macro)
2193 case 'u':
2194 force_file_uri_macro = 'f';
2195 force_file_uri = TRUE;
2196 break;
2197 case 'U':
2198 force_file_uri_macro = 'F';
2199 force_file_uri = TRUE;
2200 break;
2201 default:
2202 break;
2206 switch (macro)
2208 case 'u':
2209 case 'f':
2210 case 'd':
2211 case 'n':
2212 if (uris)
2214 uri = uris->data;
2215 if (!force_file_uri ||
2216 /* Pass URI if it contains an anchor */
2217 strchr (uri, '#') != NULL)
2219 expanded = expand_macro_single (macro, uri);
2221 else
2223 expanded = expand_macro_single (force_file_uri_macro, uri);
2224 if (expanded == NULL)
2225 expanded = expand_macro_single (macro, uri);
2228 if (expanded)
2230 g_string_append (exec, expanded);
2231 g_free (expanded);
2233 uris = uris->next;
2236 break;
2238 case 'U':
2239 case 'F':
2240 case 'D':
2241 case 'N':
2242 while (uris)
2244 uri = uris->data;
2246 if (!force_file_uri ||
2247 /* Pass URI if it contains an anchor */
2248 strchr (uri, '#') != NULL)
2250 expanded = expand_macro_single (macro, uri);
2252 else
2254 expanded = expand_macro_single (force_file_uri_macro, uri);
2255 if (expanded == NULL)
2256 expanded = expand_macro_single (macro, uri);
2259 if (expanded)
2261 g_string_append (exec, expanded);
2262 g_free (expanded);
2265 uris = uris->next;
2267 if (uris != NULL && expanded)
2268 g_string_append_c (exec, ' ');
2271 break;
2273 case 'i':
2274 if (info->icon_name)
2276 g_string_append (exec, "--icon ");
2277 expanded = g_shell_quote (info->icon_name);
2278 g_string_append (exec, expanded);
2279 g_free (expanded);
2281 break;
2283 case 'c':
2284 if (info->name)
2286 expanded = g_shell_quote (info->name);
2287 g_string_append (exec, expanded);
2288 g_free (expanded);
2290 break;
2292 case 'k':
2293 if (info->filename)
2295 expanded = g_shell_quote (info->filename);
2296 g_string_append (exec, expanded);
2297 g_free (expanded);
2299 break;
2301 case 'm': /* deprecated */
2302 break;
2304 case '%':
2305 g_string_append_c (exec, '%');
2306 break;
2309 *uri_list = uris;
2312 static gboolean
2313 expand_application_parameters (GDesktopAppInfo *info,
2314 const gchar *exec_line,
2315 GList **uris,
2316 int *argc,
2317 char ***argv,
2318 GError **error)
2320 GList *uri_list = *uris;
2321 const char *p = exec_line;
2322 GString *expanded_exec;
2323 gboolean res;
2325 if (exec_line == NULL)
2327 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2328 _("Desktop file didn't specify Exec field"));
2329 return FALSE;
2332 expanded_exec = g_string_new (NULL);
2334 while (*p)
2336 if (p[0] == '%' && p[1] != '\0')
2338 expand_macro (p[1], expanded_exec, info, uris);
2339 p++;
2341 else
2342 g_string_append_c (expanded_exec, *p);
2344 p++;
2347 /* No file substitutions */
2348 if (uri_list == *uris && uri_list != NULL)
2350 /* If there is no macro default to %f. This is also what KDE does */
2351 g_string_append_c (expanded_exec, ' ');
2352 expand_macro ('f', expanded_exec, info, uris);
2355 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
2356 g_string_free (expanded_exec, TRUE);
2357 return res;
2360 static gboolean
2361 prepend_terminal_to_vector (int *argc,
2362 char ***argv)
2364 #ifndef G_OS_WIN32
2365 char **real_argv;
2366 int real_argc;
2367 int i, j;
2368 char **term_argv = NULL;
2369 int term_argc = 0;
2370 char *check;
2371 char **the_argv;
2373 g_return_val_if_fail (argc != NULL, FALSE);
2374 g_return_val_if_fail (argv != NULL, FALSE);
2376 /* sanity */
2377 if(*argv == NULL)
2378 *argc = 0;
2380 the_argv = *argv;
2382 /* compute size if not given */
2383 if (*argc < 0)
2385 for (i = 0; the_argv[i] != NULL; i++)
2387 *argc = i;
2390 term_argc = 2;
2391 term_argv = g_new0 (char *, 3);
2393 check = g_find_program_in_path ("gnome-terminal");
2394 if (check != NULL)
2396 term_argv[0] = check;
2397 /* Note that gnome-terminal takes -x and
2398 * as -e in gnome-terminal is broken we use that. */
2399 term_argv[1] = g_strdup ("-x");
2401 else
2403 if (check == NULL)
2404 check = g_find_program_in_path ("nxterm");
2405 if (check == NULL)
2406 check = g_find_program_in_path ("color-xterm");
2407 if (check == NULL)
2408 check = g_find_program_in_path ("rxvt");
2409 if (check == NULL)
2410 check = g_find_program_in_path ("xterm");
2411 if (check == NULL)
2412 check = g_find_program_in_path ("dtterm");
2413 if (check == NULL)
2415 check = g_strdup ("xterm");
2416 g_warning ("couldn't find a terminal, falling back to xterm");
2418 term_argv[0] = check;
2419 term_argv[1] = g_strdup ("-e");
2422 real_argc = term_argc + *argc;
2423 real_argv = g_new (char *, real_argc + 1);
2425 for (i = 0; i < term_argc; i++)
2426 real_argv[i] = term_argv[i];
2428 for (j = 0; j < *argc; j++, i++)
2429 real_argv[i] = (char *)the_argv[j];
2431 real_argv[i] = NULL;
2433 g_free (*argv);
2434 *argv = real_argv;
2435 *argc = real_argc;
2437 /* we use g_free here as we sucked all the inner strings
2438 * out from it into real_argv */
2439 g_free (term_argv);
2440 return TRUE;
2441 #else
2442 return FALSE;
2443 #endif /* G_OS_WIN32 */
2446 static GList *
2447 create_files_for_uris (GList *uris)
2449 GList *res;
2450 GList *iter;
2452 res = NULL;
2454 for (iter = uris; iter; iter = iter->next)
2456 GFile *file = g_file_new_for_uri ((char *)iter->data);
2457 res = g_list_prepend (res, file);
2460 return g_list_reverse (res);
2463 typedef struct
2465 GSpawnChildSetupFunc user_setup;
2466 gpointer user_setup_data;
2468 char *pid_envvar;
2469 } ChildSetupData;
2471 static void
2472 child_setup (gpointer user_data)
2474 ChildSetupData *data = user_data;
2476 if (data->pid_envvar)
2478 pid_t pid = getpid ();
2479 char buf[20];
2480 int i;
2482 /* Write the pid into the space already reserved for it in the
2483 * environment array. We can't use sprintf because it might
2484 * malloc, so we do it by hand. It's simplest to write the pid
2485 * out backwards first, then copy it over.
2487 for (i = 0; pid; i++, pid /= 10)
2488 buf[i] = (pid % 10) + '0';
2489 for (i--; i >= 0; i--)
2490 *(data->pid_envvar++) = buf[i];
2491 *data->pid_envvar = '\0';
2494 if (data->user_setup)
2495 data->user_setup (data->user_setup_data);
2498 static void
2499 notify_desktop_launch (GDBusConnection *session_bus,
2500 GDesktopAppInfo *info,
2501 long pid,
2502 const char *display,
2503 const char *sn_id,
2504 GList *uris)
2506 GDBusMessage *msg;
2507 GVariantBuilder uri_variant;
2508 GVariantBuilder extras_variant;
2509 GList *iter;
2510 const char *desktop_file_id;
2511 const char *gio_desktop_file;
2513 if (session_bus == NULL)
2514 return;
2516 g_variant_builder_init (&uri_variant, G_VARIANT_TYPE ("as"));
2517 for (iter = uris; iter; iter = iter->next)
2518 g_variant_builder_add (&uri_variant, "s", iter->data);
2520 g_variant_builder_init (&extras_variant, G_VARIANT_TYPE ("a{sv}"));
2521 if (sn_id != NULL && g_utf8_validate (sn_id, -1, NULL))
2522 g_variant_builder_add (&extras_variant, "{sv}",
2523 "startup-id",
2524 g_variant_new ("s",
2525 sn_id));
2526 gio_desktop_file = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
2527 if (gio_desktop_file != NULL)
2528 g_variant_builder_add (&extras_variant, "{sv}",
2529 "origin-desktop-file",
2530 g_variant_new_bytestring (gio_desktop_file));
2531 if (g_get_prgname () != NULL)
2532 g_variant_builder_add (&extras_variant, "{sv}",
2533 "origin-prgname",
2534 g_variant_new_bytestring (g_get_prgname ()));
2535 g_variant_builder_add (&extras_variant, "{sv}",
2536 "origin-pid",
2537 g_variant_new ("x",
2538 (gint64)getpid ()));
2540 if (info->filename)
2541 desktop_file_id = info->filename;
2542 else if (info->desktop_id)
2543 desktop_file_id = info->desktop_id;
2544 else
2545 desktop_file_id = "";
2547 msg = g_dbus_message_new_signal ("/org/gtk/gio/DesktopAppInfo",
2548 "org.gtk.gio.DesktopAppInfo",
2549 "Launched");
2550 g_dbus_message_set_body (msg, g_variant_new ("(@aysxasa{sv})",
2551 g_variant_new_bytestring (desktop_file_id),
2552 display ? display : "",
2553 (gint64)pid,
2554 &uri_variant,
2555 &extras_variant));
2556 g_dbus_connection_send_message (session_bus,
2557 msg, 0,
2558 NULL,
2559 NULL);
2560 g_object_unref (msg);
2563 #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
2565 static gboolean
2566 g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
2567 GDBusConnection *session_bus,
2568 const gchar *exec_line,
2569 GList *uris,
2570 GAppLaunchContext *launch_context,
2571 GSpawnFlags spawn_flags,
2572 GSpawnChildSetupFunc user_setup,
2573 gpointer user_setup_data,
2574 GDesktopAppLaunchCallback pid_callback,
2575 gpointer pid_callback_data,
2576 GError **error)
2578 gboolean completed = FALSE;
2579 GList *old_uris;
2580 char **argv, **envp;
2581 int argc;
2582 ChildSetupData data;
2584 g_return_val_if_fail (info != NULL, FALSE);
2586 argv = NULL;
2588 if (launch_context)
2589 envp = g_app_launch_context_get_environment (launch_context);
2590 else
2591 envp = g_get_environ ();
2595 GPid pid;
2596 GList *launched_uris;
2597 GList *iter;
2598 char *display, *sn_id = NULL;
2600 old_uris = uris;
2601 if (!expand_application_parameters (info, exec_line, &uris, &argc, &argv, error))
2602 goto out;
2604 /* Get the subset of URIs we're launching with this process */
2605 launched_uris = NULL;
2606 for (iter = old_uris; iter != NULL && iter != uris; iter = iter->next)
2607 launched_uris = g_list_prepend (launched_uris, iter->data);
2608 launched_uris = g_list_reverse (launched_uris);
2610 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
2612 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
2613 _("Unable to find terminal required for application"));
2614 goto out;
2617 data.user_setup = user_setup;
2618 data.user_setup_data = user_setup_data;
2620 if (info->filename)
2622 envp = g_environ_setenv (envp,
2623 "GIO_LAUNCHED_DESKTOP_FILE",
2624 info->filename,
2625 TRUE);
2626 envp = g_environ_setenv (envp,
2627 "GIO_LAUNCHED_DESKTOP_FILE_PID",
2628 "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
2629 TRUE);
2630 data.pid_envvar = (char *)g_environ_getenv (envp, "GIO_LAUNCHED_DESKTOP_FILE_PID");
2632 else
2634 data.pid_envvar = NULL;
2637 display = NULL;
2638 sn_id = NULL;
2639 if (launch_context)
2641 GList *launched_files = create_files_for_uris (launched_uris);
2643 display = g_app_launch_context_get_display (launch_context,
2644 G_APP_INFO (info),
2645 launched_files);
2646 if (display)
2647 envp = g_environ_setenv (envp, "DISPLAY", display, TRUE);
2649 if (info->startup_notify)
2651 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
2652 G_APP_INFO (info),
2653 launched_files);
2654 if (sn_id)
2655 envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE);
2658 g_list_free_full (launched_files, g_object_unref);
2661 if (!g_spawn_async (info->path,
2662 argv,
2663 envp,
2664 spawn_flags,
2665 child_setup,
2666 &data,
2667 &pid,
2668 error))
2670 if (sn_id)
2671 g_app_launch_context_launch_failed (launch_context, sn_id);
2673 g_free (display);
2674 g_free (sn_id);
2675 g_list_free (launched_uris);
2677 goto out;
2680 if (pid_callback != NULL)
2681 pid_callback (info, pid, pid_callback_data);
2683 if (launch_context != NULL)
2685 GVariantBuilder builder;
2686 GVariant *platform_data;
2688 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
2689 g_variant_builder_add (&builder, "{sv}", "pid", g_variant_new_int32 (pid));
2690 if (sn_id)
2691 g_variant_builder_add (&builder, "{sv}", "startup-notification-id", g_variant_new_string (sn_id));
2692 platform_data = g_variant_ref_sink (g_variant_builder_end (&builder));
2693 g_signal_emit_by_name (launch_context, "launched", info, platform_data);
2694 g_variant_unref (platform_data);
2697 notify_desktop_launch (session_bus,
2698 info,
2699 pid,
2700 display,
2701 sn_id,
2702 launched_uris);
2704 g_free (display);
2705 g_free (sn_id);
2706 g_list_free (launched_uris);
2708 g_strfreev (argv);
2709 argv = NULL;
2711 while (uris != NULL);
2713 completed = TRUE;
2715 out:
2716 g_strfreev (argv);
2717 g_strfreev (envp);
2719 return completed;
2722 static gchar *
2723 object_path_from_appid (const gchar *app_id)
2725 gchar *path;
2726 gint i, n;
2728 n = strlen (app_id);
2729 path = g_malloc (n + 2);
2731 path[0] = '/';
2733 for (i = 0; i < n; i++)
2734 if (app_id[i] != '.')
2735 path[i + 1] = app_id[i];
2736 else
2737 path[i + 1] = '/';
2739 path[i + 1] = '\0';
2741 return path;
2744 static GVariant *
2745 g_desktop_app_info_make_platform_data (GDesktopAppInfo *info,
2746 GList *uris,
2747 GAppLaunchContext *launch_context)
2749 GVariantBuilder builder;
2751 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
2753 if (launch_context)
2755 GList *launched_files = create_files_for_uris (uris);
2757 if (info->startup_notify)
2759 gchar *sn_id;
2761 sn_id = g_app_launch_context_get_startup_notify_id (launch_context, G_APP_INFO (info), launched_files);
2762 if (sn_id)
2763 g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id));
2766 g_list_free_full (launched_files, g_object_unref);
2769 return g_variant_builder_end (&builder);
2772 static gboolean
2773 g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo *info,
2774 GDBusConnection *session_bus,
2775 GList *uris,
2776 GAppLaunchContext *launch_context)
2778 GVariantBuilder builder;
2779 gchar *object_path;
2781 g_return_val_if_fail (info != NULL, FALSE);
2783 g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
2785 if (uris)
2787 GList *iter;
2789 g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
2790 for (iter = uris; iter; iter = iter->next)
2791 g_variant_builder_add (&builder, "s", iter->data);
2792 g_variant_builder_close (&builder);
2795 g_variant_builder_add_value (&builder, g_desktop_app_info_make_platform_data (info, uris, launch_context));
2797 /* This is non-blocking API. Similar to launching via fork()/exec()
2798 * we don't wait around to see if the program crashed during startup.
2799 * This is what startup-notification's job is...
2801 object_path = object_path_from_appid (info->app_id);
2802 g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application",
2803 uris ? "Open" : "Activate", g_variant_builder_end (&builder),
2804 NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
2805 g_free (object_path);
2807 return TRUE;
2810 static gboolean
2811 g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
2812 GList *uris,
2813 GAppLaunchContext *launch_context,
2814 GSpawnFlags spawn_flags,
2815 GSpawnChildSetupFunc user_setup,
2816 gpointer user_setup_data,
2817 GDesktopAppLaunchCallback pid_callback,
2818 gpointer pid_callback_data,
2819 GError **error)
2821 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2822 GDBusConnection *session_bus;
2823 gboolean success = TRUE;
2825 session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
2827 if (session_bus && info->app_id)
2828 g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context);
2829 else
2830 success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, info->exec, uris, launch_context,
2831 spawn_flags, user_setup, user_setup_data,
2832 pid_callback, pid_callback_data, error);
2834 if (session_bus != NULL)
2836 /* This asynchronous flush holds a reference until it completes,
2837 * which ensures that the following unref won't immediately kill
2838 * the connection if we were the initial owner.
2840 g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
2841 g_object_unref (session_bus);
2844 return success;
2847 static gboolean
2848 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
2849 GList *uris,
2850 GAppLaunchContext *launch_context,
2851 GError **error)
2853 return g_desktop_app_info_launch_uris_internal (appinfo, uris,
2854 launch_context,
2855 _SPAWN_FLAGS_DEFAULT,
2856 NULL, NULL, NULL, NULL,
2857 error);
2860 static gboolean
2861 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
2863 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2865 return info->exec &&
2866 ((strstr (info->exec, "%u") != NULL) ||
2867 (strstr (info->exec, "%U") != NULL));
2870 static gboolean
2871 g_desktop_app_info_supports_files (GAppInfo *appinfo)
2873 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2875 return info->exec &&
2876 ((strstr (info->exec, "%f") != NULL) ||
2877 (strstr (info->exec, "%F") != NULL));
2880 static gboolean
2881 g_desktop_app_info_launch (GAppInfo *appinfo,
2882 GList *files,
2883 GAppLaunchContext *launch_context,
2884 GError **error)
2886 GList *uris;
2887 char *uri;
2888 gboolean res;
2890 uris = NULL;
2891 while (files)
2893 uri = g_file_get_uri (files->data);
2894 uris = g_list_prepend (uris, uri);
2895 files = files->next;
2898 uris = g_list_reverse (uris);
2900 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
2902 g_list_free_full (uris, g_free);
2904 return res;
2908 * g_desktop_app_info_launch_uris_as_manager:
2909 * @appinfo: a #GDesktopAppInfo
2910 * @uris: (element-type utf8): List of URIs
2911 * @launch_context: (allow-none): a #GAppLaunchContext
2912 * @spawn_flags: #GSpawnFlags, used for each process
2913 * @user_setup: (scope call) (allow-none): a #GSpawnChildSetupFunc, used once
2914 * for each process.
2915 * @user_setup_data: (closure user_setup) (allow-none): User data for @user_setup
2916 * @pid_callback: (scope call) (allow-none): Callback for child processes
2917 * @pid_callback_data: (closure pid_callback) (allow-none): User data for @callback
2918 * @error: return location for a #GError, or %NULL
2920 * This function performs the equivalent of g_app_info_launch_uris(),
2921 * but is intended primarily for operating system components that
2922 * launch applications. Ordinary applications should use
2923 * g_app_info_launch_uris().
2925 * If the application is launched via traditional UNIX fork()/exec()
2926 * then @spawn_flags, @user_setup and @user_setup_data are used for the
2927 * call to g_spawn_async(). Additionally, @pid_callback (with
2928 * @pid_callback_data) will be called to inform about the PID of the
2929 * created process.
2931 * If application launching occurs via some other mechanism (eg: D-Bus
2932 * activation) then @spawn_flags, @user_setup, @user_setup_data,
2933 * @pid_callback and @pid_callback_data are ignored.
2935 * Returns: %TRUE on successful launch, %FALSE otherwise.
2937 gboolean
2938 g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo,
2939 GList *uris,
2940 GAppLaunchContext *launch_context,
2941 GSpawnFlags spawn_flags,
2942 GSpawnChildSetupFunc user_setup,
2943 gpointer user_setup_data,
2944 GDesktopAppLaunchCallback pid_callback,
2945 gpointer pid_callback_data,
2946 GError **error)
2948 return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
2949 uris,
2950 launch_context,
2951 spawn_flags,
2952 user_setup,
2953 user_setup_data,
2954 pid_callback,
2955 pid_callback_data,
2956 error);
2959 /* OnlyShowIn API support {{{2 */
2962 * g_desktop_app_info_set_desktop_env:
2963 * @desktop_env: a string specifying what desktop this is
2965 * Sets the name of the desktop that the application is running in.
2966 * This is used by g_app_info_should_show() and
2967 * g_desktop_app_info_get_show_in() to evaluate the
2968 * `OnlyShowIn` and `NotShowIn`
2969 * desktop entry fields.
2971 * Should be called only once; subsequent calls are ignored.
2973 * Deprecated:2.42:do not use this API. Since 2.42 the value of the
2974 * `XDG_CURRENT_DESKTOP` environment variable will be used.
2976 void
2977 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
2979 get_current_desktops (desktop_env);
2982 static gboolean
2983 g_desktop_app_info_should_show (GAppInfo *appinfo)
2985 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
2987 if (info->nodisplay)
2988 return FALSE;
2990 return g_desktop_app_info_get_show_in (info, NULL);
2993 /* mime types/default apps support {{{2 */
2995 typedef enum {
2996 CONF_DIR,
2997 APP_DIR,
2998 MIMETYPE_DIR
2999 } DirType;
3001 static char *
3002 ensure_dir (DirType type,
3003 GError **error)
3005 char *path, *display_name;
3006 int errsv;
3008 switch (type)
3010 case CONF_DIR:
3011 path = g_build_filename (g_get_user_config_dir (), NULL);
3012 break;
3014 case APP_DIR:
3015 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
3016 break;
3018 case MIMETYPE_DIR:
3019 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
3020 break;
3022 default:
3023 g_assert_not_reached ();
3026 errno = 0;
3027 if (g_mkdir_with_parents (path, 0700) == 0)
3028 return path;
3030 errsv = errno;
3031 display_name = g_filename_display_name (path);
3032 if (type == APP_DIR)
3033 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
3034 _("Can't create user application configuration folder %s: %s"),
3035 display_name, g_strerror (errsv));
3036 else
3037 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
3038 _("Can't create user MIME configuration folder %s: %s"),
3039 display_name, g_strerror (errsv));
3041 g_free (display_name);
3042 g_free (path);
3044 return NULL;
3047 static gboolean
3048 update_mimeapps_list (const char *desktop_id,
3049 const char *content_type,
3050 UpdateMimeFlags flags,
3051 GError **error)
3053 char *dirname, *filename, *string;
3054 GKeyFile *key_file;
3055 gboolean load_succeeded, res;
3056 char **old_list, **list;
3057 gsize length, data_size;
3058 char *data;
3059 int i, j, k;
3060 char **content_types;
3062 /* Don't add both at start and end */
3063 g_assert (!((flags & UPDATE_MIME_SET_DEFAULT) &&
3064 (flags & UPDATE_MIME_SET_NON_DEFAULT)));
3066 dirname = ensure_dir (CONF_DIR, error);
3067 if (!dirname)
3068 return FALSE;
3070 filename = g_build_filename (dirname, "mimeapps.list", NULL);
3071 g_free (dirname);
3073 key_file = g_key_file_new ();
3074 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
3075 if (!load_succeeded ||
3076 (!g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP) &&
3077 !g_key_file_has_group (key_file, REMOVED_ASSOCIATIONS_GROUP) &&
3078 !g_key_file_has_group (key_file, DEFAULT_APPLICATIONS_GROUP)))
3080 g_key_file_free (key_file);
3081 key_file = g_key_file_new ();
3084 if (content_type)
3086 content_types = g_new (char *, 2);
3087 content_types[0] = g_strdup (content_type);
3088 content_types[1] = NULL;
3090 else
3092 content_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, NULL, NULL);
3095 for (k = 0; content_types && content_types[k]; k++)
3097 /* set as default, if requested so */
3098 string = g_key_file_get_string (key_file,
3099 DEFAULT_APPLICATIONS_GROUP,
3100 content_types[k],
3101 NULL);
3103 if (g_strcmp0 (string, desktop_id) != 0 &&
3104 (flags & UPDATE_MIME_SET_DEFAULT))
3106 g_free (string);
3107 string = g_strdup (desktop_id);
3109 /* add in the non-default list too, if it's not already there */
3110 flags |= UPDATE_MIME_SET_NON_DEFAULT;
3113 if (string == NULL || desktop_id == NULL)
3114 g_key_file_remove_key (key_file,
3115 DEFAULT_APPLICATIONS_GROUP,
3116 content_types[k],
3117 NULL);
3118 else
3119 g_key_file_set_string (key_file,
3120 DEFAULT_APPLICATIONS_GROUP,
3121 content_types[k],
3122 string);
3124 g_free (string);
3127 if (content_type)
3129 /* reuse the list from above */
3131 else
3133 g_strfreev (content_types);
3134 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
3137 for (k = 0; content_types && content_types[k]; k++)
3139 /* Add to the right place in the list */
3141 length = 0;
3142 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
3143 content_types[k], &length, NULL);
3145 list = g_new (char *, 1 + length + 1);
3147 i = 0;
3149 /* if we're adding a last-used hint, just put the application in front of the list */
3150 if (flags & UPDATE_MIME_SET_LAST_USED)
3152 /* avoid adding this again as non-default later */
3153 if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3154 flags ^= UPDATE_MIME_SET_NON_DEFAULT;
3156 list[i++] = g_strdup (desktop_id);
3159 if (old_list)
3161 for (j = 0; old_list[j] != NULL; j++)
3163 if (g_strcmp0 (old_list[j], desktop_id) != 0)
3165 /* rewrite other entries if they're different from the new one */
3166 list[i++] = g_strdup (old_list[j]);
3168 else if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3170 /* we encountered an old entry which is equal to the one we're adding as non-default,
3171 * don't change its position in the list.
3173 flags ^= UPDATE_MIME_SET_NON_DEFAULT;
3174 list[i++] = g_strdup (old_list[j]);
3179 /* add it at the end of the list */
3180 if (flags & UPDATE_MIME_SET_NON_DEFAULT)
3181 list[i++] = g_strdup (desktop_id);
3183 list[i] = NULL;
3185 g_strfreev (old_list);
3187 if (list[0] == NULL || desktop_id == NULL)
3188 g_key_file_remove_key (key_file,
3189 ADDED_ASSOCIATIONS_GROUP,
3190 content_types[k],
3191 NULL);
3192 else
3193 g_key_file_set_string_list (key_file,
3194 ADDED_ASSOCIATIONS_GROUP,
3195 content_types[k],
3196 (const char * const *)list, i);
3198 g_strfreev (list);
3201 if (content_type)
3203 /* reuse the list from above */
3205 else
3207 g_strfreev (content_types);
3208 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
3211 for (k = 0; content_types && content_types[k]; k++)
3213 /* Remove from removed associations group (unless remove) */
3215 length = 0;
3216 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
3217 content_types[k], &length, NULL);
3219 list = g_new (char *, 1 + length + 1);
3221 i = 0;
3222 if (flags & UPDATE_MIME_REMOVE)
3223 list[i++] = g_strdup (desktop_id);
3224 if (old_list)
3226 for (j = 0; old_list[j] != NULL; j++)
3228 if (g_strcmp0 (old_list[j], desktop_id) != 0)
3229 list[i++] = g_strdup (old_list[j]);
3232 list[i] = NULL;
3234 g_strfreev (old_list);
3236 if (list[0] == NULL || desktop_id == NULL)
3237 g_key_file_remove_key (key_file,
3238 REMOVED_ASSOCIATIONS_GROUP,
3239 content_types[k],
3240 NULL);
3241 else
3242 g_key_file_set_string_list (key_file,
3243 REMOVED_ASSOCIATIONS_GROUP,
3244 content_types[k],
3245 (const char * const *)list, i);
3247 g_strfreev (list);
3250 g_strfreev (content_types);
3252 data = g_key_file_to_data (key_file, &data_size, error);
3253 g_key_file_free (key_file);
3255 res = g_file_set_contents (filename, data, data_size, error);
3257 desktop_file_dirs_invalidate_user_config ();
3259 g_free (filename);
3260 g_free (data);
3262 return res;
3265 static gboolean
3266 g_desktop_app_info_set_as_last_used_for_type (GAppInfo *appinfo,
3267 const char *content_type,
3268 GError **error)
3270 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3272 if (!g_desktop_app_info_ensure_saved (info, error))
3273 return FALSE;
3275 if (!info->desktop_id)
3277 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3278 _("Application information lacks an identifier"));
3279 return FALSE;
3282 /* both add support for the content type and set as last used */
3283 return update_mimeapps_list (info->desktop_id, content_type,
3284 UPDATE_MIME_SET_NON_DEFAULT |
3285 UPDATE_MIME_SET_LAST_USED,
3286 error);
3289 static gboolean
3290 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
3291 const char *content_type,
3292 GError **error)
3294 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3296 if (!g_desktop_app_info_ensure_saved (info, error))
3297 return FALSE;
3299 if (!info->desktop_id)
3301 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3302 _("Application information lacks an identifier"));
3303 return FALSE;
3306 return update_mimeapps_list (info->desktop_id, content_type,
3307 UPDATE_MIME_SET_DEFAULT,
3308 error);
3311 static void
3312 update_program_done (GPid pid,
3313 gint status,
3314 gpointer data)
3316 /* Did the application exit correctly */
3317 if (g_spawn_check_exit_status (status, NULL))
3319 /* Here we could clean out any caches in use */
3323 static void
3324 run_update_command (char *command,
3325 char *subdir)
3327 char *argv[3] = {
3328 NULL,
3329 NULL,
3330 NULL,
3332 GPid pid = 0;
3333 GError *error = NULL;
3335 argv[0] = command;
3336 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
3338 if (g_spawn_async ("/", argv,
3339 NULL, /* envp */
3340 G_SPAWN_SEARCH_PATH |
3341 G_SPAWN_STDOUT_TO_DEV_NULL |
3342 G_SPAWN_STDERR_TO_DEV_NULL |
3343 G_SPAWN_DO_NOT_REAP_CHILD,
3344 NULL, NULL, /* No setup function */
3345 &pid,
3346 &error))
3347 g_child_watch_add (pid, update_program_done, NULL);
3348 else
3350 /* If we get an error at this point, it's quite likely the user doesn't
3351 * have an installed copy of either 'update-mime-database' or
3352 * 'update-desktop-database'. I don't think we want to popup an error
3353 * dialog at this point, so we just do a g_warning to give the user a
3354 * chance of debugging it.
3356 g_warning ("%s", error->message);
3359 g_free (argv[1]);
3362 static gboolean
3363 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
3364 const char *extension,
3365 GError **error)
3367 char *filename, *basename, *mimetype;
3368 char *dirname;
3369 gboolean res;
3371 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
3372 return FALSE;
3374 dirname = ensure_dir (MIMETYPE_DIR, error);
3375 if (!dirname)
3376 return FALSE;
3378 basename = g_strdup_printf ("user-extension-%s.xml", extension);
3379 filename = g_build_filename (dirname, basename, NULL);
3380 g_free (basename);
3381 g_free (dirname);
3383 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
3385 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
3387 char *contents;
3389 contents =
3390 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
3391 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
3392 " <mime-type type=\"%s\">\n"
3393 " <comment>%s document</comment>\n"
3394 " <glob pattern=\"*.%s\"/>\n"
3395 " </mime-type>\n"
3396 "</mime-info>\n", mimetype, extension, extension);
3398 g_file_set_contents (filename, contents, -1, NULL);
3399 g_free (contents);
3401 run_update_command ("update-mime-database", "mime");
3403 g_free (filename);
3405 res = g_desktop_app_info_set_as_default_for_type (appinfo,
3406 mimetype,
3407 error);
3409 g_free (mimetype);
3411 return res;
3414 static gboolean
3415 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
3416 const char *content_type,
3417 GError **error)
3419 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3421 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
3422 return FALSE;
3424 return update_mimeapps_list (info->desktop_id, content_type,
3425 UPDATE_MIME_SET_NON_DEFAULT,
3426 error);
3429 static gboolean
3430 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
3432 return TRUE;
3435 static gboolean
3436 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
3437 const char *content_type,
3438 GError **error)
3440 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3442 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
3443 return FALSE;
3445 return update_mimeapps_list (info->desktop_id, content_type,
3446 UPDATE_MIME_REMOVE,
3447 error);
3450 static const char **
3451 g_desktop_app_info_get_supported_types (GAppInfo *appinfo)
3453 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3455 return (const char**) info->mime_types;
3458 /* Saving and deleting {{{2 */
3460 static gboolean
3461 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
3462 GError **error)
3464 GKeyFile *key_file;
3465 char *dirname;
3466 char *filename;
3467 char *data, *desktop_id;
3468 gsize data_size;
3469 int fd;
3470 gboolean res;
3472 if (info->filename != NULL)
3473 return TRUE;
3475 /* This is only used for object created with
3476 * g_app_info_create_from_commandline. All other
3477 * object should have a filename
3480 dirname = ensure_dir (APP_DIR, error);
3481 if (!dirname)
3482 return FALSE;
3484 key_file = g_key_file_new ();
3486 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3487 "Encoding", "UTF-8");
3488 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3489 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
3490 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3491 G_KEY_FILE_DESKTOP_KEY_TYPE,
3492 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
3493 if (info->terminal)
3494 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3495 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
3496 if (info->nodisplay)
3497 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3498 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
3500 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3501 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
3503 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3504 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
3506 if (info->generic_name != NULL)
3507 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3508 GENERIC_NAME_KEY, info->generic_name);
3510 if (info->fullname != NULL)
3511 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3512 FULL_NAME_KEY, info->fullname);
3514 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
3515 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
3517 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
3518 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
3520 data = g_key_file_to_data (key_file, &data_size, NULL);
3521 g_key_file_free (key_file);
3523 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
3524 filename = g_build_filename (dirname, desktop_id, NULL);
3525 g_free (desktop_id);
3526 g_free (dirname);
3528 fd = g_mkstemp (filename);
3529 if (fd == -1)
3531 char *display_name;
3533 display_name = g_filename_display_name (filename);
3534 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
3535 _("Can't create user desktop file %s"), display_name);
3536 g_free (display_name);
3537 g_free (filename);
3538 g_free (data);
3539 return FALSE;
3542 desktop_id = g_path_get_basename (filename);
3544 /* FIXME - actually handle error */
3545 (void) g_close (fd, NULL);
3547 res = g_file_set_contents (filename, data, data_size, error);
3548 g_free (data);
3549 if (!res)
3551 g_free (desktop_id);
3552 g_free (filename);
3553 return FALSE;
3556 info->filename = filename;
3557 info->desktop_id = desktop_id;
3559 run_update_command ("update-desktop-database", "applications");
3561 /* We just dropped a file in the user's desktop file directory. Save
3562 * the monitor the bother of having to notice it and invalidate
3563 * immediately.
3565 * This means that calls directly following this will be able to see
3566 * the results immediately.
3568 desktop_file_dirs_invalidate_user_data ();
3570 return TRUE;
3573 static gboolean
3574 g_desktop_app_info_can_delete (GAppInfo *appinfo)
3576 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3578 if (info->filename)
3580 if (strstr (info->filename, "/userapp-"))
3581 return g_access (info->filename, W_OK) == 0;
3584 return FALSE;
3587 static gboolean
3588 g_desktop_app_info_delete (GAppInfo *appinfo)
3590 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
3592 if (info->filename)
3594 if (g_remove (info->filename) == 0)
3596 update_mimeapps_list (info->desktop_id, NULL,
3597 UPDATE_MIME_NONE,
3598 NULL);
3600 g_free (info->filename);
3601 info->filename = NULL;
3602 g_free (info->desktop_id);
3603 info->desktop_id = NULL;
3605 return TRUE;
3609 return FALSE;
3612 /* Create for commandline {{{2 */
3614 * g_app_info_create_from_commandline:
3615 * @commandline: the commandline to use
3616 * @application_name: (allow-none): the application name, or %NULL to use @commandline
3617 * @flags: flags that can specify details of the created #GAppInfo
3618 * @error: a #GError location to store the error occurring, %NULL to ignore.
3620 * Creates a new #GAppInfo from the given information.
3622 * Note that for @commandline, the quoting rules of the Exec key of the
3623 * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec)
3624 * are applied. For example, if the @commandline contains
3625 * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
3626 * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
3628 * Returns: (transfer full): new #GAppInfo for given command.
3630 GAppInfo *
3631 g_app_info_create_from_commandline (const char *commandline,
3632 const char *application_name,
3633 GAppInfoCreateFlags flags,
3634 GError **error)
3636 char **split;
3637 char *basename;
3638 GDesktopAppInfo *info;
3640 g_return_val_if_fail (commandline, NULL);
3642 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
3644 info->filename = NULL;
3645 info->desktop_id = NULL;
3647 info->terminal = (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) != 0;
3648 info->startup_notify = (flags & G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION) != 0;
3649 info->hidden = FALSE;
3650 if ((flags & G_APP_INFO_CREATE_SUPPORTS_URIS) != 0)
3651 info->exec = g_strconcat (commandline, " %u", NULL);
3652 else
3653 info->exec = g_strconcat (commandline, " %f", NULL);
3654 info->nodisplay = TRUE;
3655 info->binary = binary_from_exec (info->exec);
3657 if (application_name)
3658 info->name = g_strdup (application_name);
3659 else
3661 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
3662 split = g_strsplit (commandline, " ", 2);
3663 basename = split[0] ? g_path_get_basename (split[0]) : NULL;
3664 g_strfreev (split);
3665 info->name = basename;
3666 if (info->name == NULL)
3667 info->name = g_strdup ("custom");
3669 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
3671 return G_APP_INFO (info);
3674 /* GAppInfo interface init */
3676 static void
3677 g_desktop_app_info_iface_init (GAppInfoIface *iface)
3679 iface->dup = g_desktop_app_info_dup;
3680 iface->equal = g_desktop_app_info_equal;
3681 iface->get_id = g_desktop_app_info_get_id;
3682 iface->get_name = g_desktop_app_info_get_name;
3683 iface->get_description = g_desktop_app_info_get_description;
3684 iface->get_executable = g_desktop_app_info_get_executable;
3685 iface->get_icon = g_desktop_app_info_get_icon;
3686 iface->launch = g_desktop_app_info_launch;
3687 iface->supports_uris = g_desktop_app_info_supports_uris;
3688 iface->supports_files = g_desktop_app_info_supports_files;
3689 iface->launch_uris = g_desktop_app_info_launch_uris;
3690 iface->should_show = g_desktop_app_info_should_show;
3691 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
3692 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
3693 iface->add_supports_type = g_desktop_app_info_add_supports_type;
3694 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
3695 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
3696 iface->can_delete = g_desktop_app_info_can_delete;
3697 iface->do_delete = g_desktop_app_info_delete;
3698 iface->get_commandline = g_desktop_app_info_get_commandline;
3699 iface->get_display_name = g_desktop_app_info_get_display_name;
3700 iface->set_as_last_used_for_type = g_desktop_app_info_set_as_last_used_for_type;
3701 iface->get_supported_types = g_desktop_app_info_get_supported_types;
3704 /* Recommended applications {{{2 */
3706 /* Converts content_type into a list of itself with all of its parent
3707 * types (if include_fallback is enabled) or just returns a single-item
3708 * list with the unaliased content type.
3710 static gchar **
3711 get_list_of_mimetypes (const gchar *content_type,
3712 gboolean include_fallback)
3714 gchar *unaliased;
3715 GPtrArray *array;
3717 array = g_ptr_array_new ();
3718 unaliased = _g_unix_content_type_unalias (content_type);
3719 g_ptr_array_add (array, unaliased);
3721 if (include_fallback)
3723 gint i;
3725 /* Iterate the array as we grow it, until we have nothing more to add */
3726 for (i = 0; i < array->len; i++)
3728 gchar **parents = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
3729 gint j;
3731 for (j = 0; parents[j]; j++)
3732 /* Don't add duplicates */
3733 if (!array_contains (array, parents[j]))
3734 g_ptr_array_add (array, parents[j]);
3735 else
3736 g_free (parents[j]);
3738 /* We already stole or freed each element. Free the container. */
3739 g_free (parents);
3743 g_ptr_array_add (array, NULL);
3745 return (gchar **) g_ptr_array_free (array, FALSE);
3748 static gchar **
3749 g_desktop_app_info_get_desktop_ids_for_content_type (const gchar *content_type,
3750 gboolean include_fallback)
3752 GPtrArray *hits, *blacklist;
3753 gchar **types;
3754 gint i, j;
3756 hits = g_ptr_array_new ();
3757 blacklist = g_ptr_array_new ();
3759 types = get_list_of_mimetypes (content_type, include_fallback);
3761 desktop_file_dirs_lock ();
3763 for (i = 0; types[i]; i++)
3764 for (j = 0; j < n_desktop_file_dirs; j++)
3765 desktop_file_dir_mime_lookup (&desktop_file_dirs[j], types[i], hits, blacklist);
3767 desktop_file_dirs_unlock ();
3769 g_ptr_array_add (hits, NULL);
3771 g_ptr_array_free (blacklist, TRUE);
3772 g_strfreev (types);
3774 return (gchar **) g_ptr_array_free (hits, FALSE);
3777 static gchar **
3778 g_desktop_app_info_get_defaults_for_content_type (const gchar *content_type)
3780 GPtrArray *results;
3781 gchar **types;
3782 gint i, j;
3784 types = get_list_of_mimetypes (content_type, TRUE);
3785 results = g_ptr_array_new ();
3787 desktop_file_dirs_lock ();
3789 for (i = 0; types[i]; i++)
3790 for (j = 0; j < n_desktop_file_dirs; j++)
3791 desktop_file_dir_default_lookup (&desktop_file_dirs[j], types[i], results);
3793 desktop_file_dirs_unlock ();
3795 g_ptr_array_add (results, NULL);
3796 g_strfreev (types);
3798 return (gchar **) g_ptr_array_free (results, FALSE);
3802 * g_app_info_get_recommended_for_type:
3803 * @content_type: the content type to find a #GAppInfo for
3805 * Gets a list of recommended #GAppInfos for a given content type, i.e.
3806 * those applications which claim to support the given content type exactly,
3807 * and not by MIME type subclassing.
3808 * Note that the first application of the list is the last used one, i.e.
3809 * the last one for which g_app_info_set_as_last_used_for_type() has been
3810 * called.
3812 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3813 * for given @content_type or %NULL on error.
3815 * Since: 2.28
3817 GList *
3818 g_app_info_get_recommended_for_type (const gchar *content_type)
3820 gchar **desktop_ids;
3821 GList *infos;
3822 gint i;
3824 g_return_val_if_fail (content_type != NULL, NULL);
3826 desktop_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, FALSE);
3828 infos = NULL;
3829 for (i = 0; desktop_ids[i]; i++)
3831 GDesktopAppInfo *info;
3833 info = g_desktop_app_info_new (desktop_ids[i]);
3834 if (info)
3835 infos = g_list_prepend (infos, info);
3838 g_strfreev (desktop_ids);
3840 return g_list_reverse (infos);
3844 * g_app_info_get_fallback_for_type:
3845 * @content_type: the content type to find a #GAppInfo for
3847 * Gets a list of fallback #GAppInfos for a given content type, i.e.
3848 * those applications which claim to support the given content type
3849 * by MIME type subclassing and not directly.
3851 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3852 * for given @content_type or %NULL on error.
3854 * Since: 2.28
3856 GList *
3857 g_app_info_get_fallback_for_type (const gchar *content_type)
3859 gchar **recommended_ids;
3860 gchar **all_ids;
3861 GList *infos;
3862 gint i;
3864 g_return_val_if_fail (content_type != NULL, NULL);
3866 recommended_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, FALSE);
3867 all_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, TRUE);
3869 infos = NULL;
3870 for (i = 0; all_ids[i]; i++)
3872 GDesktopAppInfo *info;
3873 gint j;
3875 /* Don't return the ones on the recommended list */
3876 for (j = 0; recommended_ids[j]; j++)
3877 if (g_str_equal (all_ids[i], recommended_ids[j]))
3878 break;
3880 if (recommended_ids[j])
3881 continue;
3883 info = g_desktop_app_info_new (all_ids[i]);
3885 if (info)
3886 infos = g_list_prepend (infos, info);
3889 g_strfreev (recommended_ids);
3890 g_strfreev (all_ids);
3892 return g_list_reverse (infos);
3896 * g_app_info_get_all_for_type:
3897 * @content_type: the content type to find a #GAppInfo for
3899 * Gets a list of all #GAppInfos for a given content type,
3900 * including the recommended and fallback #GAppInfos. See
3901 * g_app_info_get_recommended_for_type() and
3902 * g_app_info_get_fallback_for_type().
3904 * Returns: (element-type GAppInfo) (transfer full): #GList of #GAppInfos
3905 * for given @content_type or %NULL on error.
3907 GList *
3908 g_app_info_get_all_for_type (const char *content_type)
3910 gchar **desktop_ids;
3911 GList *infos;
3912 gint i;
3914 g_return_val_if_fail (content_type != NULL, NULL);
3916 desktop_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, TRUE);
3918 infos = NULL;
3919 for (i = 0; desktop_ids[i]; i++)
3921 GDesktopAppInfo *info;
3923 info = g_desktop_app_info_new (desktop_ids[i]);
3924 if (info)
3925 infos = g_list_prepend (infos, info);
3928 g_strfreev (desktop_ids);
3930 return g_list_reverse (infos);
3934 * g_app_info_reset_type_associations:
3935 * @content_type: a content type
3937 * Removes all changes to the type associations done by
3938 * g_app_info_set_as_default_for_type(),
3939 * g_app_info_set_as_default_for_extension(),
3940 * g_app_info_add_supports_type() or
3941 * g_app_info_remove_supports_type().
3943 * Since: 2.20
3945 void
3946 g_app_info_reset_type_associations (const char *content_type)
3948 update_mimeapps_list (NULL, content_type,
3949 UPDATE_MIME_NONE,
3950 NULL);
3954 * g_app_info_get_default_for_type:
3955 * @content_type: the content type to find a #GAppInfo for
3956 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
3957 * support URIs
3959 * Gets the default #GAppInfo for a given content type.
3961 * Returns: (transfer full): #GAppInfo for given @content_type or
3962 * %NULL on error.
3964 GAppInfo *
3965 g_app_info_get_default_for_type (const char *content_type,
3966 gboolean must_support_uris)
3968 gchar **desktop_ids;
3969 GAppInfo *info;
3970 gint i;
3972 g_return_val_if_fail (content_type != NULL, NULL);
3974 desktop_ids = g_desktop_app_info_get_defaults_for_content_type (content_type);
3976 info = NULL;
3977 for (i = 0; desktop_ids[i]; i++)
3979 info = (GAppInfo *) g_desktop_app_info_new (desktop_ids[i]);
3981 if (info)
3983 if (!must_support_uris || g_app_info_supports_uris (info))
3984 break;
3986 g_object_unref (info);
3987 info = NULL;
3991 g_strfreev (desktop_ids);
3993 /* If we can't find a default app for this content type, pick one from
3994 * the list of all supported apps. This will be ordered by the user's
3995 * preference and by "recommended" apps first, so the first one we
3996 * find is probably the best fallback.
3998 if (info == NULL)
4000 desktop_ids = g_desktop_app_info_get_desktop_ids_for_content_type (content_type, TRUE);
4002 for (i = 0; desktop_ids[i]; i++)
4004 info = (GAppInfo *) g_desktop_app_info_new (desktop_ids[i]);
4006 if (info)
4008 if (!must_support_uris || g_app_info_supports_uris (info))
4009 break;
4011 g_object_unref (info);
4012 info = NULL;
4016 g_strfreev (desktop_ids);
4019 return info;
4023 * g_app_info_get_default_for_uri_scheme:
4024 * @uri_scheme: a string containing a URI scheme.
4026 * Gets the default application for handling URIs with
4027 * the given URI scheme. A URI scheme is the initial part
4028 * of the URI, up to but not including the ':', e.g. "http",
4029 * "ftp" or "sip".
4031 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4033 GAppInfo *
4034 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
4036 GAppInfo *app_info;
4037 char *content_type, *scheme_down;
4039 scheme_down = g_ascii_strdown (uri_scheme, -1);
4040 content_type = g_strdup_printf ("x-scheme-handler/%s", scheme_down);
4041 g_free (scheme_down);
4042 app_info = g_app_info_get_default_for_type (content_type, FALSE);
4043 g_free (content_type);
4045 return app_info;
4048 /* "Get all" API {{{2 */
4051 * g_desktop_app_info_get_implementations:
4052 * @interface: the name of the interface
4054 * Gets all applications that implement @interface.
4056 * An application implements an interface if that interface is listed in
4057 * the Implements= line of the desktop file of the application.
4059 * Since: 2.42
4061 GList *
4062 g_desktop_app_info_get_implementations (const gchar *interface)
4064 GList *result = NULL;
4065 GList **ptr;
4066 gint i;
4068 desktop_file_dirs_lock ();
4070 for (i = 0; i < n_desktop_file_dirs; i++)
4071 desktop_file_dir_get_implementations (&desktop_file_dirs[i], &result, interface);
4073 desktop_file_dirs_unlock ();
4075 ptr = &result;
4076 while (*ptr)
4078 gchar *name = (*ptr)->data;
4079 GDesktopAppInfo *app;
4081 app = g_desktop_app_info_new (name);
4082 g_free (name);
4084 if (app)
4086 (*ptr)->data = app;
4087 ptr = &(*ptr)->next;
4089 else
4090 *ptr = g_list_delete_link (*ptr, *ptr);
4093 return result;
4097 * g_desktop_app_info_search:
4098 * @search_string: the search string to use
4100 * Searches desktop files for ones that match @search_string.
4102 * The return value is an array of strvs. Each strv contains a list of
4103 * applications that matched @search_string with an equal score. The
4104 * outer list is sorted by score so that the first strv contains the
4105 * best-matching applications, and so on.
4106 * The algorithm for determining matches is undefined and may change at
4107 * any time.
4109 * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a
4110 * list of strvs. Free each item with g_strfreev() and free the outer
4111 * list with g_free().
4113 gchar ***
4114 g_desktop_app_info_search (const gchar *search_string)
4116 gchar **search_tokens;
4117 gint last_category = -1;
4118 gchar ***results;
4119 gint n_categories = 0;
4120 gint start_of_category;
4121 gint i, j;
4123 search_tokens = g_str_tokenize_and_fold (search_string, NULL, NULL);
4125 desktop_file_dirs_lock ();
4127 reset_total_search_results ();
4129 for (i = 0; i < n_desktop_file_dirs; i++)
4131 for (j = 0; search_tokens[j]; j++)
4133 desktop_file_dir_search (&desktop_file_dirs[i], search_tokens[j]);
4134 merge_token_results (j == 0);
4136 merge_directory_results ();
4139 sort_total_search_results ();
4141 /* Count the total number of unique categories */
4142 for (i = 0; i < static_total_results_size; i++)
4143 if (static_total_results[i].category != last_category)
4145 last_category = static_total_results[i].category;
4146 n_categories++;
4149 results = g_new (gchar **, n_categories + 1);
4151 /* Start loading into the results list */
4152 start_of_category = 0;
4153 for (i = 0; i < n_categories; i++)
4155 gint n_items_in_category = 0;
4156 gint this_category;
4157 gint j;
4159 this_category = static_total_results[start_of_category].category;
4161 while (start_of_category + n_items_in_category < static_total_results_size &&
4162 static_total_results[start_of_category + n_items_in_category].category == this_category)
4163 n_items_in_category++;
4165 results[i] = g_new (gchar *, n_items_in_category + 1);
4166 for (j = 0; j < n_items_in_category; j++)
4167 results[i][j] = g_strdup (static_total_results[start_of_category + j].app_name);
4168 results[i][j] = NULL;
4170 start_of_category += n_items_in_category;
4172 results[i] = NULL;
4174 desktop_file_dirs_unlock ();
4176 g_strfreev (search_tokens);
4178 return results;
4182 * g_app_info_get_all:
4184 * Gets a list of all of the applications currently registered
4185 * on this system.
4187 * For desktop files, this includes applications that have
4188 * `NoDisplay=true` set or are excluded from display by means
4189 * of `OnlyShowIn` or `NotShowIn`. See g_app_info_should_show().
4190 * The returned list does not include applications which have
4191 * the `Hidden` key set.
4193 * Returns: (element-type GAppInfo) (transfer full): a newly allocated #GList of references to #GAppInfos.
4195 GList *
4196 g_app_info_get_all (void)
4198 GHashTable *apps;
4199 GHashTableIter iter;
4200 gpointer value;
4201 int i;
4202 GList *infos;
4204 apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4206 desktop_file_dirs_lock ();
4208 for (i = 0; i < n_desktop_file_dirs; i++)
4209 desktop_file_dir_get_all (&desktop_file_dirs[i], apps);
4211 desktop_file_dirs_unlock ();
4213 infos = NULL;
4214 g_hash_table_iter_init (&iter, apps);
4215 while (g_hash_table_iter_next (&iter, NULL, &value))
4217 if (value)
4218 infos = g_list_prepend (infos, value);
4221 g_hash_table_destroy (apps);
4223 return infos;
4226 /* GDesktopAppInfoLookup interface {{{2 */
4228 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
4230 typedef GDesktopAppInfoLookupIface GDesktopAppInfoLookupInterface;
4231 G_DEFINE_INTERFACE (GDesktopAppInfoLookup, g_desktop_app_info_lookup, G_TYPE_OBJECT)
4233 static void
4234 g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface)
4238 /* "Get for mime type" APIs {{{2 */
4241 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
4242 * @lookup: a #GDesktopAppInfoLookup
4243 * @uri_scheme: a string containing a URI scheme.
4245 * Gets the default application for launching applications
4246 * using this URI scheme for a particular GDesktopAppInfoLookup
4247 * implementation.
4249 * The GDesktopAppInfoLookup interface and this function is used
4250 * to implement g_app_info_get_default_for_uri_scheme() backends
4251 * in a GIO module. There is no reason for applications to use it
4252 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
4254 * Returns: (transfer full): #GAppInfo for given @uri_scheme or %NULL on error.
4256 * Deprecated: The #GDesktopAppInfoLookup interface is deprecated and unused by gio.
4258 GAppInfo *
4259 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
4260 const char *uri_scheme)
4262 GDesktopAppInfoLookupIface *iface;
4264 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
4266 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
4268 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
4271 G_GNUC_END_IGNORE_DEPRECATIONS
4273 /* Misc getter APIs {{{2 */
4276 * g_desktop_app_info_get_startup_wm_class:
4277 * @info: a #GDesktopAppInfo that supports startup notify
4279 * Retrieves the StartupWMClass field from @info. This represents the
4280 * WM_CLASS property of the main window of the application, if launched
4281 * through @info.
4283 * Returns: (transfer none): the startup WM class, or %NULL if none is set
4284 * in the desktop file.
4286 * Since: 2.34
4288 const char *
4289 g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo *info)
4291 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4293 return info->startup_wm_class;
4297 * g_desktop_app_info_get_string:
4298 * @info: a #GDesktopAppInfo
4299 * @key: the key to look up
4301 * Looks up a string value in the keyfile backing @info.
4303 * The @key is looked up in the "Desktop Entry" group.
4305 * Returns: a newly allocated string, or %NULL if the key
4306 * is not found
4308 * Since: 2.36
4310 char *
4311 g_desktop_app_info_get_string (GDesktopAppInfo *info,
4312 const char *key)
4314 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4316 return g_key_file_get_string (info->keyfile,
4317 G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4321 * g_desktop_app_info_get_boolean:
4322 * @info: a #GDesktopAppInfo
4323 * @key: the key to look up
4325 * Looks up a boolean value in the keyfile backing @info.
4327 * The @key is looked up in the "Desktop Entry" group.
4329 * Returns: the boolean value, or %FALSE if the key
4330 * is not found
4332 * Since: 2.36
4334 gboolean
4335 g_desktop_app_info_get_boolean (GDesktopAppInfo *info,
4336 const char *key)
4338 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
4340 return g_key_file_get_boolean (info->keyfile,
4341 G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4345 * g_desktop_app_info_has_key:
4346 * @info: a #GDesktopAppInfo
4347 * @key: the key to look up
4349 * Returns whether @key exists in the "Desktop Entry" group
4350 * of the keyfile backing @info.
4352 * Returns: %TRUE if the @key exists
4354 * Since: 2.36
4356 gboolean
4357 g_desktop_app_info_has_key (GDesktopAppInfo *info,
4358 const char *key)
4360 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE);
4362 return g_key_file_has_key (info->keyfile,
4363 G_KEY_FILE_DESKTOP_GROUP, key, NULL);
4366 /* Desktop actions support {{{2 */
4369 * g_desktop_app_info_list_actions:
4370 * @info: a #GDesktopAppInfo
4372 * Returns the list of "additional application actions" supported on the
4373 * desktop file, as per the desktop file specification.
4375 * As per the specification, this is the list of actions that are
4376 * explicitly listed in the "Actions" key of the [Desktop Entry] group.
4378 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL
4380 * Since: 2.38
4382 const gchar * const *
4383 g_desktop_app_info_list_actions (GDesktopAppInfo *info)
4385 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4387 return (const gchar **) info->actions;
4390 static gboolean
4391 app_info_has_action (GDesktopAppInfo *info,
4392 const gchar *action_name)
4394 gint i;
4396 for (i = 0; info->actions[i]; i++)
4397 if (g_str_equal (info->actions[i], action_name))
4398 return TRUE;
4400 return FALSE;
4404 * g_desktop_app_info_get_action_name:
4405 * @info: a #GDesktopAppInfo
4406 * @action_name: the name of the action as from
4407 * g_desktop_app_info_list_actions()
4409 * Gets the user-visible display name of the "additional application
4410 * action" specified by @action_name.
4412 * This corresponds to the "Name" key within the keyfile group for the
4413 * action.
4415 * Returns: (transfer full): the locale-specific action name
4417 * Since: 2.38
4419 gchar *
4420 g_desktop_app_info_get_action_name (GDesktopAppInfo *info,
4421 const gchar *action_name)
4423 gchar *group_name;
4424 gchar *result;
4426 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), NULL);
4427 g_return_val_if_fail (action_name != NULL, NULL);
4428 g_return_val_if_fail (app_info_has_action (info, action_name), NULL);
4430 group_name = g_strdup_printf ("Desktop Action %s", action_name);
4431 result = g_key_file_get_locale_string (info->keyfile, group_name, "Name", NULL, NULL);
4432 g_free (group_name);
4434 /* The spec says that the Name field must be given.
4436 * If it's not, let's follow the behaviour of our get_name()
4437 * implementation above and never return %NULL.
4439 if (result == NULL)
4440 result = g_strdup (_("Unnamed"));
4442 return result;
4446 * g_desktop_app_info_launch_action:
4447 * @info: a #GDesktopAppInfo
4448 * @action_name: the name of the action as from
4449 * g_desktop_app_info_list_actions()
4450 * @launch_context: (allow-none): a #GAppLaunchContext
4452 * Activates the named application action.
4454 * You may only call this function on action names that were
4455 * returned from g_desktop_app_info_list_actions().
4457 * Note that if the main entry of the desktop file indicates that the
4458 * application supports startup notification, and @launch_context is
4459 * non-%NULL, then startup notification will be used when activating the
4460 * action (and as such, invocation of the action on the receiving side
4461 * must signal the end of startup notification when it is completed).
4462 * This is the expected behaviour of applications declaring additional
4463 * actions, as per the desktop file specification.
4465 * As with g_app_info_launch() there is no way to detect failures that
4466 * occur while using this function.
4468 * Since: 2.38
4470 void
4471 g_desktop_app_info_launch_action (GDesktopAppInfo *info,
4472 const gchar *action_name,
4473 GAppLaunchContext *launch_context)
4475 GDBusConnection *session_bus;
4477 g_return_if_fail (G_IS_DESKTOP_APP_INFO (info));
4478 g_return_if_fail (action_name != NULL);
4479 g_return_if_fail (app_info_has_action (info, action_name));
4481 session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
4483 if (session_bus && info->app_id)
4485 gchar *object_path;
4487 object_path = object_path_from_appid (info->app_id);
4488 g_dbus_connection_call (session_bus, info->app_id, object_path,
4489 "org.freedesktop.Application", "ActivateAction",
4490 g_variant_new ("(sav@a{sv})", action_name, NULL,
4491 g_desktop_app_info_make_platform_data (info, NULL, launch_context)),
4492 NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
4493 g_free (object_path);
4495 else
4497 gchar *group_name;
4498 gchar *exec_line;
4500 group_name = g_strdup_printf ("Desktop Action %s", action_name);
4501 exec_line = g_key_file_get_string (info->keyfile, group_name, "Exec", NULL);
4502 g_free (group_name);
4504 if (exec_line)
4505 g_desktop_app_info_launch_uris_with_spawn (info, session_bus, exec_line, NULL, launch_context,
4506 _SPAWN_FLAGS_DEFAULT, NULL, NULL, NULL, NULL, NULL);
4509 if (session_bus != NULL)
4511 g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
4512 g_object_unref (session_bus);
4515 /* Epilogue {{{1 */
4517 /* vim:set foldmethod=marker: */