X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdesktopappinfo.c;h=7bfc904719c73e3af44f8a15d5423aa16336c77c;hb=853692bdfd9f8a87aed70d21f643dc13b57c92d1;hp=0b72803bffe51bf515cd32afb8dcdf1f46737b72;hpb=afaee8dc6ad72281dd1a51ebae58aaabf0efdb05;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 0b72803..7bfc904 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -132,20 +132,19 @@ typedef enum { G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO, g_desktop_app_info_iface_init)) -G_LOCK_DEFINE_STATIC (g_desktop_env); -static gchar *g_desktop_env = NULL; - /* DesktopFileDir implementation {{{1 */ typedef struct { gchar *path; + gchar *alternatively_watching; gboolean is_config; gboolean is_setup; GLocalDirectoryMonitor *monitor; GHashTable *app_names; GHashTable *mime_tweaks; GHashTable *memory_index; + GHashTable *memory_implementations; } DesktopFileDir; static DesktopFileDir *desktop_file_dirs; @@ -157,6 +156,52 @@ static GMutex desktop_file_dir_lock; /* Monitor 'changed' signal handler {{{2 */ static void desktop_file_dir_reset (DesktopFileDir *dir); +/*< internal > + * desktop_file_dir_get_alternative_dir: + * @dir: a #DesktopFileDir + * + * Gets the "alternative" directory to monitor in case the path + * doesn't exist. + * + * If the path exists this will return NULL, otherwise it will return a + * parent directory of the path. + * + * This is used to avoid inotify on a non-existent directory (which + * results in polling). + * + * See https://bugzilla.gnome.org/show_bug.cgi?id=522314 for more info. + */ +static gchar * +desktop_file_dir_get_alternative_dir (DesktopFileDir *dir) +{ + gchar *parent; + + /* If the directory itself exists then we need no alternative. */ + if (g_access (dir->path, R_OK | X_OK) == 0) + return NULL; + + /* Otherwise, try the parent directories until we find one. */ + parent = g_path_get_dirname (dir->path); + + while (g_access (parent, R_OK | X_OK) != 0) + { + gchar *tmp = parent; + + parent = g_path_get_dirname (tmp); + + /* If somehow we get to '/' or '.' then just stop... */ + if (g_str_equal (parent, tmp)) + { + g_free (tmp); + break; + } + + g_free (tmp); + } + + return parent; +} + static void desktop_file_dir_changed (GFileMonitor *monitor, GFile *file, @@ -165,6 +210,7 @@ desktop_file_dir_changed (GFileMonitor *monitor, gpointer user_data) { DesktopFileDir *dir = user_data; + gboolean do_nothing = FALSE; /* We are not interested in receiving notifications forever just * because someone asked about one desktop file once. @@ -172,15 +218,30 @@ desktop_file_dir_changed (GFileMonitor *monitor, * After we receive the first notification, reset the dir, destroying * the monitor. We will take this as a hint, next time that we are * asked, that we need to check if everything is up to date. + * + * If this is a notification for a parent directory (because the + * desktop directory didn't exist) then we shouldn't fire the signal + * unless something actually changed. */ g_mutex_lock (&desktop_file_dir_lock); - desktop_file_dir_reset (dir); + if (dir->alternatively_watching) + { + gchar *alternative_dir; + + alternative_dir = desktop_file_dir_get_alternative_dir (dir); + do_nothing = alternative_dir && g_str_equal (dir->alternatively_watching, alternative_dir); + g_free (alternative_dir); + } + + if (!do_nothing) + desktop_file_dir_reset (dir); g_mutex_unlock (&desktop_file_dir_lock); /* Notify anyone else who may be interested */ - g_app_info_monitor_fire (); + if (!do_nothing) + g_app_info_monitor_fire (); } /* Internal utility functions {{{2 */ @@ -242,6 +303,29 @@ get_lowercase_current_desktops (void) return (const gchar **) result; } +static const gchar * const * +get_current_desktops (const gchar *value) +{ + static gchar **result; + + if (g_once_init_enter (&result)) + { + gchar **tmp; + + if (!value) + value = g_getenv ("XDG_CURRENT_DESKTOP"); + + if (!value) + value = ""; + + tmp = g_strsplit (value, ":", 0); + + g_once_init_leave (&result, tmp); + } + + return (const gchar **) result; +} + /*< internal > * add_to_table_if_appropriate: * @apps: a string to GDesktopAppInfo hash table @@ -310,13 +394,13 @@ desktop_key_get_name (guint key_id) case DESKTOP_KEY_Exec: return "Exec"; case DESKTOP_KEY_GenericName: - return "GenericName"; + return GENERIC_NAME_KEY; case DESKTOP_KEY_Keywords: - return "Keywords"; + return KEYWORDS_KEY; case DESKTOP_KEY_Name: return "Name"; case DESKTOP_KEY_X_GNOME_FullName: - return "X-GNOME-FullName"; + return FULL_NAME_KEY; default: g_assert_not_reached (); } @@ -620,7 +704,7 @@ desktop_file_dir_unindexed_get_tweaks (DesktopFileDir *dir, gchar *unaliased_type; unaliased_type = _g_unix_content_type_unalias (mime_type); - tweaks = g_hash_table_lookup (dir->mime_tweaks, mime_type); + tweaks = g_hash_table_lookup (dir->mime_tweaks, unaliased_type); if (tweaks == NULL) { @@ -686,8 +770,6 @@ desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir, const gchar *added_group, gboolean tweaks_permitted) { - const gchar default_group[] = "Default Applications"; - const gchar removed_group[] = "Removed Assocations"; UnindexedMimeTweaks *tweaks; char **desktop_file_ids; GKeyFile *key_file; @@ -727,12 +809,12 @@ desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir, g_strfreev (mime_types); } - mime_types = g_key_file_get_keys (key_file, removed_group, NULL, NULL); + mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL); if G_UNLIKELY (mime_types != NULL && !tweaks_permitted) { g_warning ("%s contains a [%s] group, but it is not permitted here. Only the non-desktop-specific " - "mimeapps.list file may add or remove associations.", filename, removed_group); + "mimeapps.list file may add or remove associations.", filename, REMOVED_ASSOCIATIONS_GROUP); g_strfreev (mime_types); mime_types = NULL; } @@ -741,7 +823,7 @@ desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir, { for (i = 0; mime_types[i] != NULL; i++) { - desktop_file_ids = g_key_file_get_string_list (key_file, removed_group, mime_types[i], NULL, NULL); + desktop_file_ids = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP, mime_types[i], NULL, NULL); if (desktop_file_ids) { @@ -753,13 +835,13 @@ desktop_file_dir_unindexed_read_mimeapps_list (DesktopFileDir *dir, g_strfreev (mime_types); } - mime_types = g_key_file_get_keys (key_file, default_group, NULL, NULL); + mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP, NULL, NULL); if (mime_types != NULL) { for (i = 0; mime_types[i] != NULL; i++) { - desktop_file_ids = g_key_file_get_string_list (key_file, default_group, mime_types[i], NULL, NULL); + desktop_file_ids = g_key_file_get_string_list (key_file, DEFAULT_APPLICATIONS_GROUP, mime_types[i], NULL, NULL); if (desktop_file_ids) { @@ -794,13 +876,13 @@ desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir *dir) for (i = 0; desktops[i]; i++) { filename = g_strdup_printf ("%s/%s-mimeapps.list", dir->path, desktops[i]); - desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", FALSE); + desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, FALSE); g_free (filename); } /* Next, the non-desktop-specific mimeapps.list */ filename = g_strdup_printf ("%s/mimeapps.list", dir->path); - desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", TRUE); + desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, TRUE); g_free (filename); /* The remaining files are only checked for in directories that might @@ -815,14 +897,14 @@ desktop_file_dir_unindexed_read_mimeapps_lists (DesktopFileDir *dir) * version. */ filename = g_strdup_printf ("%s/defaults.list", dir->path); - desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "Added Associations", FALSE); + desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, ADDED_ASSOCIATIONS_GROUP, FALSE); g_free (filename); /* Finally, the mimeinfo.cache, which is just a cached copy of what we * would find in the MimeTypes= lines of all of the desktop files. */ filename = g_strdup_printf ("%s/mimeinfo.cache", dir->path); - desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, "MIME Cache", TRUE); + desktop_file_dir_unindexed_read_mimeapps_list (dir, filename, MIME_CACHE_GROUP, TRUE); g_free (filename); } @@ -954,6 +1036,7 @@ desktop_file_dir_unindexed_setup_search (DesktopFileDir *dir) gpointer app, path; dir->memory_index = memory_index_new (); + dir->memory_implementations = memory_index_new (); /* Nothing to search? */ if (dir->app_names == NULL) @@ -973,6 +1056,7 @@ desktop_file_dir_unindexed_setup_search (DesktopFileDir *dir) !g_key_file_get_boolean (key_file, "Desktop Entry", "Hidden", NULL)) { /* Index the interesting keys... */ + gchar **implements; gint i; for (i = 0; i < G_N_ELEMENTS (desktop_key_match_category); i++) @@ -1006,6 +1090,12 @@ desktop_file_dir_unindexed_setup_search (DesktopFileDir *dir) g_free (raw); } + + /* Make note of the Implements= line */ + implements = g_key_file_get_string_list (key_file, "Desktop Entry", "Implements", NULL, NULL); + for (i = 0; implements && implements[i]; i++) + memory_index_add_token (dir->memory_implementations, implements[i], 0, app); + g_strfreev (implements); } g_key_file_free (key_file); @@ -1112,6 +1202,20 @@ desktop_file_dir_unindexed_default_lookup (DesktopFileDir *dir, } } +static void +desktop_file_dir_unindexed_get_implementations (DesktopFileDir *dir, + GList **results, + const gchar *interface) +{ + MemoryIndexEntry *mie; + + if (!dir->memory_index) + desktop_file_dir_unindexed_setup_search (dir); + + for (mie = g_hash_table_lookup (dir->memory_implementations, interface); mie; mie = mie->next) + *results = g_list_prepend (*results, g_strdup (mie->app_name)); +} + /* DesktopFileDir "API" {{{2 */ /*< internal > @@ -1164,6 +1268,12 @@ desktop_file_dir_create_for_config (GArray *array, static void desktop_file_dir_reset (DesktopFileDir *dir) { + if (dir->alternatively_watching) + { + g_free (dir->alternatively_watching); + dir->alternatively_watching = NULL; + } + if (dir->monitor) { g_signal_handlers_disconnect_by_func (dir->monitor, desktop_file_dir_changed, dir); @@ -1189,6 +1299,12 @@ desktop_file_dir_reset (DesktopFileDir *dir) dir->mime_tweaks = NULL; } + if (dir->memory_implementations) + { + g_hash_table_unref (dir->memory_implementations); + dir->memory_implementations = NULL; + } + dir->is_setup = FALSE; } @@ -1203,10 +1319,23 @@ desktop_file_dir_reset (DesktopFileDir *dir) static void desktop_file_dir_init (DesktopFileDir *dir) { + const gchar *watch_dir; + g_assert (!dir->is_setup); + g_assert (!dir->alternatively_watching); g_assert (!dir->monitor); - dir->monitor = g_local_directory_monitor_new_in_worker (dir->path, G_FILE_MONITOR_NONE, NULL); + + dir->alternatively_watching = desktop_file_dir_get_alternative_dir (dir); + watch_dir = dir->alternatively_watching ? dir->alternatively_watching : dir->path; + + /* There is a very thin race here if the watch_dir has been _removed_ + * between when we checked for it and when we establish the watch. + * Removes probably don't happen in usual operation, and even if it + * does (and we catch the unlikely race), the only degradation is that + * we will fall back to polling. + */ + dir->monitor = g_local_directory_monitor_new_in_worker (watch_dir, G_FILE_MONITOR_NONE, NULL); if (dir->monitor) { @@ -1311,6 +1440,14 @@ desktop_file_dir_search (DesktopFileDir *dir, desktop_file_dir_unindexed_search (dir, search_token); } +static void +desktop_file_dir_get_implementations (DesktopFileDir *dir, + GList **results, + const gchar *interface) +{ + desktop_file_dir_unindexed_get_implementations (dir, results, interface); +} + /* Lock/unlock and global setup API {{{2 */ static void @@ -2003,14 +2140,16 @@ g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info) /** * g_desktop_app_info_get_show_in: * @info: a #GDesktopAppInfo - * @desktop_env: a string specifying a desktop name + * @desktop_env: (nullable): a string specifying a desktop name * * Checks if the application info should be shown in menus that list available * applications for a specific name of the desktop, based on the * `OnlyShowIn` and `NotShowIn` keys. * - * If @desktop_env is %NULL, then the name of the desktop set with - * g_desktop_app_info_set_desktop_env() is used. + * @desktop_env should typically be given as %NULL, in which case the + * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want + * to override the default mechanism then you may specify @desktop_env, + * but this is not recommended. * * Note that g_app_info_should_show() for @info will include this check (with * %NULL for @desktop_env) as well as additional checks. @@ -2025,45 +2164,33 @@ gboolean g_desktop_app_info_get_show_in (GDesktopAppInfo *info, const gchar *desktop_env) { - gboolean found; - int i; + const gchar *specified_envs[] = { desktop_env, NULL }; + const gchar * const *envs; + gint i; g_return_val_if_fail (G_IS_DESKTOP_APP_INFO (info), FALSE); - if (!desktop_env) { - G_LOCK (g_desktop_env); - desktop_env = g_desktop_env; - G_UNLOCK (g_desktop_env); - } + if (desktop_env) + envs = specified_envs; + else + envs = get_current_desktops (NULL); - if (info->only_show_in) + for (i = 0; envs[i]; i++) { - if (desktop_env == NULL) - return FALSE; + gint j; - found = FALSE; - for (i = 0; info->only_show_in[i] != NULL; i++) - { - if (strcmp (info->only_show_in[i], desktop_env) == 0) - { - found = TRUE; - break; - } - } - if (!found) - return FALSE; - } + if (info->only_show_in) + for (j = 0; info->only_show_in[j]; j++) + if (g_str_equal (info->only_show_in[j], envs[i])) + return TRUE; - if (info->not_show_in && desktop_env) - { - for (i = 0; info->not_show_in[i] != NULL; i++) - { - if (strcmp (info->not_show_in[i], desktop_env) == 0) + if (info->not_show_in) + for (j = 0; info->not_show_in[j]; j++) + if (g_str_equal (info->not_show_in[j], envs[i])) return FALSE; - } } - return TRUE; + return info->only_show_in == NULL; } /* Launching... {{{2 */ @@ -2921,26 +3048,15 @@ g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo, * `OnlyShowIn` and `NotShowIn` * desktop entry fields. * - * The - * [Desktop Menu specification](http://standards.freedesktop.org/menu-spec/latest/) - * recognizes the following: - * - GNOME - * - KDE - * - ROX - * - XFCE - * - LXDE - * - Unity - * - Old - * * Should be called only once; subsequent calls are ignored. + * + * Deprecated:2.42:do not use this API. Since 2.42 the value of the + * `XDG_CURRENT_DESKTOP` environment variable will be used. */ void g_desktop_app_info_set_desktop_env (const gchar *desktop_env) { - G_LOCK (g_desktop_env); - if (!g_desktop_env) - g_desktop_env = g_strdup (desktop_env); - G_UNLOCK (g_desktop_env); + get_current_desktops (desktop_env); } static gboolean @@ -4012,6 +4128,55 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme) /* "Get all" API {{{2 */ /** + * g_desktop_app_info_get_implementations: + * @interface: the name of the interface + * + * Gets all applications that implement @interface. + * + * An application implements an interface if that interface is listed in + * the Implements= line of the desktop file of the application. + * + * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo + * objects. + * + * Since: 2.42 + **/ +GList * +g_desktop_app_info_get_implementations (const gchar *interface) +{ + GList *result = NULL; + GList **ptr; + gint i; + + desktop_file_dirs_lock (); + + for (i = 0; i < n_desktop_file_dirs; i++) + desktop_file_dir_get_implementations (&desktop_file_dirs[i], &result, interface); + + desktop_file_dirs_unlock (); + + ptr = &result; + while (*ptr) + { + gchar *name = (*ptr)->data; + GDesktopAppInfo *app; + + app = g_desktop_app_info_new (name); + g_free (name); + + if (app) + { + (*ptr)->data = app; + ptr = &(*ptr)->next; + } + else + *ptr = g_list_delete_link (*ptr, *ptr); + } + + return result; +} + +/** * g_desktop_app_info_search: * @search_string: the search string to use *