diff --git a/SOURCES/0001-Do-not-change-Wacom-LEDs-through-g-s-d.patch b/SOURCES/0001-Do-not-change-Wacom-LEDs-through-g-s-d.patch new file mode 100644 index 0000000..9eb76be --- /dev/null +++ b/SOURCES/0001-Do-not-change-Wacom-LEDs-through-g-s-d.patch @@ -0,0 +1,25 @@ +From f5ddd0fc02e99597e4b8506ac35523a6fa8ac22f Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 4 Mar 2020 16:08:31 +0100 +Subject: [PATCH] Do not change Wacom LEDs through g-s-d + +Let the wacom kernel driver sort it out by itself. +--- + js/ui/windowManager.js | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js +index dfe1b44..b2e938c 100644 +--- a/js/ui/windowManager.js ++++ b/js/ui/windowManager.js +@@ -1037,7 +1037,6 @@ var WindowManager = class { + + if (this._gsdWacomProxy) { + this._gsdWacomProxy.SetOLEDLabelsRemote(pad.get_device_node(), labels); +- this._gsdWacomProxy.SetGroupModeLEDRemote(pad.get_device_node(), group, mode); + } + }); + +-- +2.24.1 + diff --git a/SOURCES/0001-environment-reduce-calls-to-g_time_zone_new_local.patch b/SOURCES/0001-environment-reduce-calls-to-g_time_zone_new_local.patch new file mode 100644 index 0000000..3f43c64 --- /dev/null +++ b/SOURCES/0001-environment-reduce-calls-to-g_time_zone_new_local.patch @@ -0,0 +1,65 @@ +From f03c6a870820543901331d1920b7b3e423813d2c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 27 Feb 2020 13:46:44 -0800 +Subject: [PATCH 1/6] environment: reduce calls to g_time_zone_new_local() + +Creating a new GTimeZone for the local timezone can be quite expensive if +done repeatedly. It requires an open(), mmap(), and parsing of +/etc/localtime. + +This patch was provided by Florian, and I've tested it as far back as +3.28.4 to ensure that we are really reducing the number of open() calls +on the compositor thread. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1051 + +Signed-off-by: Christian Hergert +--- + js/ui/environment.js | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/js/ui/environment.js b/js/ui/environment.js +index e22ec7402..f3f2d17c7 100644 +--- a/js/ui/environment.js ++++ b/js/ui/environment.js +@@ -11,6 +11,9 @@ imports.gi.versions.TelepathyLogger = '0.2'; + + const { Clutter, GLib, Shell, St } = imports.gi; + const Gettext = imports.gettext; ++const System = imports.system; ++ ++let _localTimeZone = null; + + // We can't import shell JS modules yet, because they may have + // variable initializations, etc, that depend on init() already having +@@ -110,9 +113,26 @@ function init() { + } + }; + ++ // Override to clear our own timezone cache as well ++ const origClearDateCaches = System.clearDateCaches; ++ System.clearDateCaches = function () { ++ _localTimeZone = null; ++ origClearDateCaches(); ++ }; ++ + // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783 + Date.prototype.toLocaleFormat = function(format) { +- return Shell.util_format_date(format, this.getTime()); ++ if (_localTimeZone === null) ++ _localTimeZone = GLib.TimeZone.new_local(); ++ ++ let dt = GLib.DateTime.new(_localTimeZone, ++ this.getYear(), ++ this.getMonth() + 1, ++ this.getDate(), ++ this.getHours(), ++ this.getMinutes(), ++ this.getSeconds()); ++ return dt ? dt.format(format) : ''; + }; + + let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR'); +-- +2.26.2 + diff --git a/SOURCES/0001-st-texture-cache-Cancel-pending-requests-on-icon-the.patch b/SOURCES/0001-st-texture-cache-Cancel-pending-requests-on-icon-the.patch new file mode 100644 index 0000000..a6f6fdc --- /dev/null +++ b/SOURCES/0001-st-texture-cache-Cancel-pending-requests-on-icon-the.patch @@ -0,0 +1,94 @@ +From 1bf28eea64056846547ec33d783c7f2e0dad78a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 22 May 2020 22:53:39 +0200 +Subject: [PATCH] st/texture-cache: Cancel pending requests on icon-theme + changes + +As outlined in commit 36b8dcbe07, we can end up with wrong icons +if the icon theme changes right after a GTK theme change to/from +HighContrast triggered a theme reload. + +That's because when we reload icons for the new icon theme, there +are already pending requests due to the icon-style change; those +requests are simply re-used for the new icons, with the existing +icon infos from the old theme. + +The above commit applied a simple work-around by changing the +icon theme before the GTK theme, but that only works for the +HighContrast switch in our own UI. + +It turns out that Settings also uses the "wrong" order, so the +issue still reproduces with the Universal Access panel. + +So instead of relying on everything changing the settings in the +order we expect, cancel all ongoing requests on icon-theme changes. + +https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1277 +--- + src/st/st-texture-cache.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c +index 35e9d036f..6dc351282 100644 +--- a/src/st/st-texture-cache.c ++++ b/src/st/st-texture-cache.c +@@ -48,6 +48,8 @@ struct _StTextureCachePrivate + + /* File monitors to evict cache data on changes */ + GHashTable *file_monitors; /* char * -> GFileMonitor * */ ++ ++ GCancellable *cancellable; + }; + + static void st_texture_cache_dispose (GObject *object); +@@ -152,6 +154,9 @@ on_icon_theme_changed (StSettings *settings, + { + g_autofree gchar *theme; + ++ g_cancellable_cancel (cache->priv->cancellable); ++ g_cancellable_reset (cache->priv->cancellable); ++ + st_texture_cache_evict_icons (cache); + + g_object_get (settings, "gtk-icon-theme", &theme, NULL); +@@ -186,6 +191,8 @@ st_texture_cache_init (StTextureCache *self) + self->priv->file_monitors = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, + g_object_unref, g_object_unref); + ++ self->priv->cancellable = g_cancellable_new (); ++ + on_icon_theme_changed (settings, NULL, self); + } + +@@ -194,8 +201,11 @@ st_texture_cache_dispose (GObject *object) + { + StTextureCache *self = (StTextureCache*)object; + ++ g_cancellable_cancel (self->priv->cancellable); ++ + g_clear_object (&self->priv->settings); + g_clear_object (&self->priv->icon_theme); ++ g_clear_object (&self->priv->cancellable); + + g_clear_pointer (&self->priv->keyed_cache, g_hash_table_destroy); + g_clear_pointer (&self->priv->keyed_surface_cache, g_hash_table_destroy); +@@ -675,11 +685,14 @@ load_texture_async (StTextureCache *cache, + gtk_icon_info_load_symbolic_async (data->icon_info, + &foreground_color, &success_color, + &warning_color, &error_color, +- NULL, on_symbolic_icon_loaded, data); ++ cache->priv->cancellable, ++ on_symbolic_icon_loaded, data); + } + else + { +- gtk_icon_info_load_icon_async (data->icon_info, NULL, on_icon_loaded, data); ++ gtk_icon_info_load_icon_async (data->icon_info, ++ cache->priv->cancellable, ++ on_icon_loaded, data); + } + } + else +-- +2.26.2 + diff --git a/SOURCES/0002-environment-Fix-date-conversion.patch b/SOURCES/0002-environment-Fix-date-conversion.patch new file mode 100644 index 0000000..2f6b5bd --- /dev/null +++ b/SOURCES/0002-environment-Fix-date-conversion.patch @@ -0,0 +1,33 @@ +From 189add05c07fe9d9bed6c1399b30e51a4a934bd3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 2 Mar 2020 13:46:04 +0100 +Subject: [PATCH 2/6] environment: Fix date conversion + +This is a regression from commit 06b690ff21204: + +GLib.DateTime.new() expects the full four-digit year, so passing +the abbreviated year from Date() will result in a bogus datetime. + +Today is *not* Saturday March 2nd, 120 ... + +https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1061 +--- + js/ui/environment.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/js/ui/environment.js b/js/ui/environment.js +index f3f2d17c7..a9cc16dee 100644 +--- a/js/ui/environment.js ++++ b/js/ui/environment.js +@@ -126,7 +126,7 @@ function init() { + _localTimeZone = GLib.TimeZone.new_local(); + + let dt = GLib.DateTime.new(_localTimeZone, +- this.getYear(), ++ this.getFullYear(), + this.getMonth() + 1, + this.getDate(), + this.getHours(), +-- +2.26.2 + diff --git a/SOURCES/0003-shell-app-system-Monitor-for-icon-theme-changes.patch b/SOURCES/0003-shell-app-system-Monitor-for-icon-theme-changes.patch new file mode 100644 index 0000000..89a3a80 --- /dev/null +++ b/SOURCES/0003-shell-app-system-Monitor-for-icon-theme-changes.patch @@ -0,0 +1,152 @@ +From c9277326055c96185a80b68d4228eee360bb0e7c Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Thu, 1 Aug 2019 20:58:20 -0300 +Subject: [PATCH 3/6] shell/app-system: Monitor for icon theme changes + +Whenever an app is installed, the usual routine is +to run 'gtk-update-icon-cache' after installing all +of the app's files. + +The side effect of that is that the .desktop file of +the application is installed before the icon theme +is updated. By the time GAppInfoMonitor emits the +'changed' signal, the icon theme is not yet updated, +leading to StIcon use the fallback icon. + +Under some circumstances (e.g. on very slow spinning +disks) the app icon is never actually loaded, and we +see the fallback icon forever. + +Monitor the icon theme for changes when an app is +installed. Try as many as 6 times before giving up +on detecting an icon theme update. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/661 +--- + src/shell-app-system.c | 54 +++++++++++++++++++++++++++++++++++++++ + src/st/st-texture-cache.c | 8 ++++++ + src/st/st-texture-cache.h | 2 ++ + 3 files changed, 64 insertions(+) + +diff --git a/src/shell-app-system.c b/src/shell-app-system.c +index f632cbe54..127f29ef0 100644 +--- a/src/shell-app-system.c ++++ b/src/shell-app-system.c +@@ -14,6 +14,14 @@ + #include "shell-app-system-private.h" + #include "shell-global.h" + #include "shell-util.h" ++#include "st.h" ++ ++/* Rescan for at most RESCAN_TIMEOUT_MS * MAX_RESCAN_RETRIES. That ++ * should be plenty of time for even a slow spinning drive to update ++ * the icon cache. ++ */ ++#define RESCAN_TIMEOUT_MS 2500 ++#define MAX_RESCAN_RETRIES 6 + + /* Vendor prefixes are something that can be preprended to a .desktop + * file name. Undo this. +@@ -51,6 +59,9 @@ struct _ShellAppSystemPrivate { + GHashTable *id_to_app; + GHashTable *startup_wm_class_to_id; + GList *installed_apps; ++ ++ guint rescan_icons_timeout_id; ++ guint n_rescan_retries; + }; + + static void shell_app_system_finalize (GObject *object); +@@ -157,12 +168,54 @@ stale_app_remove_func (gpointer key, + return app_is_stale (value); + } + ++static gboolean ++rescan_icon_theme_cb (gpointer user_data) ++{ ++ ShellAppSystemPrivate *priv; ++ ShellAppSystem *self; ++ StTextureCache *texture_cache; ++ gboolean rescanned; ++ ++ self = (ShellAppSystem *) user_data; ++ priv = self->priv; ++ ++ texture_cache = st_texture_cache_get_default (); ++ rescanned = st_texture_cache_rescan_icon_theme (texture_cache); ++ ++ priv->n_rescan_retries++; ++ ++ if (rescanned || priv->n_rescan_retries >= MAX_RESCAN_RETRIES) ++ { ++ priv->n_rescan_retries = 0; ++ priv->rescan_icons_timeout_id = 0; ++ return G_SOURCE_REMOVE; ++ } ++ ++ return G_SOURCE_CONTINUE; ++} ++ ++static void ++rescan_icon_theme (ShellAppSystem *self) ++{ ++ ShellAppSystemPrivate *priv = self->priv; ++ ++ priv->n_rescan_retries = 0; ++ ++ if (priv->rescan_icons_timeout_id > 0) ++ return; ++ ++ priv->rescan_icons_timeout_id = g_timeout_add (RESCAN_TIMEOUT_MS, ++ rescan_icon_theme_cb, ++ self); ++} ++ + static void + installed_changed (GAppInfoMonitor *monitor, + gpointer user_data) + { + ShellAppSystem *self = user_data; + ++ rescan_icon_theme (self); + scan_startup_wm_class_to_id (self); + + g_hash_table_foreach_remove (self->priv->id_to_app, stale_app_remove_func, NULL); +@@ -200,6 +253,7 @@ shell_app_system_finalize (GObject *object) + g_hash_table_destroy (priv->id_to_app); + g_hash_table_destroy (priv->startup_wm_class_to_id); + g_list_free_full (priv->installed_apps, g_object_unref); ++ g_clear_handle_id (&priv->rescan_icons_timeout_id, g_source_remove); + + G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object); + } +diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c +index c1331747f..4d0d617c4 100644 +--- a/src/st/st-texture-cache.c ++++ b/src/st/st-texture-cache.c +@@ -1554,3 +1554,11 @@ st_texture_cache_get_default (void) + instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL); + return instance; + } ++ ++gboolean ++st_texture_cache_rescan_icon_theme (StTextureCache *cache) ++{ ++ StTextureCachePrivate *priv = cache->priv; ++ ++ return gtk_icon_theme_rescan_if_needed (priv->icon_theme); ++} +diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h +index 11d1c4e64..a99316da8 100644 +--- a/src/st/st-texture-cache.h ++++ b/src/st/st-texture-cache.h +@@ -113,4 +113,6 @@ CoglTexture * st_texture_cache_load (StTextureCache *cache, + void *data, + GError **error); + ++gboolean st_texture_cache_rescan_icon_theme (StTextureCache *cache); ++ + #endif /* __ST_TEXTURE_CACHE_H__ */ +-- +2.26.2 + diff --git a/SOURCES/0004-global-force-fsync-to-worker-thread-when-saving-stat.patch b/SOURCES/0004-global-force-fsync-to-worker-thread-when-saving-stat.patch new file mode 100644 index 0000000..2271311 --- /dev/null +++ b/SOURCES/0004-global-force-fsync-to-worker-thread-when-saving-stat.patch @@ -0,0 +1,124 @@ +From 2a4f33df723d4b9ce68e5948b568a89675d37411 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Wed, 26 Feb 2020 14:46:20 -0800 +Subject: [PATCH 4/6] global: force fsync() to worker thread when saving state + +The g_file_replace_contents_async() API can potentially call fsync() from +the thread calling into it upon completion. This can have disasterous +effects when run from the compositor main thread such as complete stalls. + +This is a followup to 86a00b6872375a266449beee1ea6d5e94f1ebbcb which +assumed (like the rest of us) that the fsync() would be performed on the +thread that was doing the I/O operations. + +You can verify this with an strace -e fsync and cause terminal to display +a command completed notification (eg: from a backdrop window). + +This also fixes a lifecycle bug for the variant, as +g_file_replace_contents_async() does not copy the data during the operation +as that is the responsibility of the caller. Instead, we just use a GBytes +variant and reference the variant there. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1050 +--- + src/shell-global.c | 70 +++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 63 insertions(+), 7 deletions(-) + +diff --git a/src/shell-global.c b/src/shell-global.c +index df84b6b0d..4b33778e0 100644 +--- a/src/shell-global.c ++++ b/src/shell-global.c +@@ -1572,6 +1572,55 @@ delete_variant_cb (GObject *object, + g_hash_table_remove (global->save_ops, object); + } + ++static void ++replace_contents_worker (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GFile *file = source_object; ++ GBytes *bytes = task_data; ++ GError *error = NULL; ++ const gchar *data; ++ gsize len; ++ ++ data = g_bytes_get_data (bytes, &len); ++ ++ if (!g_file_replace_contents (file, data, len, NULL, FALSE, ++ G_FILE_CREATE_REPLACE_DESTINATION, ++ NULL, cancellable, &error)) ++ g_task_return_error (task, g_steal_pointer (&error)); ++ else ++ g_task_return_boolean (task, TRUE); ++} ++ ++static void ++replace_contents_async (GFile *path, ++ GBytes *bytes, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autoptr(GTask) task = NULL; ++ ++ g_assert (G_IS_FILE (path)); ++ g_assert (bytes != NULL); ++ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); ++ ++ task = g_task_new (path, cancellable, callback, user_data); ++ g_task_set_source_tag (task, replace_contents_async); ++ g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref); ++ g_task_run_in_thread (task, replace_contents_worker); ++} ++ ++static gboolean ++replace_contents_finish (GFile *file, ++ GAsyncResult *result, ++ GError **error) ++{ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ + static void + replace_variant_cb (GObject *object, + GAsyncResult *result, +@@ -1580,7 +1629,7 @@ replace_variant_cb (GObject *object, + ShellGlobal *global = user_data; + GError *error = NULL; + +- if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error)) ++ if (!replace_contents_finish (G_FILE (object), result, &error)) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { +@@ -1616,12 +1665,19 @@ save_variant (ShellGlobal *global, + } + else + { +- g_file_replace_contents_async (path, +- g_variant_get_data (variant), +- g_variant_get_size (variant), +- NULL, FALSE, +- G_FILE_CREATE_REPLACE_DESTINATION, +- cancellable, replace_variant_cb, global); ++ g_autoptr(GBytes) bytes = NULL; ++ ++ bytes = g_bytes_new_with_free_func (g_variant_get_data (variant), ++ g_variant_get_size (variant), ++ (GDestroyNotify)g_variant_unref, ++ g_variant_ref (variant)); ++ /* g_file_replace_contents_async() can potentially fsync() from the ++ * calling thread when completing the asynchronous task. Instead, we ++ * want to force that fsync() to a thread to avoid blocking the ++ * compository main loop. Using our own replace_contents_async() ++ * simply executes the operation synchronously from a thread. ++ */ ++ replace_contents_async (path, bytes, cancellable, replace_variant_cb, global); + } + + g_object_unref (path); +-- +2.26.2 + diff --git a/SOURCES/0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch b/SOURCES/0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch new file mode 100644 index 0000000..1f19cb0 --- /dev/null +++ b/SOURCES/0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch @@ -0,0 +1,674 @@ +From a3fc35a2b452855d004549afbec57d1b4f36c917 Mon Sep 17 00:00:00 2001 +From: Christian Hergert +Date: Thu, 27 Feb 2020 19:36:14 -0800 +Subject: [PATCH 5/6] app-cache: add ShellAppCache for GAppInfo caching + +This caches GAppInfo so that the compositor thread does not have to perform +costly disk access to load them. Instead, they are loaded from a worker +thread and the ShellAppCache notifies of changes. + +To simplify maintenance, ShellAppCache manages this directly and the +existing ShellAppSystem wraps the cache. We may want to graft these +together in the future, but now it provides the easiest way to backport +changes to older Shell releases. + +Another source of compositor thread disk access was in determining the +name for an application directory. Translations are provided via GKeyFile +installed in "desktop-directories". Each time we would build the name +for a label (or update it) we would have to load all of these files. + +Instead, the ShellAppCache caches that information and updates the cache +in bulk when those change. We can reduce this in the future to do less +work, but chances are these will come together anyway so that is probably +worth fixing if we ever come across it. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/2282 +--- + js/ui/appDisplay.js | 12 +- + src/meson.build | 5 +- + src/shell-app-cache-private.h | 19 ++ + src/shell-app-cache.c | 404 ++++++++++++++++++++++++++++++++++ + src/shell-app-system.c | 34 ++- + src/shell-util.c | 16 ++ + src/shell-util.h | 2 + + 7 files changed, 463 insertions(+), 29 deletions(-) + create mode 100644 src/shell-app-cache-private.h + create mode 100644 src/shell-app-cache.c + +diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js +index 7fad02cd0..a2d691085 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -78,15 +78,9 @@ function _getFolderName(folder) { + let name = folder.get_string('name'); + + if (folder.get_boolean('translate')) { +- let keyfile = new GLib.KeyFile(); +- let path = 'desktop-directories/' + name; +- +- try { +- keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE); +- name = keyfile.get_locale_string('Desktop Entry', 'Name', null); +- } catch(e) { +- return name; +- } ++ let translated = Shell.util_get_translated_folder_name(name); ++ if (translated !== null) ++ return translated; + } + + return name; +diff --git a/src/meson.build b/src/meson.build +index 97a5a796c..2b911d347 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -108,6 +108,7 @@ endif + + libshell_private_headers = [ + 'shell-app-private.h', ++ 'shell-app-cache-private.h', + 'shell-app-system-private.h', + 'shell-global-private.h', + 'shell-window-tracker-private.h', +@@ -146,7 +147,9 @@ if have_networkmanager + libshell_sources += 'shell-network-agent.c' + endif + +-libshell_private_sources = [] ++libshell_private_sources = [ ++ 'shell-app-cache.c', ++] + + if enable_recorder + libshell_sources += ['shell-recorder.c'] +diff --git a/src/shell-app-cache-private.h b/src/shell-app-cache-private.h +new file mode 100644 +index 000000000..b73094ab1 +--- /dev/null ++++ b/src/shell-app-cache-private.h +@@ -0,0 +1,19 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++#ifndef __SHELL_APP_CACHE_PRIVATE_H__ ++#define __SHELL_APP_CACHE_PRIVATE_H__ ++ ++#include ++#include ++ ++#define SHELL_TYPE_APP_CACHE (shell_app_cache_get_type()) ++ ++G_DECLARE_FINAL_TYPE (ShellAppCache, shell_app_cache, SHELL, APP_CACHE, GObject) ++ ++ShellAppCache *shell_app_cache_get_default (void); ++GList *shell_app_cache_get_all (ShellAppCache *cache); ++GDesktopAppInfo *shell_app_cache_get_info (ShellAppCache *cache, ++ const char *id); ++char *shell_app_cache_translate_folder (ShellAppCache *cache, ++ const char *name); ++ ++#endif /* __SHELL_APP_CACHE_PRIVATE_H__ */ +diff --git a/src/shell-app-cache.c b/src/shell-app-cache.c +new file mode 100644 +index 000000000..15d4734d0 +--- /dev/null ++++ b/src/shell-app-cache.c +@@ -0,0 +1,404 @@ ++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ ++ ++#include "config.h" ++ ++#include "shell-app-cache-private.h" ++ ++/** ++ * SECTION:shell-app-cache ++ * @title: ShellAppCache ++ * @short_description: application information cache ++ * ++ * The #ShellAppCache is responsible for caching information about #GAppInfo ++ * to ensure that the compositor thread never needs to perform disk reads to ++ * access them. All of the work is done off-thread. When the new data has ++ * been loaded, a #ShellAppCache::changed signal is emitted. ++ * ++ * Additionally, the #ShellAppCache caches information about translations for ++ * directories. This allows translation provided in [Desktop Entry] GKeyFiles ++ * to be available when building StLabel and other elements without performing ++ * costly disk reads. ++ * ++ * Various monitors are used to keep this information up to date while the ++ * Shell is running. ++ */ ++ ++#define DEFAULT_TIMEOUT_SECONDS 5 ++ ++struct _ShellAppCache ++{ ++ GObject parent_instance; ++ ++ GAppInfoMonitor *monitor; ++ GPtrArray *dir_monitors; ++ GHashTable *folders; ++ GCancellable *cancellable; ++ GList *app_infos; ++ ++ guint queued_update; ++}; ++ ++typedef struct ++{ ++ GList *app_infos; ++ GHashTable *folders; ++} CacheState; ++ ++G_DEFINE_TYPE (ShellAppCache, shell_app_cache, G_TYPE_OBJECT) ++ ++enum { ++ CHANGED, ++ N_SIGNALS ++}; ++ ++static guint signals [N_SIGNALS]; ++ ++static void ++cache_state_free (CacheState *state) ++{ ++ g_clear_pointer (&state->folders, g_hash_table_unref); ++ g_list_free_full (state->app_infos, g_object_unref); ++ g_slice_free (CacheState, state); ++} ++ ++static CacheState * ++cache_state_new (void) ++{ ++ CacheState *state; ++ ++ state = g_slice_new0 (CacheState); ++ state->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++ ++ return g_steal_pointer (&state); ++} ++ ++/** ++ * shell_app_cache_get_default: ++ * ++ * Gets the default #ShellAppCache. ++ * ++ * Returns: (transfer none): a #ShellAppCache ++ */ ++ShellAppCache * ++shell_app_cache_get_default (void) ++{ ++ static ShellAppCache *instance; ++ ++ if (instance == NULL) ++ { ++ instance = g_object_new (SHELL_TYPE_APP_CACHE, NULL); ++ g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance); ++ } ++ ++ return instance; ++} ++ ++static void ++load_folder (GHashTable *folders, ++ const char *path) ++{ ++ g_autoptr(GDir) dir = NULL; ++ const char *name; ++ ++ g_assert (folders != NULL); ++ g_assert (path != NULL); ++ ++ dir = g_dir_open (path, 0, NULL); ++ if (dir == NULL) ++ return; ++ ++ while ((name = g_dir_read_name (dir))) ++ { ++ g_autofree gchar *filename = NULL; ++ g_autoptr(GKeyFile) keyfile = NULL; ++ ++ /* First added wins */ ++ if (g_hash_table_contains (folders, name)) ++ continue; ++ ++ filename = g_build_filename (path, name, NULL); ++ keyfile = g_key_file_new (); ++ ++ if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, NULL)) ++ { ++ gchar *translated; ++ ++ translated = g_key_file_get_locale_string (keyfile, ++ "Desktop Entry", "Name", ++ NULL, NULL); ++ ++ if (translated != NULL) ++ g_hash_table_insert (folders, g_strdup (name), translated); ++ } ++ } ++} ++ ++static void ++load_folders (GHashTable *folders) ++{ ++ const char * const *dirs; ++ g_autofree gchar *userdir = NULL; ++ guint i; ++ ++ g_assert (folders != NULL); ++ ++ userdir = g_build_filename (g_get_user_data_dir (), "desktop-directories", NULL); ++ load_folder (folders, userdir); ++ ++ dirs = g_get_system_data_dirs (); ++ for (i = 0; dirs[i] != NULL; i++) ++ { ++ g_autofree gchar *sysdir = g_build_filename (dirs[i], "desktop-directories", NULL); ++ load_folder (folders, sysdir); ++ } ++} ++ ++static void ++shell_app_cache_worker (GTask *task, ++ gpointer source_object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ CacheState *state; ++ ++ g_assert (G_IS_TASK (task)); ++ g_assert (SHELL_IS_APP_CACHE (source_object)); ++ ++ state = cache_state_new (); ++ state->app_infos = g_app_info_get_all (); ++ load_folders (state->folders); ++ ++ g_task_return_pointer (task, state, (GDestroyNotify) cache_state_free); ++} ++ ++static void ++apply_update_cb (GObject *object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ ShellAppCache *cache = (ShellAppCache *)object; ++ g_autoptr(GError) error = NULL; ++ CacheState *state; ++ ++ g_assert (SHELL_IS_APP_CACHE (cache)); ++ g_assert (G_IS_TASK (result)); ++ g_assert (user_data == NULL); ++ ++ state = g_task_propagate_pointer (G_TASK (result), &error); ++ ++ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ return; ++ ++ g_list_free_full (cache->app_infos, g_object_unref); ++ cache->app_infos = g_steal_pointer (&state->app_infos); ++ ++ g_clear_pointer (&cache->folders, g_hash_table_unref); ++ cache->folders = g_steal_pointer (&state->folders); ++ ++ g_signal_emit (cache, signals[CHANGED], 0); ++ ++ cache_state_free (state); ++} ++ ++static gboolean ++shell_app_cache_do_update (gpointer user_data) ++{ ++ ShellAppCache *cache = user_data; ++ g_autoptr(GTask) task = NULL; ++ ++ cache->queued_update = 0; ++ ++ /* Reset the cancellable state so we don't race with ++ * two updates coming back overlapped and applying the ++ * information in the wrong order. ++ */ ++ g_cancellable_cancel (cache->cancellable); ++ g_clear_object (&cache->cancellable); ++ cache->cancellable = g_cancellable_new (); ++ ++ task = g_task_new (cache, cache->cancellable, apply_update_cb, NULL); ++ g_task_set_source_tag (task, shell_app_cache_do_update); ++ g_task_run_in_thread (task, shell_app_cache_worker); ++ ++ return G_SOURCE_REMOVE; ++} ++ ++static void ++shell_app_cache_queue_update (ShellAppCache *self) ++{ ++ g_assert (SHELL_IS_APP_CACHE (self)); ++ ++ if (self->queued_update != 0) ++ g_source_remove (self->queued_update); ++ ++ self->queued_update = g_timeout_add_seconds (DEFAULT_TIMEOUT_SECONDS, ++ shell_app_cache_do_update, ++ self); ++} ++ ++static void ++monitor_desktop_directories_for_data_dir (ShellAppCache *self, ++ const gchar *directory) ++{ ++ g_autofree gchar *subdir = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GFileMonitor) monitor = NULL; ++ ++ g_assert (SHELL_IS_APP_CACHE (self)); ++ ++ if (directory == NULL) ++ return; ++ ++ subdir = g_build_filename (directory, "desktop-directories", NULL); ++ file = g_file_new_for_path (subdir); ++ monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL); ++ ++ if (monitor != NULL) ++ { ++ g_file_monitor_set_rate_limit (monitor, DEFAULT_TIMEOUT_SECONDS * 1000); ++ g_signal_connect_object (monitor, ++ "changed", ++ G_CALLBACK (shell_app_cache_queue_update), ++ self, ++ G_CONNECT_SWAPPED); ++ g_ptr_array_add (self->dir_monitors, g_steal_pointer (&monitor)); ++ } ++} ++ ++static void ++shell_app_cache_finalize (GObject *object) ++{ ++ ShellAppCache *self = (ShellAppCache *)object; ++ ++ g_clear_object (&self->monitor); ++ ++ if (self->queued_update) ++ { ++ g_source_remove (self->queued_update); ++ self->queued_update = 0; ++ } ++ ++ g_clear_pointer (&self->dir_monitors, g_ptr_array_unref); ++ g_clear_pointer (&self->folders, g_hash_table_unref); ++ g_list_free_full (self->app_infos, g_object_unref); ++ ++ G_OBJECT_CLASS (shell_app_cache_parent_class)->finalize (object); ++} ++ ++static void ++shell_app_cache_class_init (ShellAppCacheClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ ++ object_class->finalize = shell_app_cache_finalize; ++ ++ /** ++ * ShellAppCache::changed: ++ * ++ * The "changed" signal is emitted when the cache has updated ++ * information about installed applications. ++ */ ++ signals [CHANGED] = ++ g_signal_new ("changed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++} ++ ++static void ++shell_app_cache_init (ShellAppCache *self) ++{ ++ const gchar * const *sysdirs; ++ guint i; ++ ++ /* Monitor directories for translation changes */ ++ self->dir_monitors = g_ptr_array_new_with_free_func (g_object_unref); ++ monitor_desktop_directories_for_data_dir (self, g_get_user_data_dir ()); ++ sysdirs = g_get_system_data_dirs (); ++ for (i = 0; sysdirs[i] != NULL; i++) ++ monitor_desktop_directories_for_data_dir (self, sysdirs[i]); ++ ++ /* Load translated directory names immediately */ ++ self->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); ++ load_folders (self->folders); ++ ++ /* Setup AppMonitor to track changes */ ++ self->monitor = g_app_info_monitor_get (); ++ g_signal_connect_object (self->monitor, ++ "changed", ++ G_CALLBACK (shell_app_cache_queue_update), ++ self, ++ G_CONNECT_SWAPPED); ++ self->app_infos = g_app_info_get_all (); ++} ++ ++/** ++ * shell_app_cache_get_all: ++ * @cache: (nullable): a #ShellAppCache or %NULL ++ * ++ * Like g_app_info_get_all() but always returns a ++ * cached set of application info so the caller can be ++ * sure that I/O will not happen on the current thread. ++ * ++ * Returns: (transfer none) (element-type GAppInfo): ++ * a #GList of references to #GAppInfo. ++ */ ++GList * ++shell_app_cache_get_all (ShellAppCache *cache) ++{ ++ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL); ++ ++ return cache->app_infos; ++} ++ ++/** ++ * shell_app_cache_get_info: ++ * @cache: (nullable): a #ShellAppCache or %NULL ++ * @id: the application id ++ * ++ * A replacement for g_desktop_app_info_new() that will lookup the ++ * information from the cache instead of (re)loading from disk. ++ * ++ * Returns: (nullable) (transfer none): a #GDesktopAppInfo or %NULL ++ */ ++GDesktopAppInfo * ++shell_app_cache_get_info (ShellAppCache *cache, ++ const char *id) ++{ ++ const GList *iter; ++ ++ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL); ++ ++ for (iter = cache->app_infos; iter != NULL; iter = iter->next) ++ { ++ GAppInfo *info = iter->data; ++ ++ if (g_strcmp0 (id, g_app_info_get_id (info)) == 0) ++ return G_DESKTOP_APP_INFO (info); ++ } ++ ++ return NULL; ++} ++ ++/** ++ * shell_app_cache_translate_folder: ++ * @cache: (nullable): a #ShellAppCache or %NULL ++ * @name: the folder name ++ * ++ * Gets the translated folder name for @name if any exists. ++ * ++ * Returns: (nullable): the translated string or %NULL if there is no ++ * translation. ++ */ ++char * ++shell_app_cache_translate_folder (ShellAppCache *cache, ++ const char *name) ++{ ++ g_return_val_if_fail (SHELL_IS_APP_CACHE (cache), NULL); ++ ++ if (name == NULL) ++ return NULL; ++ ++ return g_strdup (g_hash_table_lookup (cache->folders, name)); ++} +diff --git a/src/shell-app-system.c b/src/shell-app-system.c +index 127f29ef0..828fa726a 100644 +--- a/src/shell-app-system.c ++++ b/src/shell-app-system.c +@@ -9,6 +9,7 @@ + #include + #include + ++#include "shell-app-cache-private.h" + #include "shell-app-private.h" + #include "shell-window-tracker-private.h" + #include "shell-app-system-private.h" +@@ -94,14 +95,14 @@ static void + scan_startup_wm_class_to_id (ShellAppSystem *self) + { + ShellAppSystemPrivate *priv = self->priv; +- GList *l; ++ const GList *l; ++ GList *all; + + g_hash_table_remove_all (priv->startup_wm_class_to_id); + +- g_list_free_full (priv->installed_apps, g_object_unref); +- priv->installed_apps = g_app_info_get_all (); ++ all = shell_app_cache_get_all (shell_app_cache_get_default ()); + +- for (l = priv->installed_apps; l != NULL; l = l->next) ++ for (l = all; l != NULL; l = l->next) + { + GAppInfo *info = l->data; + const char *startup_wm_class, *id, *old_id; +@@ -131,7 +132,8 @@ app_is_stale (ShellApp *app) + if (shell_app_is_window_backed (app)) + return FALSE; + +- info = g_desktop_app_info_new (shell_app_get_id (app)); ++ info = shell_app_cache_get_info (shell_app_cache_get_default (), ++ shell_app_get_id (app)); + if (!info) + return TRUE; + +@@ -156,7 +158,6 @@ app_is_stale (ShellApp *app) + g_icon_equal (g_app_info_get_icon (old_info), + g_app_info_get_icon (new_info)); + +- g_object_unref (info); + return !is_unchanged; + } + +@@ -210,11 +211,9 @@ rescan_icon_theme (ShellAppSystem *self) + } + + static void +-installed_changed (GAppInfoMonitor *monitor, +- gpointer user_data) ++installed_changed (ShellAppCache *cache, ++ ShellAppSystem *self) + { +- ShellAppSystem *self = user_data; +- + rescan_icon_theme (self); + scan_startup_wm_class_to_id (self); + +@@ -227,7 +226,7 @@ static void + shell_app_system_init (ShellAppSystem *self) + { + ShellAppSystemPrivate *priv; +- GAppInfoMonitor *monitor; ++ ShellAppCache *cache; + + self->priv = priv = shell_app_system_get_instance_private (self); + +@@ -238,9 +237,9 @@ shell_app_system_init (ShellAppSystem *self) + + priv->startup_wm_class_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + +- monitor = g_app_info_monitor_get (); +- g_signal_connect (monitor, "changed", G_CALLBACK (installed_changed), self); +- installed_changed (monitor, self); ++ cache = shell_app_cache_get_default (); ++ g_signal_connect (cache, "changed", G_CALLBACK (installed_changed), self); ++ installed_changed (cache, self); + } + + static void +@@ -293,13 +292,12 @@ shell_app_system_lookup_app (ShellAppSystem *self, + if (app) + return app; + +- info = g_desktop_app_info_new (id); ++ info = shell_app_cache_get_info (shell_app_cache_get_default (), id); + if (!info) + return NULL; + + app = _shell_app_new (info); + g_hash_table_insert (priv->id_to_app, (char *) shell_app_get_id (app), app); +- g_object_unref (info); + return app; + } + +@@ -506,7 +504,5 @@ shell_app_system_search (const char *search_string) + GList * + shell_app_system_get_installed (ShellAppSystem *self) + { +- ShellAppSystemPrivate *priv = self->priv; +- +- return priv->installed_apps; ++ return shell_app_cache_get_all (shell_app_cache_get_default ()); + } +diff --git a/src/shell-util.c b/src/shell-util.c +index fa3fc08c8..370784523 100644 +--- a/src/shell-util.c ++++ b/src/shell-util.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "shell-app-cache-private.h" + #include "shell-util.h" + #include + #include +@@ -639,3 +640,18 @@ shell_util_has_x11_display_extension (MetaDisplay *display, + xdisplay = meta_x11_display_get_xdisplay (x11_display); + return XQueryExtension (xdisplay, extension, &op, &event, &error); + } ++ ++/** ++ * shell_util_get_translated_folder_name: ++ * @name: the untranslated folder name ++ * ++ * Attempts to translate the folder @name using translations provided ++ * by .directory files. ++ * ++ * Returns: (nullable): a translated string or %NULL ++ */ ++char * ++shell_util_get_translated_folder_name (const char *name) ++{ ++ return shell_app_cache_translate_folder (shell_app_cache_get_default (), name); ++} +diff --git a/src/shell-util.h b/src/shell-util.h +index 02b8404e9..843a1253d 100644 +--- a/src/shell-util.h ++++ b/src/shell-util.h +@@ -62,6 +62,8 @@ void shell_util_check_cloexec_fds (void); + gboolean shell_util_has_x11_display_extension (MetaDisplay *display, + const char *extension); + ++char *shell_util_get_translated_folder_name (const char *name); ++ + G_END_DECLS + + #endif /* __SHELL_UTIL_H__ */ +-- +2.26.2 + diff --git a/SOURCES/0006-js-Always-use-AppSystem-to-lookup-apps.patch b/SOURCES/0006-js-Always-use-AppSystem-to-lookup-apps.patch new file mode 100644 index 0000000..73080e8 --- /dev/null +++ b/SOURCES/0006-js-Always-use-AppSystem-to-lookup-apps.patch @@ -0,0 +1,66 @@ +From a0df79f8de4c13c36ed3b22cfdbb78e324424ef1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Sat, 14 Mar 2020 14:45:42 +0100 +Subject: [PATCH 6/6] js: Always use AppSystem to lookup apps + +There is no good reason for bypassing the application cache in +AppSystem and loading .desktop files again. + +https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1093 +--- + js/ui/appDisplay.js | 4 ++-- + js/ui/calendar.js | 16 ++++++++++------ + 2 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js +index a2d691085..cb2be7d3c 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -1001,8 +1001,8 @@ var AppSearchProvider = class AppSearchProvider { + let results = []; + groups.forEach(group => { + group = group.filter(appID => { +- let app = Gio.DesktopAppInfo.new(appID); +- return app && app.should_show(); ++ const app = this._appSys.lookup_app(appID); ++ return app && app.app_info.should_show(); + }); + results = results.concat(group.sort( + (a, b) => usage.compare(a, b) +diff --git a/js/ui/calendar.js b/js/ui/calendar.js +index cd3e879c4..3ae2e44f8 100644 +--- a/js/ui/calendar.js ++++ b/js/ui/calendar.js +@@ -791,8 +791,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { + this._title.connect('clicked', this._onTitleClicked.bind(this)); + this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this)); + +- Shell.AppSystem.get_default().connect('installed-changed', +- this._appInstalledChanged.bind(this)); ++ this._appSys = Shell.AppSystem.get_default(); ++ this._appSys.connect('installed-changed', ++ this._appInstalledChanged.bind(this)); + this._appInstalledChanged(); + } + +@@ -883,10 +884,13 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection { + Main.overview.hide(); + Main.panel.closeCalendar(); + +- let app = this._getCalendarApp(); +- if (app.get_id() == 'evolution.desktop') +- app = Gio.DesktopAppInfo.new('evolution-calendar.desktop'); +- app.launch([], global.create_app_launch_context(0, -1)); ++ let appInfo = this._getCalendarApp(); ++ if (app.get_id() == 'evolution.desktop') { ++ let app = this._appSys.lookup_app('evolution-calendar.desktop'); ++ if (app) ++ appInfo = app.app_info; ++ } ++ appInfo.launch([], global.create_app_launch_context(0, -1)); + } + + setDate(date) { +-- +2.26.2 + diff --git a/SOURCES/allow-timed-login-with-no-user-list.patch b/SOURCES/allow-timed-login-with-no-user-list.patch index bc5b56e..040e507 100644 --- a/SOURCES/allow-timed-login-with-no-user-list.patch +++ b/SOURCES/allow-timed-login-with-no-user-list.patch @@ -1,4 +1,4 @@ -From de891fadb0b40a9b6e84131b82086e42d86992a1 Mon Sep 17 00:00:00 2001 +From 3252f05b8745a5d3118986474793fe3ecc2b041c Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 19 Apr 2016 13:12:46 -0400 Subject: [PATCH] loginDialog: allow timed login with disabled user list @@ -13,8 +13,8 @@ auth prompt, in that scenario. --- data/theme/gnome-shell-sass/_common.scss | 4 +++ js/gdm/authPrompt.js | 41 +++++++++++++++++++++++- - js/gdm/loginDialog.js | 23 ++++++++++++- - 3 files changed, 66 insertions(+), 2 deletions(-) + js/gdm/loginDialog.js | 25 +++++++++++++-- + 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss index a6357baad..c2df28279 100644 @@ -97,7 +97,7 @@ index 27eb31a89..cf77b3f26 100644 if (this._preemptiveAnswerWatchId) { this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js -index 6c4d1357d..9aaa013d8 100644 +index 6c4d1357d..b4df6e959 100644 --- a/js/gdm/loginDialog.js +++ b/js/gdm/loginDialog.js @@ -734,6 +734,9 @@ var LoginDialog = GObject.registerClass({ @@ -110,7 +110,7 @@ index 6c4d1357d..9aaa013d8 100644 } } -@@ -1020,9 +1023,21 @@ var LoginDialog = GObject.registerClass({ +@@ -1020,16 +1023,31 @@ var LoginDialog = GObject.registerClass({ let loginItem = null; let animationTime; @@ -125,15 +125,16 @@ index 6c4d1357d..9aaa013d8 100644 + }, () => { +- loginItem = this._userList.getItemFromUserName(userName); + this._timedLoginUserListHold = null; + -+ if (this._disableUserList) -+ return; + - loginItem = this._userList.getItemFromUserName(userName); ++ loginItem = this._disableUserList ++ ? this._authPrompt ++ : this._userList.getItemFromUserName(userName); // If there is an animation running on the item, reset it. -@@ -1030,6 +1045,9 @@ var LoginDialog = GObject.registerClass({ + loginItem.hideTimedLoginIndicator(); }, () => { @@ -154,5 +155,5 @@ index 6c4d1357d..9aaa013d8 100644 if (delay > _TIMED_LOGIN_IDLE_THRESHOLD && this._authPrompt.actor.visible) -- -2.21.0 +2.26.2 diff --git a/SOURCES/introspect-backports.patch b/SOURCES/introspect-backports.patch new file mode 100644 index 0000000..b93532c --- /dev/null +++ b/SOURCES/introspect-backports.patch @@ -0,0 +1,643 @@ +From 781dfcf6ce7168c6b116d58df5f1c67291a7b513 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 16 May 2019 00:57:27 +0200 +Subject: [PATCH 01/11] introspect: Include `sandboxed-app-id` as well + +App IDs in gnome-shell don't match AppStream, Flatpak or Snap IDs. For the +desktop portal, the latter two are more relevant, so include it in the +returned information. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/1289 +--- + js/misc/introspect.js | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index f7a7f2fe6..1e8300d0a 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -55,6 +55,11 @@ var IntrospectService = class { + return APP_WHITELIST.includes(sender); + } + ++ _getSandboxedAppId(app) { ++ let ids = app.get_windows().map(w => w.get_sandboxed_app_id()); ++ return ids.find(id => id != null); ++ } ++ + _syncRunningApplications() { + let tracker = Shell.WindowTracker.get_default(); + let apps = this._appSystem.get_running(); +@@ -76,6 +81,10 @@ var IntrospectService = class { + newActiveApplication = app.get_id(); + } + ++ let sandboxedAppId = this._getSandboxedAppId(app); ++ if (sandboxedAppId) ++ appInfo['sandboxed-app-id'] = new GLib.Variant('s', sandboxedAppId); ++ + newRunningApplications[app.get_id()] = appInfo; + } + +@@ -137,6 +146,7 @@ var IntrospectService = class { + let frameRect = window.get_frame_rect(); + let title = window.get_title(); + let wmClass = window.get_wm_class(); ++ let sandboxedAppId = window.get_sandboxed_app_id(); + + windowsList[windowId] = { + 'app-id': GLib.Variant.new('s', app.get_id()), +@@ -153,6 +163,10 @@ var IntrospectService = class { + + if (wmClass != null) + windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass); ++ ++ if (sandboxedAppId != null) ++ windowsList[windowId]['sandboxed-app-id'] = ++ GLib.Variant.new('s', sandboxedAppId); + } + } + invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList])); +-- +2.26.2 + + +From b0b4fb82c058722e2171d24902ba3855ffe243f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 18 Sep 2019 14:57:48 +0200 +Subject: [PATCH 02/11] introspect: Check whitelist also for + GetRunningWindows() + +Otherwise the xdg-desktop-portal-gtk screen cast widget won't work. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/732 +--- + js/misc/introspect.js | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index 1e8300d0a..cee6409a8 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -128,7 +128,8 @@ var IntrospectService = class { + let apps = this._appSystem.get_running(); + let windowsList = {}; + +- if (!this._isIntrospectEnabled()) { ++ if (!this._isIntrospectEnabled() && ++ !this._isSenderWhitelisted(invocation.get_sender())) { + invocation.return_error_literal(Gio.DBusError, + Gio.DBusError.ACCESS_DENIED, + 'App introspection not allowed'); +-- +2.26.2 + + +From 23556e03db3743ddf478a3c1bbb64946c687afdf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 25 Nov 2019 19:44:10 +0100 +Subject: [PATCH 03/11] introspect: Fix whitelist check + +The whitelist is a list of well-known D-Bus names, which we then search +for the unique name we get from the method invocation - unsuccesfully. + +Fix this by watching the bus for any name in the whitelist in order +to maintain a map from wel-known to unique name that we can use for +matching. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/1916 +--- + js/misc/introspect.js | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index cee6409a8..f14eabfad 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -39,6 +39,15 @@ var IntrospectService = class { + }); + + this._syncRunningApplications(); ++ ++ this._whitelistMap = new Map(); ++ APP_WHITELIST.forEach(appName => { ++ Gio.DBus.watch_name(Gio.BusType.SESSION, ++ appName, ++ Gio.BusNameWatcherFlags.NONE, ++ (conn, name, owner) => this._whitelistMap.set(name, owner), ++ (conn, name) => this._whitelistMap.delete(name)); ++ }); + } + + _isStandaloneApp(app) { +@@ -52,7 +61,7 @@ var IntrospectService = class { + } + + _isSenderWhitelisted(sender) { +- return APP_WHITELIST.includes(sender); ++ return [...this._whitelistMap.values()].includes(sender); + } + + _getSandboxedAppId(app) { +-- +2.26.2 + + +From 1a6275add6d214df958ed8a06c097445bef021bc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 25 Sep 2019 20:36:28 +0200 +Subject: [PATCH 04/11] introspect: Add helper to check method call permission + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + js/misc/introspect.js | 16 ++++++++++++---- + 1 file changed, 12 insertions(+), 4 deletions(-) + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index f14eabfad..6186754cd 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -120,9 +120,18 @@ var IntrospectService = class { + type == Meta.WindowType.UTILITY); + } + ++ _isInvocationAllowed(invocation) { ++ if (this._isIntrospectEnabled()) ++ return true; ++ ++ if (this._isSenderWhitelisted(invocation.get_sender())) ++ return true; ++ ++ return false; ++ } ++ + GetRunningApplicationsAsync(params, invocation) { +- if (!this._isIntrospectEnabled() && +- !this._isSenderWhitelisted(invocation.get_sender())) { ++ if (!this._isInvocationAllowed(invocation)) { + invocation.return_error_literal(Gio.DBusError, + Gio.DBusError.ACCESS_DENIED, + 'App introspection not allowed'); +@@ -137,8 +146,7 @@ var IntrospectService = class { + let apps = this._appSystem.get_running(); + let windowsList = {}; + +- if (!this._isIntrospectEnabled() && +- !this._isSenderWhitelisted(invocation.get_sender())) { ++ if (!this._isInvocationAllowed(invocation)) { + invocation.return_error_literal(Gio.DBusError, + Gio.DBusError.ACCESS_DENIED, + 'App introspection not allowed'); +-- +2.26.2 + + +From f578dc01cf774faa4504a4d258cc0e82060d988b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 11:55:33 +0200 +Subject: [PATCH 05/11] shell-util: Add API to check for X11 extensions + +Will be used to disable animations when running inside Xvnc. This was +done in gsd-xsettings before. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + src/shell-util.c | 26 ++++++++++++++++++++++++++ + src/shell-util.h | 3 +++ + 2 files changed, 29 insertions(+) + +diff --git a/src/shell-util.c b/src/shell-util.c +index 31bb18e70..fa3fc08c8 100644 +--- a/src/shell-util.c ++++ b/src/shell-util.c +@@ -21,6 +21,8 @@ + #include + #include + #include ++#include ++#include + + #include + #ifdef HAVE__NL_TIME_FIRST_WEEKDAY +@@ -613,3 +615,27 @@ shell_util_check_cloexec_fds (void) + fdwalk (check_cloexec, NULL); + g_info ("Open fd CLOEXEC check complete"); + } ++ ++/** ++ * shell_util_has_x11_display_extension: ++ * @display: A #MetaDisplay ++ * @extension: An X11 extension ++ * ++ * If the corresponding X11 display provides the passed extension, return %TRUE, ++ * otherwise %FALSE. If there is no X11 display, %FALSE is passed. ++ */ ++gboolean ++shell_util_has_x11_display_extension (MetaDisplay *display, ++ const char *extension) ++{ ++ MetaX11Display *x11_display; ++ Display *xdisplay; ++ int op, event, error; ++ ++ x11_display = meta_display_get_x11_display (display); ++ if (!x11_display) ++ return FALSE; ++ ++ xdisplay = meta_x11_display_get_xdisplay (x11_display); ++ return XQueryExtension (xdisplay, extension, &op, &event, &error); ++} +diff --git a/src/shell-util.h b/src/shell-util.h +index 6904f43bc..02b8404e9 100644 +--- a/src/shell-util.h ++++ b/src/shell-util.h +@@ -59,6 +59,9 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures + + void shell_util_check_cloexec_fds (void); + ++gboolean shell_util_has_x11_display_extension (MetaDisplay *display, ++ const char *extension); ++ + G_END_DECLS + + #endif /* __SHELL_UTIL_H__ */ +-- +2.26.2 + + +From 48ee79bb7b48c7e93e77e35629f21bbdbabc253f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 11:56:34 +0200 +Subject: [PATCH 06/11] st/settings: Add API to inhibit animations + +There may be situations where we shouldn't enable animations. Make it +possible for the Shell to decide when there are such situations and in +when needed inhibit animations. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + src/st/st-settings.c | 38 +++++++++++++++++++++++++++++++++++++- + src/st/st-settings.h | 4 ++++ + 2 files changed, 41 insertions(+), 1 deletion(-) + +diff --git a/src/st/st-settings.c b/src/st/st-settings.c +index 17f2c466e..ebfd28480 100644 +--- a/src/st/st-settings.c ++++ b/src/st/st-settings.c +@@ -54,6 +54,7 @@ struct _StSettings + + gchar *gtk_theme; + gchar *gtk_icon_theme; ++ int inhibit_animations_count; + gboolean enable_animations; + gboolean primary_paste; + gboolean magnifier_active; +@@ -62,6 +63,41 @@ struct _StSettings + + G_DEFINE_TYPE (StSettings, st_settings, G_TYPE_OBJECT) + ++static gboolean ++get_enable_animations (StSettings *settings) ++{ ++ if (settings->inhibit_animations_count > 0) ++ return FALSE; ++ else ++ return settings->enable_animations; ++} ++ ++void ++st_settings_inhibit_animations (StSettings *settings) ++{ ++ gboolean enable_animations; ++ ++ enable_animations = get_enable_animations (settings); ++ settings->inhibit_animations_count++; ++ ++ if (enable_animations != get_enable_animations (settings)) ++ g_object_notify_by_pspec (G_OBJECT (settings), ++ props[PROP_ENABLE_ANIMATIONS]); ++} ++ ++void ++st_settings_uninhibit_animations (StSettings *settings) ++{ ++ gboolean enable_animations; ++ ++ enable_animations = get_enable_animations (settings); ++ settings->inhibit_animations_count--; ++ ++ if (enable_animations != get_enable_animations (settings)) ++ g_object_notify_by_pspec (G_OBJECT (settings), ++ props[PROP_ENABLE_ANIMATIONS]); ++} ++ + static void + st_settings_finalize (GObject *object) + { +@@ -95,7 +131,7 @@ st_settings_get_property (GObject *object, + switch (prop_id) + { + case PROP_ENABLE_ANIMATIONS: +- g_value_set_boolean (value, settings->enable_animations); ++ g_value_set_boolean (value, get_enable_animations (settings)); + break; + case PROP_PRIMARY_PASTE: + g_value_set_boolean (value, settings->primary_paste); +diff --git a/src/st/st-settings.h b/src/st/st-settings.h +index c2c4fa23e..8b2549469 100644 +--- a/src/st/st-settings.h ++++ b/src/st/st-settings.h +@@ -33,6 +33,10 @@ G_DECLARE_FINAL_TYPE (StSettings, st_settings, ST, SETTINGS, GObject) + + StSettings * st_settings_get (void); + ++void st_settings_inhibit_animations (StSettings *settings); ++ ++void st_settings_uninhibit_animations (StSettings *settings); ++ + G_END_DECLS + + #endif /* __ST_SETTINGS_H__ */ +-- +2.26.2 + + +From 80025388c44296b629c8f24ea673d77ffc4efc67 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 12:02:31 +0200 +Subject: [PATCH 07/11] main: Inhibit animations when software rendered + +This was previously decided by gsd-xsettings. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + js/ui/main.js | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/js/ui/main.js b/js/ui/main.js +index 978f83c3f..c3230ff03 100644 +--- a/js/ui/main.js ++++ b/js/ui/main.js +@@ -147,6 +147,8 @@ function _initializeUI() { + _loadOskLayouts(); + _loadDefaultStylesheet(); + ++ new AnimationsSettings(); ++ + // Setup the stage hierarchy early + layoutManager = new Layout.LayoutManager(); + +@@ -723,3 +725,13 @@ function showRestartMessage(message) { + let restartMessage = new RestartMessage(message); + restartMessage.open(); + } ++ ++var AnimationsSettings = class { ++ constructor() { ++ let backend = Meta.get_backend(); ++ if (!backend.is_rendering_hardware_accelerated()) { ++ St.Settings.get().inhibit_animations(); ++ return; ++ } ++ } ++}; +-- +2.26.2 + + +From 788ecb60e35d8a369f0747813f37e8b1ca27cb87 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 12:03:52 +0200 +Subject: [PATCH 08/11] main: Inhibit animations if X server advertises + VNC-EXTENSION + +This was previously done by gsd-xsettings to disable animations when +running in Xvnc. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + js/ui/main.js | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/js/ui/main.js b/js/ui/main.js +index c3230ff03..ae7c3ffd0 100644 +--- a/js/ui/main.js ++++ b/js/ui/main.js +@@ -733,5 +733,12 @@ var AnimationsSettings = class { + St.Settings.get().inhibit_animations(); + return; + } ++ ++ let isXvnc = Shell.util_has_x11_display_extension( ++ global.display, 'VNC-EXTENSION'); ++ if (isXvnc) { ++ St.Settings.get().inhibit_animations(); ++ return; ++ } + } + }; +-- +2.26.2 + + +From 1da5a7ce4cf0b95b96dd50b62ac6c1380fd88cb1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 12:04:52 +0200 +Subject: [PATCH 09/11] main: Inhibit animations when there is a remote desktop + session + +If a remote desktop session asks for animations to be disabled, inhibit +animations while the session is active. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + js/ui/main.js | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/js/ui/main.js b/js/ui/main.js +index ae7c3ffd0..1203b3c39 100644 +--- a/js/ui/main.js ++++ b/js/ui/main.js +@@ -740,5 +740,31 @@ var AnimationsSettings = class { + St.Settings.get().inhibit_animations(); + return; + } ++ ++ let remoteAccessController = backend.get_remote_access_controller(); ++ if (!remoteAccessController) ++ return; ++ ++ this._handles = new Set(); ++ remoteAccessController.connect('new-handle', ++ (_, handle) => this._onNewRemoteAccessHandle(handle)); ++ } ++ ++ _onRemoteAccessHandleStopped(handle) { ++ let settings = St.Settings.get(); ++ ++ settings.uninhibit_animations(); ++ this._handles.delete(handle); ++ } ++ ++ _onNewRemoteAccessHandle(handle) { ++ if (!handle.get_disable_animations()) ++ return; ++ ++ let settings = St.Settings.get(); ++ ++ settings.inhibit_animations(); ++ this._handles.add(handle); ++ handle.connect('stopped', this._onRemoteAccessHandleStopped.bind(this)); + } + }; +-- +2.26.2 + + +From ebfd46341a2d7a6338386e4be4a2807a6bc6e63c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 12:06:13 +0200 +Subject: [PATCH 10/11] introspect: Rename variable + +It was too generic, and would conflict with a StSettings variable. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + js/misc/introspect.js | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index 6186754cd..8e68a7e4f 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -29,7 +29,9 @@ var IntrospectService = class { + this._syncRunningApplications(); + }); + +- this._settings = new Gio.Settings({ schema_id: INTROSPECT_SCHEMA }); ++ this._introspectSettings = new Gio.Settings({ ++ schema_id: INTROSPECT_SCHEMA, ++ }); + + let tracker = Shell.WindowTracker.get_default(); + tracker.connect('notify::focus-app', +@@ -57,7 +59,7 @@ var IntrospectService = class { + } + + _isIntrospectEnabled() { +- return this._settings.get_boolean(INTROSPECT_KEY); ++ return this._introspectSettings.get_boolean(INTROSPECT_KEY); + } + + _isSenderWhitelisted(sender) { +-- +2.26.2 + + +From 343e7792fc84c296b331c3fcb142ed79d2ce9bd5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 12:07:03 +0200 +Subject: [PATCH 11/11] introspect: Add AnimationsEnabled property + +While the gsetting is available for all who needs it, the Shell might +override it given various hueristics. Expose the decision made by the +Shell via a new property. + +Intended to be used by gsd-xsettings as well as xdg-desktop-portal-gtk. + +This also add a version property to the API, so that semi external +services (xdg-desktop-portal-gtk) can detect what API is expected to be +present. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757 +--- + .../org.gnome.Shell.Introspect.xml | 14 ++++++++++ + js/misc/introspect.js | 27 ++++++++++++++++++- + 2 files changed, 40 insertions(+), 1 deletion(-) + +diff --git a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml +index 9508681af..d71f2414b 100644 +--- a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml ++++ b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml +@@ -57,5 +57,19 @@ + + + ++ ++ ++ ++ ++ + + +diff --git a/js/misc/introspect.js b/js/misc/introspect.js +index 8e68a7e4f..7c62113e5 100644 +--- a/js/misc/introspect.js ++++ b/js/misc/introspect.js +@@ -1,9 +1,11 @@ +-const { Gio, GLib, Meta, Shell } = imports.gi; ++const { Gio, GLib, Meta, Shell, St } = imports.gi; + + const INTROSPECT_SCHEMA = 'org.gnome.shell'; + const INTROSPECT_KEY = 'introspect'; + const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk']; + ++const INTROSPECT_DBUS_API_VERSION = 2; ++ + const { loadInterfaceXML } = imports.misc.fileUtils; + + const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect'); +@@ -21,6 +23,7 @@ var IntrospectService = class { + this._runningApplicationsDirty = true; + this._activeApplication = null; + this._activeApplicationDirty = true; ++ this._animationsEnabled = true; + + this._appSystem = Shell.AppSystem.get_default(); + this._appSystem.connect('app-state-changed', +@@ -50,6 +53,11 @@ var IntrospectService = class { + (conn, name, owner) => this._whitelistMap.set(name, owner), + (conn, name) => this._whitelistMap.delete(name)); + }); ++ ++ this._settings = St.Settings.get(); ++ this._settings.connect('notify::enable-animations', ++ this._syncAnimationsEnabled.bind(this)); ++ this._syncAnimationsEnabled(); + } + + _isStandaloneApp(app) { +@@ -191,4 +199,21 @@ var IntrospectService = class { + } + invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList])); + } ++ ++ _syncAnimationsEnabled() { ++ let wasAnimationsEnabled = this._animationsEnabled; ++ this._animationsEnabled = this._settings.enable_animations; ++ if (wasAnimationsEnabled !== this._animationsEnabled) { ++ let variant = new GLib.Variant('b', this._animationsEnabled); ++ this._dbusImpl.emit_property_changed('AnimationsEnabled', variant); ++ } ++ } ++ ++ get AnimationsEnabled() { ++ return this._animationsEnabled; ++ } ++ ++ get version() { ++ return INTROSPECT_DBUS_API_VERSION; ++ } + }; +-- +2.26.2 + diff --git a/SOURCES/more-spurious-allocation-warnings.patch b/SOURCES/more-spurious-allocation-warnings.patch new file mode 100644 index 0000000..16acc8f --- /dev/null +++ b/SOURCES/more-spurious-allocation-warnings.patch @@ -0,0 +1,176 @@ +From 4926a9b8f958617d67d603622b1382c17fe4037c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Wed, 20 May 2020 12:05:04 +0200 +Subject: [PATCH 1/2] workspacesView: Avoid setting invalid geometries on views + +The fullGeometry and the actualGeometry of the WorkspacesDisplay are set +from the allocation of the overviews ControlsManager and the +WorkspacesDisplay, that means they're only valid after those actors got +their allocations during Clutters allocation cycle. + +Since WorkspacesDisplay._updateWorkspacesViews() is already called while +showing/mapping the WorkspacesDisplay, that allocation cycle didn't +happen yet and we end up either setting the geometries of the views to +null (in case of the fullGeometry) or to something wrong (a 0-sized +allocation in case of the actualGeometry). + +So avoid setting invalid geometries on the views by initializing both +the fullGeometry and the actualGeometry to null, and then only updating +the geometries of the views after they're set to a correct value. + +Note that this means we won't correctly animate the overview the first +time we open it since the animation depends on the geometries being set, +but is being started from show(), which means no allocations have +happened yet. In practice this introduces no regression though since +before this change we simply used incorrect geometries (see the 0-sized +allocation mentioned above) on the initial opening and the animation +didn't work either. + +https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119 +--- + js/ui/workspacesView.js | 28 +++++++++++++++++----------- + 1 file changed, 17 insertions(+), 11 deletions(-) + +diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js +index e302296a6..02baddc6e 100644 +--- a/js/ui/workspacesView.js ++++ b/js/ui/workspacesView.js +@@ -521,6 +521,7 @@ var WorkspacesDisplay = class { + this._scrollEventId = 0; + this._keyPressEventId = 0; + ++ this._actualGeometry = null; + this._fullGeometry = null; + } + +@@ -675,8 +676,10 @@ var WorkspacesDisplay = class { + + this._workspacesViews.forEach(v => v.actor.show()); + +- this._updateWorkspacesFullGeometry(); +- this._updateWorkspacesActualGeometry(); ++ if (this._fullGeometry) ++ this._syncWorkspacesFullGeometry(); ++ if (this._actualGeometry) ++ this._syncWorkspacesActualGeometry(); + } + + _scrollValueChanged() { +@@ -739,10 +742,10 @@ var WorkspacesDisplay = class { + // the sliding controls were never slid in at all. + setWorkspacesFullGeometry(geom) { + this._fullGeometry = geom; +- this._updateWorkspacesFullGeometry(); ++ this._syncWorkspacesFullGeometry(); + } + +- _updateWorkspacesFullGeometry() { ++ _syncWorkspacesFullGeometry() { + if (!this._workspacesViews.length) + return; + +@@ -754,18 +757,21 @@ var WorkspacesDisplay = class { + } + + _updateWorkspacesActualGeometry() { ++ const [x, y] = this.actor.get_transformed_position(); ++ const width = this.actor.allocation.get_width(); ++ const height = this.actor.allocation.get_height(); ++ ++ this._actualGeometry = { x, y, width, height }; ++ this._syncWorkspacesActualGeometry(); ++ } ++ ++ _syncWorkspacesActualGeometry() { + if (!this._workspacesViews.length) + return; + +- let [x, y] = this.actor.get_transformed_position(); +- let allocation = this.actor.allocation; +- let width = allocation.x2 - allocation.x1; +- let height = allocation.y2 - allocation.y1; +- let primaryGeometry = { x: x, y: y, width: width, height: height }; +- + let monitors = Main.layoutManager.monitors; + for (let i = 0; i < monitors.length; i++) { +- let geometry = (i == this._primaryIndex) ? primaryGeometry : monitors[i]; ++ let geometry = i === this._primaryIndex ? this._actualGeometry : monitors[i]; + this._workspacesViews[i].setActualGeometry(geometry); + } + } +-- +2.26.2 + + +From 4671eebccf4e6afce8c0a869d63095b39aa7e163 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Wed, 20 May 2020 13:39:11 +0200 +Subject: [PATCH 2/2] workspacesView: Only animate on show() when geometries + are already set + +Animating the window clones of the overview requires the fullGeometry +and the actualGeometry to be set, which they won't be when showing the +overview for the first time. So don't even try to animate the window +clones in that case because the geometries will still be null and +accessing them in workspace.js will throw errors. + +The workspace views will still get the correct layout as soon as the +allocations happen because syncing the geometries will trigger updating +the window positions. Since animations are disabled for position changes +when syncing the geometry though, we won't get an animation and the +clones will jump into place. That's not a regression though since before +this change we also didn't animate in that case because the geometries +used were simply wrong (the actualGeometry was 0-sized as explained in +the last commit). + +If we wanted to fix the initial animation of the overview, we'd have to +always enable animations of the window clones when syncing geometries, +but that would break the animation of the workspace when hovering the +workspaceThumbnail slider, because right now those animations are "glued +together" using the actualGeometry, so they would get out of sync. + +The reason there are no errors happening in workspace.js with the +existing code is that due to a bug in Clutter the fullGeometry of +WorkspacesDisplay gets set very early while mapping the WorkspacesViews +(because the overviews ControlsManager gets an allocation during the +resource scale calculation of a ClutterClone, see +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1181), so it +won't be set to null anymore when calling +WorkspacesView.animateToOverview(). + +https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119 +--- + js/ui/workspacesView.js | 17 ++++++++++------- + 1 file changed, 10 insertions(+), 7 deletions(-) + +diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js +index 02baddc6e..3e9d77655 100644 +--- a/js/ui/workspacesView.js ++++ b/js/ui/workspacesView.js +@@ -589,13 +589,16 @@ var WorkspacesDisplay = class { + + show(fadeOnPrimary) { + this._updateWorkspacesViews(); +- for (let i = 0; i < this._workspacesViews.length; i++) { +- let animationType; +- if (fadeOnPrimary && i == this._primaryIndex) +- animationType = AnimationType.FADE; +- else +- animationType = AnimationType.ZOOM; +- this._workspacesViews[i].animateToOverview(animationType); ++ ++ if (this._actualGeometry && this._fullGeometry) { ++ for (let i = 0; i < this._workspacesViews.length; i++) { ++ let animationType; ++ if (fadeOnPrimary && i == this._primaryIndex) ++ animationType = AnimationType.FADE; ++ else ++ animationType = AnimationType.ZOOM; ++ this._workspacesViews[i].animateToOverview(animationType); ++ } + } + + this._restackedNotifyId = +-- +2.26.2 + diff --git a/SOURCES/perf-tool-wayland.patch b/SOURCES/perf-tool-wayland.patch index c7530a6..be4b625 100644 --- a/SOURCES/perf-tool-wayland.patch +++ b/SOURCES/perf-tool-wayland.patch @@ -1,7 +1,7 @@ -From 3c4c37e4d0668d748ee792f7580defdf4780624a Mon Sep 17 00:00:00 2001 +From 119ec213b8f9a9e55ca340dbde10b0d19becab41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 5 Dec 2019 14:12:47 +0100 -Subject: [PATCH 1/2] perf-helper: Add content for custom drawing +Subject: [PATCH 1/4] perf-helper: Add content for custom drawing Drawing windows got a lot more involved with the advent of client-side decorations. Instead of accounting for visible and invisible borders, @@ -14,10 +14,10 @@ https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/shell-perf-helper.c b/src/shell-perf-helper.c -index f115dcbdc..ba1754e02 100644 +index e5eab208b..55bdbef02 100644 --- a/src/shell-perf-helper.c +++ b/src/shell-perf-helper.c -@@ -119,9 +119,9 @@ on_window_map_event (GtkWidget *window, +@@ -120,9 +120,9 @@ on_window_map_event (GtkWidget *window, } static gboolean @@ -30,7 +30,7 @@ index f115dcbdc..ba1754e02 100644 { cairo_rectangle_int_t allocation; double x_offset, y_offset; -@@ -203,6 +203,7 @@ create_window (int width, +@@ -204,6 +204,7 @@ create_window (int width, gboolean redraws) { WindowInfo *info; @@ -38,7 +38,7 @@ index f115dcbdc..ba1754e02 100644 info = g_new0 (WindowInfo, 1); info->width = width; -@@ -218,10 +219,13 @@ create_window (int width, +@@ -219,10 +220,13 @@ create_window (int width, info->pending = TRUE; info->start_time = -1; @@ -54,13 +54,13 @@ index f115dcbdc..ba1754e02 100644 if (info->redraws) -- -2.23.0 +2.26.2 -From 0185c288c3e6b82b76f2a0704aab0e2ab20b75b8 Mon Sep 17 00:00:00 2001 +From bb4c2acaef4d8fdea50915030c221e1190f704a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 5 Dec 2019 13:29:38 +0100 -Subject: [PATCH 2/2] perf-helper: Remove unused atoms +Subject: [PATCH 2/4] perf-helper: Remove unused atoms Those aren't used for anything, but make the helper dependent on X11. @@ -70,7 +70,7 @@ https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887 1 file changed, 18 deletions(-) diff --git a/src/shell-perf-helper.c b/src/shell-perf-helper.c -index ba1754e02..a50376e2e 100644 +index 55bdbef02..d3280de96 100644 --- a/src/shell-perf-helper.c +++ b/src/shell-perf-helper.c @@ -12,7 +12,6 @@ @@ -94,7 +94,7 @@ index ba1754e02..a50376e2e 100644 static guint timeout_id; static GList *our_windows; static GList *wait_windows_invocations; -@@ -350,8 +343,6 @@ on_name_lost (GDBusConnection *connection, +@@ -351,8 +344,6 @@ on_name_lost (GDBusConnection *connection, int main (int argc, char **argv) { @@ -103,7 +103,7 @@ index ba1754e02..a50376e2e 100644 GOptionContext *context; GError *error = NULL; -@@ -367,15 +358,6 @@ main (int argc, char **argv) +@@ -368,15 +359,6 @@ main (int argc, char **argv) return 1; } @@ -120,5 +120,280 @@ index ba1754e02..a50376e2e 100644 BUS_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | -- -2.23.0 +2.26.2 + + +From d8b4d72b89340dab46bdcb92ee54bde18dbb9ba9 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Fri, 24 Jan 2020 10:59:31 +0100 +Subject: [PATCH 3/4] perf-tool: Spawn perf-tool-helper from gnome-shell + +On Wayland, the display server is the Wayland compositor, i.e. +`gnome-shell` itself. + +As a result, we cannot spawn `gnome-shell-perf-helper` before +`gnome-shell` is started, as `gnome-shell-perf-helper` needs to connect +to the display server. + +So, instead of spawning `gnome-shell-perf-helper` from the perf tool, +start it from `gnome-shell` itself. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/941 +--- + js/ui/scripting.js | 51 ++++++++++++++++++++++------------ + src/gnome-shell-perf-tool.in | 53 ------------------------------------ + 2 files changed, 34 insertions(+), 70 deletions(-) + +diff --git a/js/ui/scripting.js b/js/ui/scripting.js +index d227b9ef4..eef8f3887 100644 +--- a/js/ui/scripting.js ++++ b/js/ui/scripting.js +@@ -3,8 +3,10 @@ + const { Gio, GLib, Meta, Shell } = imports.gi; + const Mainloop = imports.mainloop; + ++const Config = imports.misc.config; + const Main = imports.ui.main; + const Params = imports.misc.params; ++const Util = imports.misc.util; + + const { loadInterfaceXML } = imports.misc.fileUtils; + +@@ -73,6 +75,12 @@ function _getPerfHelper() { + return _perfHelper; + } + ++function _spawnPerfHelper() { ++ let path = Config.LIBEXECDIR; ++ let command = `${path}/gnome-shell-perf-helper`; ++ Util.trySpawnCommandLine(command); ++} ++ + function _callRemote(obj, method, ...args) { + return new Promise((resolve, reject) => { + args.push((result, excp) => { +@@ -270,6 +278,25 @@ function _collect(scriptModule, outputFile) { + } + } + ++async function _runPerfScript(scriptModule, outputFile) { ++ for (let step of scriptModule.run()) { ++ try { ++ await step; // eslint-disable-line no-await-in-loop ++ } catch (err) { ++ log(`Script failed: ${err}\n${err.stack}`); ++ Meta.exit(Meta.ExitCode.ERROR); ++ } ++ } ++ ++ try { ++ _collect(scriptModule, outputFile); ++ } catch (err) { ++ log(`Script failed: ${err}\n${err.stack}`); ++ Meta.exit(Meta.ExitCode.ERROR); ++ } ++ Meta.exit(Meta.ExitCode.SUCCESS); ++} ++ + /** + * runPerfScript + * @scriptModule: module object with run and finish functions +@@ -310,23 +337,13 @@ function _collect(scriptModule, outputFile) { + * After running the script and collecting statistics from the + * event log, GNOME Shell will exit. + **/ +-async function runPerfScript(scriptModule, outputFile) { ++function runPerfScript(scriptModule, outputFile) { + Shell.PerfLog.get_default().set_enabled(true); ++ _spawnPerfHelper(); + +- for (let step of scriptModule.run()) { +- try { +- await step; +- } catch (err) { +- log(`Script failed: ${err}\n${err.stack}`); +- Meta.exit(Meta.ExitCode.ERROR); +- } +- } +- +- try { +- _collect(scriptModule, outputFile); +- } catch (err) { +- log(`Script failed: ${err}\n${err.stack}`); +- Meta.exit(Meta.ExitCode.ERROR); +- } +- Meta.exit(Meta.ExitCode.SUCCESS); ++ Gio.bus_watch_name(Gio.BusType.SESSION, ++ 'org.gnome.Shell.PerfHelper', ++ Gio.BusNameWatcherFlags.NONE, ++ () => _runPerfScript(scriptModule, outputFile), ++ null); + } +diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in +index f4b48f730..050c66b30 100755 +--- a/src/gnome-shell-perf-tool.in ++++ b/src/gnome-shell-perf-tool.in +@@ -24,52 +24,6 @@ def show_version(option, opt_str, value, parser): + print("GNOME Shell Performance Test @VERSION@") + sys.exit() + +-def wait_for_dbus_name(wait_name): +- loop = GLib.MainLoop() +- +- def on_name_appeared(connection, name, new_owner, *args): +- if not (name == wait_name and new_owner != ''): +- return +- loop.quit() +- return +- +- watch_id = Gio.bus_watch_name(Gio.BusType.SESSION, +- wait_name, +- Gio.BusNameWatcherFlags.NONE, +- on_name_appeared, +- None) +- +- def on_timeout(): +- print("\nFailed to start %s: timed out" % (wait_name,)) +- sys.exit(1) +- GLib.timeout_add_seconds(7, on_timeout) +- +- loop.run() +- Gio.bus_unwatch_name(watch_id) +- +-PERF_HELPER_NAME = "org.gnome.Shell.PerfHelper" +-PERF_HELPER_IFACE = "org.gnome.Shell.PerfHelper" +-PERF_HELPER_PATH = "/org/gnome/Shell/PerfHelper" +- +-def start_perf_helper(): +- self_dir = os.path.dirname(os.path.abspath(sys.argv[0])) +- perf_helper_path = "@libexecdir@/gnome-shell-perf-helper" +- +- subprocess.Popen([perf_helper_path]) +- wait_for_dbus_name (PERF_HELPER_NAME) +- +-def stop_perf_helper(): +- bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) +- +- proxy = Gio.DBusProxy.new_sync(bus, +- Gio.DBusProxyFlags.NONE, +- None, +- PERF_HELPER_NAME, +- PERF_HELPER_PATH, +- PERF_HELPER_IFACE, +- None) +- proxy.Exit() +- + def start_shell(perf_output=None): + # Set up environment + env = dict(os.environ) +@@ -204,8 +158,6 @@ def run_performance_test(): + logs = [] + metric_summaries = {} + +- start_perf_helper() +- + for i in range(0, iters): + # We create an empty temporary file that the shell will overwrite + # with the contents. +@@ -217,14 +169,12 @@ def run_performance_test(): + try: + normal_exit = run_shell(perf_output=output_file) + except: +- stop_perf_helper() + raise + finally: + if not normal_exit: + os.remove(output_file) + + if not normal_exit: +- stop_perf_helper() + return False + + try: +@@ -232,7 +182,6 @@ def run_performance_test(): + output = json.load(f) + f.close() + except: +- stop_perf_helper() + raise + finally: + os.remove(output_file) +@@ -260,8 +209,6 @@ def run_performance_test(): + + logs.append(output['log']) + +- stop_perf_helper() +- + if options.perf_output or options.perf_upload: + # Write a complete report, formatted as JSON. The Javascript/C code that + # generates the individual reports we are summarizing here is very careful +-- +2.26.2 + + +From 8090db0f29dc72e602be341d43b3113373404b21 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 21 Jan 2020 11:05:58 +0100 +Subject: [PATCH 4/4] perf-tool: Allow to run as a Wayland compositor + +`gnome-shell-perf-tool` is initially designed to run on X11, using the +`--replace` option which does not work when gnome-shell is a Wayland +compositor. + +A solution would be to run `gnome-shell-perf-tool` in place of just +`gnome-shell` to run the entire perf session under Wayland, but the +script `gnome-shell-perf-tool` does not spawn `gnome-shell` as a Wayladn +compositor, so that fails as well. + +Add a `--wayland` option to `gnome-shell-perf-tool` so that it can +optionally spawn gnome-shell as a Wayland compositor so the whole perf +tool can be starred from a console with: + +``` + $ dbus-run-session -- gnome-shell-perf-tool --wayland +``` + +Alternatively, for testing purposes, it can also be started nested with: + +``` + $ dbus-run-session -- gnome-shell-perf-tool --nested +``` + +Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/2139 +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/941 +--- + src/gnome-shell-perf-tool.in | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in +index 050c66b30..04072c4cd 100755 +--- a/src/gnome-shell-perf-tool.in ++++ b/src/gnome-shell-perf-tool.in +@@ -45,6 +45,13 @@ def start_shell(perf_output=None): + if options.replace: + args.append('--replace') + ++ if options.wayland or options.nested: ++ args.append('--wayland') ++ if options.nested: ++ args.append('--nested') ++ else: ++ args.append('--display-server') ++ + return subprocess.Popen(args, env=env) + + def run_shell(perf_output=None): +@@ -284,6 +291,10 @@ parser.add_option("", "--version", action="callback", callback=show_version, + + parser.add_option("-r", "--replace", action="store_true", + help="Replace the running window manager") ++parser.add_option("-w", "--wayland", action="store_true", ++ help="Run as a Wayland compositor") ++parser.add_option("-n", "--nested", action="store_true", ++ help="Run as a Wayland nested compositor") + + options, args = parser.parse_args() + +-- +2.26.2 diff --git a/SPECS/gnome-shell.spec b/SPECS/gnome-shell.spec index 2385263..3687878 100644 --- a/SPECS/gnome-shell.spec +++ b/SPECS/gnome-shell.spec @@ -1,6 +1,6 @@ Name: gnome-shell Version: 3.32.2 -Release: 13%{?dist} +Release: 19%{?dist} Summary: Window management and application launching for GNOME Group: User Interface/Desktops @@ -43,9 +43,21 @@ Patch45: 0001-workspace-Pass-device-to-startDrag.patch Patch46: 0001-a11y-Change-HC-icon-theme-first.patch Patch47: perf-tool-wayland.patch Patch48: 0001-padOsd-Re-query-action-labels-after-mode-switches.patch +Patch49: 0001-Do-not-change-Wacom-LEDs-through-g-s-d.patch +Patch50: 0001-st-texture-cache-Cancel-pending-requests-on-icon-the.patch +Patch51: introspect-backports.patch # Backport JS invalid access warnings (#1651894, #1663171, #1642482, #1637622) Patch54: fix-invalid-access-warnings.patch +Patch55: more-spurious-allocation-warnings.patch + +# Backport performance fixes under load (#1820760) +Patch60: 0001-environment-reduce-calls-to-g_time_zone_new_local.patch +Patch61: 0002-environment-Fix-date-conversion.patch +Patch62: 0003-shell-app-system-Monitor-for-icon-theme-changes.patch +Patch63: 0004-global-force-fsync-to-worker-thread-when-saving-stat.patch +Patch64: 0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch +Patch65: 0006-js-Always-use-AppSystem-to-lookup-apps.patch # suspend/resume fix on nvidia (#1663440) Patch10001: 0001-background-refresh-after-suspend-on-wayland.patch @@ -226,6 +238,36 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/evolution-calendar.de %{_mandir}/man1/%{name}.1.gz %changelog +* Mon Jun 15 2020 Florian Müllner - 3.32.2-19 +- Fix last backport + Resolves: #1847051 + +* Sun Jun 14 2020 Florian Müllner - 3.32.2-18 +- Fix more spurious allocation warnings + Resolves: #1715845 + +* Fri May 22 2020 Florian Müllner - 3.32.2-17 +- Really allow using perf-tool on wayland + Resolves: #1652178 +- Fix timed login without user list + Resolves: #1668895 +- Fix HighContrast/symbolic icon mixup + Resolves: #1794045 +- Backport introspect API changes + Resolves: #1837413 + +* Fri Apr 17 2020 Florian Müllner - 3.32.2-16 +- Drop bad upstream patch + Resolves: #1820760 + +* Wed Apr 08 2020 Florian Müllner - 3.32.2-15 +- Improve performance under load + Resolves: #1820760 + +* Wed Mar 04 2020 Carlos Garnacho - 3.32.2-14 +- Do not set Wacom LEDs through gnome-settings-daemon, rely on kernel driver + Resolves: #1687979 + * Mon Dec 16 2019 Carlos Garnacho - 3.32.2-13 - Update pad OSD on mode switching Resolves: #1716774