|
|
1be690 |
From a3fc35a2b452855d004549afbec57d1b4f36c917 Mon Sep 17 00:00:00 2001
|
|
|
e2a246 |
From: Christian Hergert <chergert@redhat.com>
|
|
|
e2a246 |
Date: Thu, 27 Feb 2020 19:36:14 -0800
|
|
|
1be690 |
Subject: [PATCH 5/6] app-cache: add ShellAppCache for GAppInfo caching
|
|
|
e2a246 |
|
|
|
e2a246 |
This caches GAppInfo so that the compositor thread does not have to perform
|
|
|
e2a246 |
costly disk access to load them. Instead, they are loaded from a worker
|
|
|
e2a246 |
thread and the ShellAppCache notifies of changes.
|
|
|
e2a246 |
|
|
|
e2a246 |
To simplify maintenance, ShellAppCache manages this directly and the
|
|
|
e2a246 |
existing ShellAppSystem wraps the cache. We may want to graft these
|
|
|
e2a246 |
together in the future, but now it provides the easiest way to backport
|
|
|
e2a246 |
changes to older Shell releases.
|
|
|
e2a246 |
|
|
|
e2a246 |
Another source of compositor thread disk access was in determining the
|
|
|
e2a246 |
name for an application directory. Translations are provided via GKeyFile
|
|
|
e2a246 |
installed in "desktop-directories". Each time we would build the name
|
|
|
e2a246 |
for a label (or update it) we would have to load all of these files.
|
|
|
e2a246 |
|
|
|
e2a246 |
Instead, the ShellAppCache caches that information and updates the cache
|
|
|
e2a246 |
in bulk when those change. We can reduce this in the future to do less
|
|
|
e2a246 |
work, but chances are these will come together anyway so that is probably
|
|
|
e2a246 |
worth fixing if we ever come across it.
|
|
|
e2a246 |
|
|
|
e2a246 |
https://gitlab.gnome.org/GNOME/gnome-shell/issues/2282
|
|
|
e2a246 |
---
|
|
|
e2a246 |
js/ui/appDisplay.js | 12 +-
|
|
|
e2a246 |
src/meson.build | 5 +-
|
|
|
e2a246 |
src/shell-app-cache-private.h | 19 ++
|
|
|
e2a246 |
src/shell-app-cache.c | 404 ++++++++++++++++++++++++++++++++++
|
|
|
e2a246 |
src/shell-app-system.c | 34 ++-
|
|
|
e2a246 |
src/shell-util.c | 16 ++
|
|
|
e2a246 |
src/shell-util.h | 2 +
|
|
|
e2a246 |
7 files changed, 463 insertions(+), 29 deletions(-)
|
|
|
e2a246 |
create mode 100644 src/shell-app-cache-private.h
|
|
|
e2a246 |
create mode 100644 src/shell-app-cache.c
|
|
|
e2a246 |
|
|
|
e2a246 |
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
|
|
|
e2a246 |
index 7fad02cd0..a2d691085 100644
|
|
|
e2a246 |
--- a/js/ui/appDisplay.js
|
|
|
e2a246 |
+++ b/js/ui/appDisplay.js
|
|
|
e2a246 |
@@ -78,15 +78,9 @@ function _getFolderName(folder) {
|
|
|
e2a246 |
let name = folder.get_string('name');
|
|
|
e2a246 |
|
|
|
e2a246 |
if (folder.get_boolean('translate')) {
|
|
|
e2a246 |
- let keyfile = new GLib.KeyFile();
|
|
|
e2a246 |
- let path = 'desktop-directories/' + name;
|
|
|
e2a246 |
-
|
|
|
e2a246 |
- try {
|
|
|
e2a246 |
- keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
|
|
|
e2a246 |
- name = keyfile.get_locale_string('Desktop Entry', 'Name', null);
|
|
|
e2a246 |
- } catch(e) {
|
|
|
e2a246 |
- return name;
|
|
|
e2a246 |
- }
|
|
|
e2a246 |
+ let translated = Shell.util_get_translated_folder_name(name);
|
|
|
e2a246 |
+ if (translated !== null)
|
|
|
e2a246 |
+ return translated;
|
|
|
e2a246 |
}
|
|
|
e2a246 |
|
|
|
e2a246 |
return name;
|
|
|
e2a246 |
diff --git a/src/meson.build b/src/meson.build
|
|
|
e2a246 |
index 97a5a796c..2b911d347 100644
|
|
|
e2a246 |
--- a/src/meson.build
|
|
|
e2a246 |
+++ b/src/meson.build
|
|
|
e2a246 |
@@ -108,6 +108,7 @@ endif
|
|
|
e2a246 |
|
|
|
e2a246 |
libshell_private_headers = [
|
|
|
e2a246 |
'shell-app-private.h',
|
|
|
e2a246 |
+ 'shell-app-cache-private.h',
|
|
|
e2a246 |
'shell-app-system-private.h',
|
|
|
e2a246 |
'shell-global-private.h',
|
|
|
e2a246 |
'shell-window-tracker-private.h',
|
|
|
e2a246 |
@@ -146,7 +147,9 @@ if have_networkmanager
|
|
|
e2a246 |
libshell_sources += 'shell-network-agent.c'
|
|
|
e2a246 |
endif
|
|
|
e2a246 |
|
|
|
e2a246 |
-libshell_private_sources = []
|
|
|
e2a246 |
+libshell_private_sources = [
|
|
|
e2a246 |
+ 'shell-app-cache.c',
|
|
|
e2a246 |
+]
|
|
|
e2a246 |
|
|
|
e2a246 |
if enable_recorder
|
|
|
e2a246 |
libshell_sources += ['shell-recorder.c']
|
|
|
e2a246 |
diff --git a/src/shell-app-cache-private.h b/src/shell-app-cache-private.h
|
|
|
e2a246 |
new file mode 100644
|
|
|
e2a246 |
index 000000000..b73094ab1
|
|
|
e2a246 |
--- /dev/null
|
|
|
e2a246 |
+++ b/src/shell-app-cache-private.h
|
|
|
e2a246 |
@@ -0,0 +1,19 @@
|
|
|
e2a246 |
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
e2a246 |
+#ifndef __SHELL_APP_CACHE_PRIVATE_H__
|
|
|
e2a246 |
+#define __SHELL_APP_CACHE_PRIVATE_H__
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#include <gio/gio.h>
|
|
|
e2a246 |
+#include <gio/gdesktopappinfo.h>
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#define SHELL_TYPE_APP_CACHE (shell_app_cache_get_type())
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+G_DECLARE_FINAL_TYPE (ShellAppCache, shell_app_cache, SHELL, APP_CACHE, GObject)
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ShellAppCache *shell_app_cache_get_default (void);
|
|
|
e2a246 |
+GList *shell_app_cache_get_all (ShellAppCache *cache);
|
|
|
e2a246 |
+GDesktopAppInfo *shell_app_cache_get_info (ShellAppCache *cache,
|
|
|
e2a246 |
+ const char *id);
|
|
|
e2a246 |
+char *shell_app_cache_translate_folder (ShellAppCache *cache,
|
|
|
e2a246 |
+ const char *name);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#endif /* __SHELL_APP_CACHE_PRIVATE_H__ */
|
|
|
e2a246 |
diff --git a/src/shell-app-cache.c b/src/shell-app-cache.c
|
|
|
e2a246 |
new file mode 100644
|
|
|
e2a246 |
index 000000000..15d4734d0
|
|
|
e2a246 |
--- /dev/null
|
|
|
e2a246 |
+++ b/src/shell-app-cache.c
|
|
|
e2a246 |
@@ -0,0 +1,404 @@
|
|
|
e2a246 |
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#include "config.h"
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#include "shell-app-cache-private.h"
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * SECTION:shell-app-cache
|
|
|
e2a246 |
+ * @title: ShellAppCache
|
|
|
e2a246 |
+ * @short_description: application information cache
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * The #ShellAppCache is responsible for caching information about #GAppInfo
|
|
|
e2a246 |
+ * to ensure that the compositor thread never needs to perform disk reads to
|
|
|
e2a246 |
+ * access them. All of the work is done off-thread. When the new data has
|
|
|
e2a246 |
+ * been loaded, a #ShellAppCache::changed signal is emitted.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Additionally, the #ShellAppCache caches information about translations for
|
|
|
e2a246 |
+ * directories. This allows translation provided in [Desktop Entry] GKeyFiles
|
|
|
e2a246 |
+ * to be available when building StLabel and other elements without performing
|
|
|
e2a246 |
+ * costly disk reads.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Various monitors are used to keep this information up to date while the
|
|
|
e2a246 |
+ * Shell is running.
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+#define DEFAULT_TIMEOUT_SECONDS 5
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+struct _ShellAppCache
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ GObject parent_instance;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ GAppInfoMonitor *monitor;
|
|
|
e2a246 |
+ GPtrArray *dir_monitors;
|
|
|
e2a246 |
+ GHashTable *folders;
|
|
|
e2a246 |
+ GCancellable *cancellable;
|
|
|
e2a246 |
+ GList *app_infos;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ guint queued_update;
|
|
|
e2a246 |
+};
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+typedef struct
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ GList *app_infos;
|
|
|
e2a246 |
+ GHashTable *folders;
|
|
|
e2a246 |
+} CacheState;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+G_DEFINE_TYPE (ShellAppCache, shell_app_cache, G_TYPE_OBJECT)
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+enum {
|
|
|
e2a246 |
+ CHANGED,
|
|
|
e2a246 |
+ N_SIGNALS
|
|
|
e2a246 |
+};
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static guint signals [N_SIGNALS];
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+cache_state_free (CacheState *state)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_clear_pointer (&state->folders, g_hash_table_unref);
|
|
|
e2a246 |
+ g_list_free_full (state->app_infos, g_object_unref);
|
|
|
e2a246 |
+ g_slice_free (CacheState, state);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static CacheState *
|
|
|
e2a246 |
+cache_state_new (void)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ CacheState *state;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ state = g_slice_new0 (CacheState);
|
|
|
e2a246 |
+ state->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return g_steal_pointer (&state);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * shell_app_cache_get_default:
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Gets the default #ShellAppCache.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Returns: (transfer none): a #ShellAppCache
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+ShellAppCache *
|
|
|
e2a246 |
+shell_app_cache_get_default (void)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ static ShellAppCache *instance;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (instance == NULL)
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ instance = g_object_new (SHELL_TYPE_APP_CACHE, NULL);
|
|
|
e2a246 |
+ g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return instance;
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+load_folder (GHashTable *folders,
|
|
|
e2a246 |
+ const char *path)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_autoptr(GDir) dir = NULL;
|
|
|
e2a246 |
+ const char *name;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_assert (folders != NULL);
|
|
|
e2a246 |
+ g_assert (path != NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ dir = g_dir_open (path, 0, NULL);
|
|
|
e2a246 |
+ if (dir == NULL)
|
|
|
e2a246 |
+ return;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ while ((name = g_dir_read_name (dir)))
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ g_autofree gchar *filename = NULL;
|
|
|
e2a246 |
+ g_autoptr(GKeyFile) keyfile = NULL;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /* First added wins */
|
|
|
e2a246 |
+ if (g_hash_table_contains (folders, name))
|
|
|
e2a246 |
+ continue;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ filename = g_build_filename (path, name, NULL);
|
|
|
e2a246 |
+ keyfile = g_key_file_new ();
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL))
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ gchar *translated;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ translated = g_key_file_get_locale_string (keyfile,
|
|
|
e2a246 |
+ "Desktop Entry", "Name",
|
|
|
e2a246 |
+ NULL, NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (translated != NULL)
|
|
|
e2a246 |
+ g_hash_table_insert (folders, g_strdup (name), translated);
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+load_folders (GHashTable *folders)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ const char * const *dirs;
|
|
|
e2a246 |
+ g_autofree gchar *userdir = NULL;
|
|
|
e2a246 |
+ guint i;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_assert (folders != NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ userdir = g_build_filename (g_get_user_data_dir (), "desktop-directories", NULL);
|
|
|
e2a246 |
+ load_folder (folders, userdir);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ dirs = g_get_system_data_dirs ();
|
|
|
e2a246 |
+ for (i = 0; dirs[i] != NULL; i++)
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ g_autofree gchar *sysdir = g_build_filename (dirs[i], "desktop-directories", NULL);
|
|
|
e2a246 |
+ load_folder (folders, sysdir);
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+shell_app_cache_worker (GTask *task,
|
|
|
e2a246 |
+ gpointer source_object,
|
|
|
e2a246 |
+ gpointer task_data,
|
|
|
e2a246 |
+ GCancellable *cancellable)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ CacheState *state;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_assert (G_IS_TASK (task));
|
|
|
e2a246 |
+ g_assert (SHELL_IS_APP_CACHE (source_object));
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ state = cache_state_new ();
|
|
|
e2a246 |
+ state->app_infos = g_app_info_get_all ();
|
|
|
e2a246 |
+ load_folders (state->folders);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_task_return_pointer (task, state, (GDestroyNotify) cache_state_free);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+apply_update_cb (GObject *object,
|
|
|
e2a246 |
+ GAsyncResult *result,
|
|
|
e2a246 |
+ gpointer user_data)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ ShellAppCache *cache = (ShellAppCache *)object;
|
|
|
e2a246 |
+ g_autoptr(GError) error = NULL;
|
|
|
e2a246 |
+ CacheState *state;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_assert (SHELL_IS_APP_CACHE (cache));
|
|
|
e2a246 |
+ g_assert (G_IS_TASK (result));
|
|
|
e2a246 |
+ g_assert (user_data == NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ state = g_task_propagate_pointer (G_TASK (result), &error);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
|
|
e2a246 |
+ return;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_list_free_full (cache->app_infos, g_object_unref);
|
|
|
e2a246 |
+ cache->app_infos = g_steal_pointer (&state->app_infos);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_clear_pointer (&cache->folders, g_hash_table_unref);
|
|
|
e2a246 |
+ cache->folders = g_steal_pointer (&state->folders);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_signal_emit (cache, signals[CHANGED], 0);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ cache_state_free (state);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static gboolean
|
|
|
e2a246 |
+shell_app_cache_do_update (gpointer user_data)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ ShellAppCache *cache = user_data;
|
|
|
e2a246 |
+ g_autoptr(GTask) task = NULL;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ cache->queued_update = 0;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /* Reset the cancellable state so we don't race with
|
|
|
e2a246 |
+ * two updates coming back overlapped and applying the
|
|
|
e2a246 |
+ * information in the wrong order.
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+ g_cancellable_cancel (cache->cancellable);
|
|
|
e2a246 |
+ g_clear_object (&cache->cancellable);
|
|
|
e2a246 |
+ cache->cancellable = g_cancellable_new ();
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ task = g_task_new (cache, cache->cancellable, apply_update_cb, NULL);
|
|
|
e2a246 |
+ g_task_set_source_tag (task, shell_app_cache_do_update);
|
|
|
e2a246 |
+ g_task_run_in_thread (task, shell_app_cache_worker);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return G_SOURCE_REMOVE;
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+shell_app_cache_queue_update (ShellAppCache *self)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_assert (SHELL_IS_APP_CACHE (self));
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (self->queued_update != 0)
|
|
|
e2a246 |
+ g_source_remove (self->queued_update);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ self->queued_update = g_timeout_add_seconds (DEFAULT_TIMEOUT_SECONDS,
|
|
|
e2a246 |
+ shell_app_cache_do_update,
|
|
|
e2a246 |
+ self);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+monitor_desktop_directories_for_data_dir (ShellAppCache *self,
|
|
|
e2a246 |
+ const gchar *directory)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_autofree gchar *subdir = NULL;
|
|
|
e2a246 |
+ g_autoptr(GFile) file = NULL;
|
|
|
e2a246 |
+ g_autoptr(GFileMonitor) monitor = NULL;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_assert (SHELL_IS_APP_CACHE (self));
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (directory == NULL)
|
|
|
e2a246 |
+ return;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ subdir = g_build_filename (directory, "desktop-directories", NULL);
|
|
|
e2a246 |
+ file = g_file_new_for_path (subdir);
|
|
|
e2a246 |
+ monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (monitor != NULL)
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ g_file_monitor_set_rate_limit (monitor, DEFAULT_TIMEOUT_SECONDS * 1000);
|
|
|
e2a246 |
+ g_signal_connect_object (monitor,
|
|
|
e2a246 |
+ "changed",
|
|
|
e2a246 |
+ G_CALLBACK (shell_app_cache_queue_update),
|
|
|
e2a246 |
+ self,
|
|
|
e2a246 |
+ G_CONNECT_SWAPPED);
|
|
|
e2a246 |
+ g_ptr_array_add (self->dir_monitors, g_steal_pointer (&monitor));
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+shell_app_cache_finalize (GObject *object)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ ShellAppCache *self = (ShellAppCache *)object;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_clear_object (&self->monitor);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (self->queued_update)
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ g_source_remove (self->queued_update);
|
|
|
e2a246 |
+ self->queued_update = 0;
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_clear_pointer (&self->dir_monitors, g_ptr_array_unref);
|
|
|
e2a246 |
+ g_clear_pointer (&self->folders, g_hash_table_unref);
|
|
|
e2a246 |
+ g_list_free_full (self->app_infos, g_object_unref);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ G_OBJECT_CLASS (shell_app_cache_parent_class)->finalize (object);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+shell_app_cache_class_init (ShellAppCacheClass *klass)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ object_class->finalize = shell_app_cache_finalize;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /**
|
|
|
e2a246 |
+ * ShellAppCache::changed:
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * The "changed" signal is emitted when the cache has updated
|
|
|
e2a246 |
+ * information about installed applications.
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+ signals [CHANGED] =
|
|
|
e2a246 |
+ g_signal_new ("changed",
|
|
|
e2a246 |
+ G_TYPE_FROM_CLASS (klass),
|
|
|
e2a246 |
+ G_SIGNAL_RUN_LAST,
|
|
|
e2a246 |
+ 0, NULL, NULL, NULL,
|
|
|
e2a246 |
+ G_TYPE_NONE, 0);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+static void
|
|
|
e2a246 |
+shell_app_cache_init (ShellAppCache *self)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ const gchar * const *sysdirs;
|
|
|
e2a246 |
+ guint i;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /* Monitor directories for translation changes */
|
|
|
e2a246 |
+ self->dir_monitors = g_ptr_array_new_with_free_func (g_object_unref);
|
|
|
e2a246 |
+ monitor_desktop_directories_for_data_dir (self, g_get_user_data_dir ());
|
|
|
e2a246 |
+ sysdirs = g_get_system_data_dirs ();
|
|
|
e2a246 |
+ for (i = 0; sysdirs[i] != NULL; i++)
|
|
|
e2a246 |
+ monitor_desktop_directories_for_data_dir (self, sysdirs[i]);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /* Load translated directory names immediately */
|
|
|
e2a246 |
+ self->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
e2a246 |
+ load_folders (self->folders);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ /* Setup AppMonitor to track changes */
|
|
|
e2a246 |
+ self->monitor = g_app_info_monitor_get ();
|
|
|
e2a246 |
+ g_signal_connect_object (self->monitor,
|
|
|
e2a246 |
+ "changed",
|
|
|
e2a246 |
+ G_CALLBACK (shell_app_cache_queue_update),
|
|
|
e2a246 |
+ self,
|
|
|
e2a246 |
+ G_CONNECT_SWAPPED);
|
|
|
e2a246 |
+ self->app_infos = g_app_info_get_all ();
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * shell_app_cache_get_all:
|
|
|
e2a246 |
+ * @cache: (nullable): a #ShellAppCache or %NULL
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Like g_app_info_get_all() but always returns a
|
|
|
e2a246 |
+ * cached set of application info so the caller can be
|
|
|
e2a246 |
+ * sure that I/O will not happen on the current thread.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Returns: (transfer none) (element-type GAppInfo):
|
|
|
e2a246 |
+ * a #GList of references to #GAppInfo.
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+GList *
|
|
|
e2a246 |
+shell_app_cache_get_all (ShellAppCache *cache)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return cache->app_infos;
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * shell_app_cache_get_info:
|
|
|
e2a246 |
+ * @cache: (nullable): a #ShellAppCache or %NULL
|
|
|
e2a246 |
+ * @id: the application id
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * A replacement for g_desktop_app_info_new() that will lookup the
|
|
|
e2a246 |
+ * information from the cache instead of (re)loading from disk.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Returns: (nullable) (transfer none): a #GDesktopAppInfo or %NULL
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+GDesktopAppInfo *
|
|
|
e2a246 |
+shell_app_cache_get_info (ShellAppCache *cache,
|
|
|
e2a246 |
+ const char *id)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ const GList *iter;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ for (iter = cache->app_infos; iter != NULL; iter = iter->next)
|
|
|
e2a246 |
+ {
|
|
|
e2a246 |
+ GAppInfo *info = iter->data;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (g_strcmp0 (id, g_app_info_get_id (info)) == 0)
|
|
|
e2a246 |
+ return G_DESKTOP_APP_INFO (info);
|
|
|
e2a246 |
+ }
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return NULL;
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * shell_app_cache_translate_folder:
|
|
|
e2a246 |
+ * @cache: (nullable): a #ShellAppCache or %NULL
|
|
|
e2a246 |
+ * @name: the folder name
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Gets the translated folder name for @name if any exists.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Returns: (nullable): the translated string or %NULL if there is no
|
|
|
e2a246 |
+ * translation.
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+char *
|
|
|
e2a246 |
+shell_app_cache_translate_folder (ShellAppCache *cache,
|
|
|
e2a246 |
+ const char *name)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ if (name == NULL)
|
|
|
e2a246 |
+ return NULL;
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+ return g_strdup (g_hash_table_lookup (cache->folders, name));
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
|
|
|
e2a246 |
index 127f29ef0..828fa726a 100644
|
|
|
e2a246 |
--- a/src/shell-app-system.c
|
|
|
e2a246 |
+++ b/src/shell-app-system.c
|
|
|
e2a246 |
@@ -9,6 +9,7 @@
|
|
|
e2a246 |
#include <gio/gio.h>
|
|
|
e2a246 |
#include <glib/gi18n.h>
|
|
|
e2a246 |
|
|
|
e2a246 |
+#include "shell-app-cache-private.h"
|
|
|
e2a246 |
#include "shell-app-private.h"
|
|
|
e2a246 |
#include "shell-window-tracker-private.h"
|
|
|
e2a246 |
#include "shell-app-system-private.h"
|
|
|
e2a246 |
@@ -94,14 +95,14 @@ static void
|
|
|
e2a246 |
scan_startup_wm_class_to_id (ShellAppSystem *self)
|
|
|
e2a246 |
{
|
|
|
e2a246 |
ShellAppSystemPrivate *priv = self->priv;
|
|
|
e2a246 |
- GList *l;
|
|
|
e2a246 |
+ const GList *l;
|
|
|
e2a246 |
+ GList *all;
|
|
|
e2a246 |
|
|
|
e2a246 |
g_hash_table_remove_all (priv->startup_wm_class_to_id);
|
|
|
e2a246 |
|
|
|
e2a246 |
- g_list_free_full (priv->installed_apps, g_object_unref);
|
|
|
e2a246 |
- priv->installed_apps = g_app_info_get_all ();
|
|
|
e2a246 |
+ all = shell_app_cache_get_all (shell_app_cache_get_default ());
|
|
|
e2a246 |
|
|
|
e2a246 |
- for (l = priv->installed_apps; l != NULL; l = l->next)
|
|
|
e2a246 |
+ for (l = all; l != NULL; l = l->next)
|
|
|
e2a246 |
{
|
|
|
e2a246 |
GAppInfo *info = l->data;
|
|
|
e2a246 |
const char *startup_wm_class, *id, *old_id;
|
|
|
e2a246 |
@@ -131,7 +132,8 @@ app_is_stale (ShellApp *app)
|
|
|
e2a246 |
if (shell_app_is_window_backed (app))
|
|
|
e2a246 |
return FALSE;
|
|
|
e2a246 |
|
|
|
e2a246 |
- info = g_desktop_app_info_new (shell_app_get_id (app));
|
|
|
e2a246 |
+ info = shell_app_cache_get_info (shell_app_cache_get_default (),
|
|
|
e2a246 |
+ shell_app_get_id (app));
|
|
|
e2a246 |
if (!info)
|
|
|
e2a246 |
return TRUE;
|
|
|
e2a246 |
|
|
|
e2a246 |
@@ -156,7 +158,6 @@ app_is_stale (ShellApp *app)
|
|
|
e2a246 |
g_icon_equal (g_app_info_get_icon (old_info),
|
|
|
e2a246 |
g_app_info_get_icon (new_info));
|
|
|
e2a246 |
|
|
|
e2a246 |
- g_object_unref (info);
|
|
|
e2a246 |
return !is_unchanged;
|
|
|
e2a246 |
}
|
|
|
e2a246 |
|
|
|
e2a246 |
@@ -210,11 +211,9 @@ rescan_icon_theme (ShellAppSystem *self)
|
|
|
e2a246 |
}
|
|
|
e2a246 |
|
|
|
e2a246 |
static void
|
|
|
e2a246 |
-installed_changed (GAppInfoMonitor *monitor,
|
|
|
e2a246 |
- gpointer user_data)
|
|
|
e2a246 |
+installed_changed (ShellAppCache *cache,
|
|
|
e2a246 |
+ ShellAppSystem *self)
|
|
|
e2a246 |
{
|
|
|
e2a246 |
- ShellAppSystem *self = user_data;
|
|
|
e2a246 |
-
|
|
|
e2a246 |
rescan_icon_theme (self);
|
|
|
e2a246 |
scan_startup_wm_class_to_id (self);
|
|
|
e2a246 |
|
|
|
e2a246 |
@@ -227,7 +226,7 @@ static void
|
|
|
e2a246 |
shell_app_system_init (ShellAppSystem *self)
|
|
|
e2a246 |
{
|
|
|
e2a246 |
ShellAppSystemPrivate *priv;
|
|
|
e2a246 |
- GAppInfoMonitor *monitor;
|
|
|
e2a246 |
+ ShellAppCache *cache;
|
|
|
e2a246 |
|
|
|
e2a246 |
self->priv = priv = shell_app_system_get_instance_private (self);
|
|
|
e2a246 |
|
|
|
e2a246 |
@@ -238,9 +237,9 @@ shell_app_system_init (ShellAppSystem *self)
|
|
|
e2a246 |
|
|
|
e2a246 |
priv->startup_wm_class_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
|
|
e2a246 |
|
|
|
e2a246 |
- monitor = g_app_info_monitor_get ();
|
|
|
e2a246 |
- g_signal_connect (monitor, "changed", G_CALLBACK (installed_changed), self);
|
|
|
e2a246 |
- installed_changed (monitor, self);
|
|
|
e2a246 |
+ cache = shell_app_cache_get_default ();
|
|
|
e2a246 |
+ g_signal_connect (cache, "changed", G_CALLBACK (installed_changed), self);
|
|
|
e2a246 |
+ installed_changed (cache, self);
|
|
|
e2a246 |
}
|
|
|
e2a246 |
|
|
|
e2a246 |
static void
|
|
|
e2a246 |
@@ -293,13 +292,12 @@ shell_app_system_lookup_app (ShellAppSystem *self,
|
|
|
e2a246 |
if (app)
|
|
|
e2a246 |
return app;
|
|
|
e2a246 |
|
|
|
e2a246 |
- info = g_desktop_app_info_new (id);
|
|
|
e2a246 |
+ info = shell_app_cache_get_info (shell_app_cache_get_default (), id);
|
|
|
e2a246 |
if (!info)
|
|
|
e2a246 |
return NULL;
|
|
|
e2a246 |
|
|
|
e2a246 |
app = _shell_app_new (info);
|
|
|
e2a246 |
g_hash_table_insert (priv->id_to_app, (char *) shell_app_get_id (app), app);
|
|
|
e2a246 |
- g_object_unref (info);
|
|
|
e2a246 |
return app;
|
|
|
e2a246 |
}
|
|
|
e2a246 |
|
|
|
e2a246 |
@@ -506,7 +504,5 @@ shell_app_system_search (const char *search_string)
|
|
|
e2a246 |
GList *
|
|
|
e2a246 |
shell_app_system_get_installed (ShellAppSystem *self)
|
|
|
e2a246 |
{
|
|
|
e2a246 |
- ShellAppSystemPrivate *priv = self->priv;
|
|
|
e2a246 |
-
|
|
|
e2a246 |
- return priv->installed_apps;
|
|
|
e2a246 |
+ return shell_app_cache_get_all (shell_app_cache_get_default ());
|
|
|
e2a246 |
}
|
|
|
e2a246 |
diff --git a/src/shell-util.c b/src/shell-util.c
|
|
|
1be690 |
index fa3fc08c8..370784523 100644
|
|
|
e2a246 |
--- a/src/shell-util.c
|
|
|
e2a246 |
+++ b/src/shell-util.c
|
|
|
e2a246 |
@@ -16,6 +16,7 @@
|
|
|
e2a246 |
#include <GL/gl.h>
|
|
|
e2a246 |
#include <cogl/cogl.h>
|
|
|
e2a246 |
|
|
|
e2a246 |
+#include "shell-app-cache-private.h"
|
|
|
e2a246 |
#include "shell-util.h"
|
|
|
e2a246 |
#include <glib/gi18n-lib.h>
|
|
|
e2a246 |
#include <gtk/gtk.h>
|
|
|
1be690 |
@@ -639,3 +640,18 @@ shell_util_has_x11_display_extension (MetaDisplay *display,
|
|
|
1be690 |
xdisplay = meta_x11_display_get_xdisplay (x11_display);
|
|
|
1be690 |
return XQueryExtension (xdisplay, extension, &op, &event, &error);
|
|
|
e2a246 |
}
|
|
|
e2a246 |
+
|
|
|
e2a246 |
+/**
|
|
|
e2a246 |
+ * shell_util_get_translated_folder_name:
|
|
|
e2a246 |
+ * @name: the untranslated folder name
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Attempts to translate the folder @name using translations provided
|
|
|
e2a246 |
+ * by .directory files.
|
|
|
e2a246 |
+ *
|
|
|
e2a246 |
+ * Returns: (nullable): a translated string or %NULL
|
|
|
e2a246 |
+ */
|
|
|
e2a246 |
+char *
|
|
|
e2a246 |
+shell_util_get_translated_folder_name (const char *name)
|
|
|
e2a246 |
+{
|
|
|
e2a246 |
+ return shell_app_cache_translate_folder (shell_app_cache_get_default (), name);
|
|
|
e2a246 |
+}
|
|
|
e2a246 |
diff --git a/src/shell-util.h b/src/shell-util.h
|
|
|
1be690 |
index 02b8404e9..843a1253d 100644
|
|
|
e2a246 |
--- a/src/shell-util.h
|
|
|
e2a246 |
+++ b/src/shell-util.h
|
|
|
1be690 |
@@ -62,6 +62,8 @@ void shell_util_check_cloexec_fds (void);
|
|
|
1be690 |
gboolean shell_util_has_x11_display_extension (MetaDisplay *display,
|
|
|
1be690 |
const char *extension);
|
|
|
e2a246 |
|
|
|
e2a246 |
+char *shell_util_get_translated_folder_name (const char *name);
|
|
|
e2a246 |
+
|
|
|
e2a246 |
G_END_DECLS
|
|
|
e2a246 |
|
|
|
e2a246 |
#endif /* __SHELL_UTIL_H__ */
|
|
|
e2a246 |
--
|
|
|
1be690 |
2.26.2
|
|
|
e2a246 |
|