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

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