Blame SOURCES/0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch

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