From 067a6b7e13f65c152496f12f531ee12403142a03 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 20 2020 10:09:22 +0000 Subject: import gnome-shell-3.28.3-25.el7 --- diff --git a/SOURCES/0001-ShellApp-Use-g_signal_connect_object-for-window-sign.patch b/SOURCES/0001-ShellApp-Use-g_signal_connect_object-for-window-sign.patch new file mode 100644 index 0000000..b507053 --- /dev/null +++ b/SOURCES/0001-ShellApp-Use-g_signal_connect_object-for-window-sign.patch @@ -0,0 +1,48 @@ +From ce5d15f2365edcadb9bbf58969fe6c7172f37e26 Mon Sep 17 00:00:00 2001 +From: Simon McVittie +Date: Tue, 16 Apr 2019 23:59:58 +0100 +Subject: [PATCH] ShellApp: Use g_signal_connect_object for window signals + +A window being unmanaged can cause the ShellApp to be removed from +the ShellAppSystem, which if we are unlucky is the app's last +reference, causing it to be disposed and freed. It would be bad if this +happened before we finished handling the signal. + +Use g_signal_connect_object to ensure that a reference is held to +the ShellApp for the duration of the signal handler, delaying its +last-unref. + +In particular, when a signal handler calls _shell_app_remove_window(), +there is a brief period for which ShellApp breaks the intended +invariant (see !497) that app->running_state is non-NULL if and only if +app->running_state->windows is also non-NULL (non-empty). Freeing the +ShellApp at this point would cause a crash. This seems likely to be the +root cause of , + and +. + +Signed-off-by: Simon McVittie +--- + src/shell-app.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/shell-app.c b/src/shell-app.c +index 84fcc97bf..64f318229 100644 +--- a/src/shell-app.c ++++ b/src/shell-app.c +@@ -1034,9 +1034,9 @@ _shell_app_add_window (ShellApp *app, + + app->running_state->window_sort_stale = TRUE; + app->running_state->windows = g_slist_prepend (app->running_state->windows, g_object_ref (window)); +- g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app); +- g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app); +- g_signal_connect (window, "notify::skip-taskbar", G_CALLBACK(shell_app_on_skip_taskbar_changed), app); ++ g_signal_connect_object (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app, 0); ++ g_signal_connect_object (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app, 0); ++ g_signal_connect_object (window, "notify::skip-taskbar", G_CALLBACK(shell_app_on_skip_taskbar_changed), app, 0); + + shell_app_update_app_menu (app, window); + shell_app_ensure_busy_watch (app); +-- +2.23.0 + 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..df79249 --- /dev/null +++ b/SOURCES/0001-environment-reduce-calls-to-g_time_zone_new_local.patch @@ -0,0 +1,65 @@ +From dd03c3a2fe0c87dc1823aad578f95e972aba50a9 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 719680f61..78a24f888 100644 +--- a/js/ui/environment.js ++++ b/js/ui/environment.js +@@ -17,6 +17,9 @@ const Gtk = imports.gi.Gtk; + const Lang = imports.lang; + const Shell = imports.gi.Shell; + const St = imports.gi.St; ++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 +@@ -116,9 +119,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.0 + diff --git a/SOURCES/0001-windowManager-ensure-actor-resize-clone-dies-with-ac.patch b/SOURCES/0001-windowManager-ensure-actor-resize-clone-dies-with-ac.patch new file mode 100644 index 0000000..8ad7396 --- /dev/null +++ b/SOURCES/0001-windowManager-ensure-actor-resize-clone-dies-with-ac.patch @@ -0,0 +1,47 @@ +From ce368a83a782931c56239daa99e3dc96d1de41e2 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 7 Feb 2019 14:27:35 -0500 +Subject: [PATCH] windowManager: ensure actor resize clone dies with actor + +If a window gets destroyed right before it's resize +animation starts the user can get confronted with an undead +zombie clone that doesn't go away. + +This commit makes sure said clones get reaped with their +actors. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/1166 +--- + js/ui/windowManager.js | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js +index b439b3c0a..3fda973f1 100644 +--- a/js/ui/windowManager.js ++++ b/js/ui/windowManager.js +@@ -1318,8 +1318,13 @@ var WindowManager = new Lang.Class({ + if (this._clearAnimationInfo(actor)) + this._shellwm.completed_size_change(actor); + ++ let destroyId = actor.connect('destroy', () => { ++ this._clearAnimationInfo(actor); ++ }); ++ + actor.__animationInfo = { clone: actorClone, +- oldRect: oldFrameRect }; ++ oldRect: oldFrameRect, ++ destroyId: destroyId }; + }, + + _sizeChangedWindow(shellwm, actor) { +@@ -1380,6 +1385,7 @@ var WindowManager = new Lang.Class({ + _clearAnimationInfo(actor) { + if (actor.__animationInfo) { + actor.__animationInfo.clone.destroy(); ++ actor.disconnect(actor.__animationInfo.destroyId); + delete actor.__animationInfo; + return true; + } +-- +2.23.0 + diff --git a/SOURCES/0002-environment-Fix-date-conversion.patch b/SOURCES/0002-environment-Fix-date-conversion.patch new file mode 100644 index 0000000..018dbd2 --- /dev/null +++ b/SOURCES/0002-environment-Fix-date-conversion.patch @@ -0,0 +1,33 @@ +From dacc190881bceaa8d553ab0486316f2d8e21d224 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 78a24f888..c65c05f08 100644 +--- a/js/ui/environment.js ++++ b/js/ui/environment.js +@@ -132,7 +132,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.0 + 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..1b180fc --- /dev/null +++ b/SOURCES/0003-shell-app-system-Monitor-for-icon-theme-changes.patch @@ -0,0 +1,152 @@ +From b3bdc04362e4a147a907b8b31f03ff02d0807c08 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 58e0185d1..61ca1c35c 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 0c794a3ab..ab6d494b7 100644 +--- a/src/st/st-texture-cache.c ++++ b/src/st/st-texture-cache.c +@@ -1425,3 +1425,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 26f9c30ac..606694c86 100644 +--- a/src/st/st-texture-cache.h ++++ b/src/st/st-texture-cache.h +@@ -106,4 +106,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.0 + diff --git a/SOURCES/0003-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch b/SOURCES/0003-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch index bfb26f4..19e68c1 100644 --- a/SOURCES/0003-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch +++ b/SOURCES/0003-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch @@ -1,4 +1,4 @@ -From fa6756e4e3c6e921ec643d749a6973ad766af2d5 Mon Sep 17 00:00:00 2001 +From 9b861aa428c21c59e91d5b5a629a5308eb1d5246 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 21 Aug 2019 15:06:46 -0400 Subject: [PATCH 3/3] shellEntry: Support lockdown of "Show Text" menu in @@ -9,14 +9,14 @@ the password they're currently typing. This commit adds support for that kind of lockdown. --- - js/ui/shellEntry.js | 32 ++++++++++++++++++++++++++------ - 1 file changed, 26 insertions(+), 6 deletions(-) + js/ui/shellEntry.js | 33 +++++++++++++++++++++++++++------ + 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js -index 5ce0a40a0..72e2fc33b 100644 +index 5ce0a40a0..f0ee1d9e0 100644 --- a/js/ui/shellEntry.js +++ b/js/ui/shellEntry.js -@@ -1,94 +1,114 @@ +@@ -1,94 +1,115 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Clutter = imports.gi.Clutter; @@ -80,7 +80,8 @@ index 5ce0a40a0..72e2fc33b 100644 + this._passwordItem.destroy(); + this._passwordItem = null; + } -+ this._entry.clutter_text.set_password_char('\u25cf'); ++ if (this.isPassword) ++ this._entry.clutter_text.set_password_char('\u25cf'); + } else if (this.isPassword && !passwordDisabled) { + if (!this._passwordItem) + this._makePasswordItem(); 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..ba9d206 --- /dev/null +++ b/SOURCES/0004-global-force-fsync-to-worker-thread-when-saving-stat.patch @@ -0,0 +1,124 @@ +From 6155789a63258865d65df4bf6abb4c83a22e58a8 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 8f5418677..3e4864555 100644 +--- a/src/shell-global.c ++++ b/src/shell-global.c +@@ -1804,6 +1804,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, +@@ -1812,7 +1861,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)) + { +@@ -1848,12 +1897,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.0 + 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..2c6272b --- /dev/null +++ b/SOURCES/0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch @@ -0,0 +1,674 @@ +From 0b96b08045f0a10a2f835a9fc5e184e3a66de8bd 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 233deeb92..3798fac57 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -93,15 +93,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 1d655f407..568ee0c9a 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -129,6 +129,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', +@@ -170,7 +171,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 61ca1c35c..bb9163a18 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; + } + +@@ -507,7 +505,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 70b8c0611..454058a05 100644 +--- a/src/shell-util.c ++++ b/src/shell-util.c +@@ -8,6 +8,7 @@ + #include + #include + ++#include "shell-app-cache-private.h" + #include "shell-util.h" + #include + #include +@@ -514,3 +515,18 @@ shell_util_composite_capture_images (ClutterCapture *captures, + + return image; + } ++ ++/** ++ * 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 2218594c1..4e30f3730 100644 +--- a/src/shell-util.h ++++ b/src/shell-util.h +@@ -60,6 +60,8 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures + int width, + int height); + ++char *shell_util_get_translated_folder_name (const char *name); ++ + G_END_DECLS + + #endif /* __SHELL_UTIL_H__ */ +-- +2.26.0 + 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..78060b0 --- /dev/null +++ b/SOURCES/0006-js-Always-use-AppSystem-to-lookup-apps.patch @@ -0,0 +1,66 @@ +From be4a943a4974a9c37fabbfd4afd0f698b7d6f068 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 3798fac57..76d05efc9 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -1111,8 +1111,8 @@ var AppSearchProvider = new Lang.Class({ + 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 651aac610..0b2c61bf5 100644 +--- a/js/ui/calendar.js ++++ b/js/ui/calendar.js +@@ -834,8 +834,9 @@ var EventsSection = new Lang.Class({ + 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(); + }, + +@@ -933,10 +934,13 @@ var EventsSection = new Lang.Class({ + 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.0 + diff --git a/SOURCES/fix-partial-lock-bypass.patch b/SOURCES/fix-partial-lock-bypass.patch new file mode 100644 index 0000000..f937bc7 --- /dev/null +++ b/SOURCES/fix-partial-lock-bypass.patch @@ -0,0 +1,132 @@ +From d90d7e22143949d59880981fe53adcfad27a5fd3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 23 Jan 2019 23:55:12 +0100 +Subject: [PATCH 1/2] panel: Don't allow opening hidden menus via keybindings + +We shouldn't allow toggling menus that aren't supported by the +current session mode, but as indicators are hidden rather than +destroyed on mode switches, it is not enough to check for an +indicator's existence. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/851 +--- + js/ui/panel.js | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/js/ui/panel.js b/js/ui/panel.js +index 2f593247d..02667f92f 100644 +--- a/js/ui/panel.js ++++ b/js/ui/panel.js +@@ -985,8 +985,8 @@ var Panel = new Lang.Class({ + }, + + _toggleMenu(indicator) { +- if (!indicator) // menu not supported by current session mode +- return; ++ if (!indicator || !indicator.container.visible) ++ return; // menu not supported by current session mode + + let menu = indicator.menu; + if (!indicator.actor.reactive) +-- +2.23.0 + + +From 5083ad899c976f7221848500fc9d4bb393a66327 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 23 Jan 2019 15:59:42 -0500 +Subject: [PATCH 2/2] shellActionModes: disable POPUP keybindings in unlock + screen + +Certain keybindings should continue to work even when a popup +menu is on screen. For instance, the keybinding for showing +the app menu and the keyinding for showing the calendar are +examples. + +This is achieved by putting in place a special "POPUP" action +mode, whenever a popup menu is active. This mode replaces +the (e.g., "NORMAL" or "OVERVIEW") action mode that was in place +for as long as the popup menu is active. + +But those keybindings should not work when the user is at the +unlock dialog (which uses an action mode of "UNLOCK"). + +Unfortunately, since commit c79d24b6 they do. + +This commit addresses the problem by forcing the action mode +to NONE at the unlock screen when popups are visible. + +CVE-2019-3820 + +Closes https://gitlab.gnome.org/GNOME/gnome-shell/issues/851 +--- + js/gdm/authPrompt.js | 3 ++- + js/gdm/loginDialog.js | 3 ++- + js/ui/shellEntry.js | 6 ++++-- + 3 files changed, 8 insertions(+), 4 deletions(-) + +diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js +index 27a55246a..15d3273fa 100644 +--- a/js/gdm/authPrompt.js ++++ b/js/gdm/authPrompt.js +@@ -14,6 +14,7 @@ const Batch = imports.gdm.batch; + const GdmUtil = imports.gdm.util; + const Meta = imports.gi.Meta; + const Params = imports.misc.params; ++const Shell = imports.gi.Shell; + const ShellEntry = imports.ui.shellEntry; + const Tweener = imports.ui.tweener; + const UserWidget = imports.ui.userWidget; +@@ -110,7 +111,7 @@ var AuthPrompt = new Lang.Class({ + x_align: St.Align.START }); + this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry', + can_focus: true }); +- ShellEntry.addContextMenu(this._entry, { isPassword: true }); ++ ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE }); + + this.actor.add(this._entry, + { expand: true, +diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js +index 912c0e0ca..141ed9265 100644 +--- a/js/gdm/loginDialog.js ++++ b/js/gdm/loginDialog.js +@@ -338,7 +338,8 @@ var SessionMenuButton = new Lang.Class({ + this._button.remove_style_pseudo_class('active'); + }); + +- this._manager = new PopupMenu.PopupMenuManager({ actor: this._button }); ++ this._manager = new PopupMenu.PopupMenuManager({ actor: this._button }, ++ { actionMode: Shell.ActionMode.NONE }); + this._manager.addMenu(this._menu); + + this._button.connect('clicked', () => { this._menu.toggle(); }); +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index 72e2fc33b..6d46a0997 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -10,6 +10,7 @@ const BoxPointer = imports.ui.boxpointer; + const Main = imports.ui.main; + const Params = imports.misc.params; + const PopupMenu = imports.ui.popupMenu; ++const Shell = imports.gi.Shell; + + const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; + const DISABLE_SHOW_PASSWORD_KEY = 'disable-show-password'; +@@ -171,11 +172,12 @@ function addContextMenu(entry, params) { + if (entry.menu) + return; + +- params = Params.parse (params, { isPassword: false }); ++ params = Params.parse (params, { isPassword: false, actionMode: Shell.ActionMode.POPUP }); + + entry.menu = new EntryMenu(entry); + entry.menu.isPassword = params.isPassword; +- entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }); ++ entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry }, ++ { actionMode: params.actionMode }); + entry._menuManager.addMenu(entry.menu); + + // Add an event handler to both the entry and its clutter_text; the former +-- +2.23.0 + diff --git a/SOURCES/shell-app-performance-backports.patch b/SOURCES/shell-app-performance-backports.patch new file mode 100644 index 0000000..87a5775 --- /dev/null +++ b/SOURCES/shell-app-performance-backports.patch @@ -0,0 +1,360 @@ +From b593bf334a7aada890d8ec4ce358c9f431b605ff Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 3 Dec 2018 13:09:47 +0100 +Subject: [PATCH 1/4] shell-global: Make saving of persistent state + asynchronous + +This is an expensive operation that is best avoided in the main loop. Given +the call doesn't care much about returning error or status, it can just +be made async within. + +Every operation on a given file will be destructive wrt previous +operations on the same file, so we just cancel any pending operation on +it before batching the current one. + +Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/815 +--- + src/shell-global.c | 85 ++++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 75 insertions(+), 10 deletions(-) + +diff --git a/src/shell-global.c b/src/shell-global.c +index 961fd3a70..8f5418677 100644 +--- a/src/shell-global.c ++++ b/src/shell-global.c +@@ -88,6 +88,8 @@ struct _ShellGlobal { + /* For sound notifications */ + ca_context *sound_context; + ++ GHashTable *save_ops; ++ + gboolean has_modal; + gboolean frame_timestamps; + gboolean frame_finish_timestamp; +@@ -331,6 +333,10 @@ shell_global_init (ShellGlobal *global) + NULL); + + g_strfreev (search_path); ++ ++ global->save_ops = g_hash_table_new_full (g_file_hash, ++ (GEqualFunc) g_file_equal, ++ g_object_unref, g_object_unref); + } + + static void +@@ -350,6 +356,8 @@ shell_global_finalize (GObject *object) + g_free (global->imagedir); + g_free (global->userdatadir); + ++ g_hash_table_unref (global->save_ops); ++ + G_OBJECT_CLASS(shell_global_parent_class)->finalize (object); + } + +@@ -1775,20 +1783,77 @@ shell_global_get_session_mode (ShellGlobal *global) + } + + static void +-save_variant (GFile *dir, +- const char *property_name, +- GVariant *variant) ++delete_variant_cb (GObject *object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ ShellGlobal *global = user_data; ++ GError *error = NULL; ++ ++ if (!g_file_delete_finish (G_FILE (object), result, &error)) ++ { ++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ { ++ g_warning ("Could not delete runtime/persistent state file: %s\n", ++ error->message); ++ } ++ ++ g_error_free (error); ++ } ++ ++ g_hash_table_remove (global->save_ops, object); ++} ++ ++static void ++replace_variant_cb (GObject *object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ ShellGlobal *global = user_data; ++ GError *error = NULL; ++ ++ if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error)) ++ { ++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ { ++ g_warning ("Could not replace runtime/persistent state file: %s\n", ++ error->message); ++ } ++ ++ g_error_free (error); ++ } ++ ++ g_hash_table_remove (global->save_ops, object); ++} ++ ++static void ++save_variant (ShellGlobal *global, ++ GFile *dir, ++ const char *property_name, ++ GVariant *variant) + { + GFile *path = g_file_get_child (dir, property_name); ++ GCancellable *cancellable; ++ ++ cancellable = g_hash_table_lookup (global->save_ops, path); ++ g_cancellable_cancel (cancellable); ++ ++ cancellable = g_cancellable_new (); ++ g_hash_table_insert (global->save_ops, g_object_ref (path), cancellable); + + if (variant == NULL || g_variant_get_data (variant) == NULL) +- (void) g_file_delete (path, NULL, NULL); ++ { ++ g_file_delete_async (path, G_PRIORITY_DEFAULT, cancellable, ++ delete_variant_cb, global); ++ } + else + { +- gsize size = g_variant_get_size (variant); +- g_file_replace_contents (path, g_variant_get_data (variant), size, +- NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, +- NULL, NULL, NULL); ++ 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_object_unref (path); +@@ -1842,7 +1907,7 @@ shell_global_set_runtime_state (ShellGlobal *global, + const char *property_name, + GVariant *variant) + { +- save_variant (global->runtime_state_path, property_name, variant); ++ save_variant (global, global->runtime_state_path, property_name, variant); + } + + /** +@@ -1877,7 +1942,7 @@ shell_global_set_persistent_state (ShellGlobal *global, + const char *property_name, + GVariant *variant) + { +- save_variant (global->userdatadir_path, property_name, variant); ++ save_variant (global, global->userdatadir_path, property_name, variant); + } + + /** +-- +2.23.0 + + +From 6e898656365c18ad48349b7e3586e2d10b9ead9a Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Mon, 3 Dec 2018 13:18:19 +0100 +Subject: [PATCH 2/4] appDisplay: Reduce g_app_info_get_all() calls + +Whenever the AllView needs (re)populating, we used to do one general +g_app_info_get_all() to get all GAppInfo, plus one per app folder in order +to check the ones that fall within that category. This calls results in a +fair amount of I/O blocking the main loop. + +In order to ease this, keep the GAppInfo list around in AllView, and make +the AppFolders use it when figuring out the contained apps. Since reloading +the AllView results in AppFolders regenerated from scratch, the app info +list is ensured to be up-to-date for any later change within the AppFolder +(eg. through the GSettings key changing). + +As the list was already filtered in the first place, we can also remove +the try{}catch() in AppFolder in order to discard desktop files with +invalid encoding. + +Related: https://gitlab.gnome.org/GNOME/gnome-shell/issues/832 +--- + js/ui/appDisplay.js | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js +index 070057c69..115eb8f92 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -498,15 +498,21 @@ var AllView = new Lang.Class({ + }); + }, + ++ getAppInfos() { ++ return this._appInfoList; ++ }, ++ + _loadApps() { +- let apps = Gio.AppInfo.get_all().filter(appInfo => { ++ this._appInfoList = Gio.AppInfo.get_all().filter(appInfo => { + try { + let id = appInfo.get_id(); // catch invalid file encodings + } catch(e) { + return false; + } + return appInfo.should_show(); +- }).map(app => app.get_id()); ++ }); ++ ++ let apps = this._appInfoList.map(app => app.get_id()); + + let appSys = Shell.AppSystem.get_default(); + +@@ -1329,15 +1335,13 @@ var FolderIcon = new Lang.Class({ + folderApps.forEach(addAppId); + + let folderCategories = this._folder.get_strv('categories'); +- Gio.AppInfo.get_all().forEach(appInfo => { ++ let appInfos = this._parentView.getAppInfos(); ++ appInfos.forEach(appInfo => { + let appCategories = _getCategories(appInfo); + if (!_listsIntersect(folderCategories, appCategories)) + return; + +- try { +- addAppId(appInfo.get_id()); // catch invalid file encodings +- } catch(e) { +- } ++ addAppId(appInfo.get_id()); + }); + + this.actor.visible = this.view.getAllItems().length > 0; +-- +2.23.0 + + +From cf823de60bff23872d20dfbb05ee4c51e3ebe9a3 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 19 Dec 2018 13:13:42 +0100 +Subject: [PATCH 3/4] shell-app-system: Cache GAppInfos around + +This was called here just to end up emitting ::installed-changed, +which would trigger other g_app_info_get_all() calls. Cache it here +so it may be reused later on. +--- + src/shell-app-system.c | 30 +++++++++++++++++++++++++----- + src/shell-app-system.h | 2 ++ + 2 files changed, 27 insertions(+), 5 deletions(-) + +diff --git a/src/shell-app-system.c b/src/shell-app-system.c +index 8edf757a9..58e0185d1 100644 +--- a/src/shell-app-system.c ++++ b/src/shell-app-system.c +@@ -50,6 +50,7 @@ struct _ShellAppSystemPrivate { + GHashTable *running_apps; + GHashTable *id_to_app; + GHashTable *startup_wm_class_to_id; ++ GList *installed_apps; + }; + + static void shell_app_system_finalize (GObject *object); +@@ -82,12 +83,14 @@ static void + scan_startup_wm_class_to_id (ShellAppSystem *self) + { + ShellAppSystemPrivate *priv = self->priv; +- GList *apps, *l; ++ GList *l; + + g_hash_table_remove_all (priv->startup_wm_class_to_id); + +- apps = g_app_info_get_all (); +- for (l = apps; l != NULL; l = l->next) ++ g_list_free_full (priv->installed_apps, g_object_unref); ++ priv->installed_apps = g_app_info_get_all (); ++ ++ for (l = priv->installed_apps; l != NULL; l = l->next) + { + GAppInfo *info = l->data; + const char *startup_wm_class, *id, *old_id; +@@ -105,8 +108,6 @@ scan_startup_wm_class_to_id (ShellAppSystem *self) + g_hash_table_insert (priv->startup_wm_class_to_id, + g_strdup (startup_wm_class), g_strdup (id)); + } +- +- g_list_free_full (apps, g_object_unref); + } + + static gboolean +@@ -198,6 +199,7 @@ shell_app_system_finalize (GObject *object) + g_hash_table_destroy (priv->running_apps); + 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_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object); + } +@@ -437,3 +439,21 @@ shell_app_system_search (const char *search_string) + + return results; + } ++ ++/** ++ * shell_app_system_get_installed: ++ * @self: the #ShellAppSystem ++ * ++ * Returns all installed apps, as a list of #GAppInfo ++ * ++ * Returns: (transfer none) (element-type GAppInfo): a list of #GAppInfo ++ * describing all known applications. This memory is owned by the ++ * #ShellAppSystem and should not be freed. ++ **/ ++GList * ++shell_app_system_get_installed (ShellAppSystem *self) ++{ ++ ShellAppSystemPrivate *priv = self->priv; ++ ++ return priv->installed_apps; ++} +diff --git a/src/shell-app-system.h b/src/shell-app-system.h +index 445671f5b..8719dbcf2 100644 +--- a/src/shell-app-system.h ++++ b/src/shell-app-system.h +@@ -27,4 +27,6 @@ ShellApp *shell_app_system_lookup_desktop_wmclass (ShellAppSystem *s + GSList *shell_app_system_get_running (ShellAppSystem *self); + char ***shell_app_system_search (const char *search_string); + ++GList *shell_app_system_get_installed (ShellAppSystem *self); ++ + #endif /* __SHELL_APP_SYSTEM_H__ */ +-- +2.23.0 + + +From e294793e3d5525897e1c293cd9fd77ccf8983ca7 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Wed, 19 Dec 2018 13:26:54 +0100 +Subject: [PATCH 4/4] appDisplay: Use GAppInfo list from ShellAppSystem + +It is now cached there, so the number of g_app_info_get_all() calls +is reduced to one per change. +--- + js/ui/appDisplay.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js +index 115eb8f92..233deeb92 100644 +--- a/js/ui/appDisplay.js ++++ b/js/ui/appDisplay.js +@@ -503,7 +503,7 @@ var AllView = new Lang.Class({ + }, + + _loadApps() { +- this._appInfoList = Gio.AppInfo.get_all().filter(appInfo => { ++ this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => { + try { + let id = appInfo.get_id(); // catch invalid file encodings + } catch(e) { +-- +2.23.0 + diff --git a/SPECS/gnome-shell.spec b/SPECS/gnome-shell.spec index a400de2..6503942 100644 --- a/SPECS/gnome-shell.spec +++ b/SPECS/gnome-shell.spec @@ -1,6 +1,6 @@ Name: gnome-shell Version: 3.28.3 -Release: 18%{?dist} +Release: 25%{?dist} Summary: Window management and application launching for GNOME Group: User Interface/Desktops @@ -36,6 +36,7 @@ Patch23: 0004-objectManager-Fix-index-mix-up.patch Patch24: 0001-shellEntry-Determine-if-password-entry-from-content-.patch Patch25: 0002-shellEntry-if-password-menu-item-a-label-when-it-s-c.patch Patch26: 0003-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch +Patch27: fix-partial-lock-bypass.patch # Misc. Patch30: 0001-shellDBus-Add-a-DBus-method-to-load-a-single-extensi.patch @@ -50,6 +51,7 @@ Patch39: 0001-appDisplay-Show-full-app-name-on-hover.patch Patch40: fix-close-dialog-annoyances.patch Patch41: startup-warnings.patch Patch42: horizontal-workspace-support.patch +Patch43: 0001-windowManager-ensure-actor-resize-clone-dies-with-ac.patch # Backported from upstream Patch50: 0001-keyboard-Handle-no-window-case-in-FocusTracker.patch @@ -68,6 +70,21 @@ Patch54: 0001-windowManager-listen-actively-to-windows-being-destr.patch # Handle rapid mouse input better (#1657887) Patch55: 0001-boxpointer-Ungrab-pointer-when-hiding.patch +# Fix crash when window removed (#1752547) +Patch56: 0001-ShellApp-Use-g_signal_connect_object-for-window-sign.patch + +# Performance backports (#1766501) +Patch57: shell-app-performance-backports.patch + +# IO performance backports (#1824871) +Patch58: 0001-environment-reduce-calls-to-g_time_zone_new_local.patch +Patch59: 0002-environment-Fix-date-conversion.patch +Patch60: 0003-shell-app-system-Monitor-for-icon-theme-changes.patch +Patch61: 0004-global-force-fsync-to-worker-thread-when-saving-stat.patch +Patch62: 0005-app-cache-add-ShellAppCache-for-GAppInfo-caching.patch +Patch63: 0006-js-Always-use-AppSystem-to-lookup-apps.patch + + %define libcroco_version 0.6.8 %define eds_version 3.17.2 %define gnome_desktop_version 3.7.90 @@ -281,6 +298,34 @@ glib-compile-schemas --allow-any-name %{_datadir}/glib-2.0/schemas &> /dev/null %{_mandir}/man1/%{name}.1.gz %changelog +* Fri Apr 17 2020 Florian Müllner - 3.28.3-25 +- Improve performance under load + Resolves: #1824871 + +* Tue Dec 03 2019 Florian Müllner - 3.28.3-24 +- Fix orphaned animation actors + Related: #1753799 + +* Thu Nov 21 2019 Ray Strode - 3.28.3-23 +- Fix "Not Listed?" entry to shows characters instead of bullets + Resolves: #1772896 + +* Wed Nov 20 2019 Florian Müllner - 3.28.3-22 +- Fix partial lock screen bypass + Resolves: #1669393 + +* Wed Oct 30 2019 Ray Strode - 3.28.3-21 +- Add missing comma neglected in last build + Related: #1766501 + +* Tue Oct 29 2019 Jonas Ådahl - 3.28.3-20 +- Performance backports + Resolves: #1766501 + +* Fri Oct 25 2019 Jonas Ådahl - 3.28.3-19 +- Fix crash when window removed + Resolves: #1752547 + * Wed Oct 02 2019 Jonas Ådahl - 3.28.3-18 - Change method for handling rapid mouse input better Resolves: #1657887