diff --git a/0001-app-Fall-back-to-window-title-instead-of-WM_CLASS.patch b/0001-app-Fall-back-to-window-title-instead-of-WM_CLASS.patch new file mode 100644 index 0000000..001e896 --- /dev/null +++ b/0001-app-Fall-back-to-window-title-instead-of-WM_CLASS.patch @@ -0,0 +1,28 @@ +From afa3fc7be62cf70c9f6c6954e9cf5b49581269fb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Wed, 20 May 2015 16:44:00 +0200 +Subject: [PATCH] app: Fall back to window title instead of WM_CLASS + +It's a bad fallback as it's clearly window-specific (rather than +app-specific), but it likely looks prettier when we fail to associate +a .desktop file ... +--- + src/shell-app.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shell-app.c b/src/shell-app.c +index 62ba2ec73..dc0e1c732 100644 +--- a/src/shell-app.c ++++ b/src/shell-app.c +@@ -293,7 +293,7 @@ shell_app_get_name (ShellApp *app) + const char *name = NULL; + + if (window) +- name = meta_window_get_wm_class (window); ++ name = meta_window_get_title (window); + if (!name) + name = C_("program", "Unknown"); + return name; +-- +2.31.1 + diff --git a/0001-extensionDownloader-Refuse-to-override-system-extens.patch b/0001-extensionDownloader-Refuse-to-override-system-extens.patch new file mode 100644 index 0000000..58a3ee8 --- /dev/null +++ b/0001-extensionDownloader-Refuse-to-override-system-extens.patch @@ -0,0 +1,36 @@ +From c18b7b7819f17f5d14be1ba2760653f3d93b81b1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 1 Feb 2021 18:26:00 +0100 +Subject: [PATCH] extensionDownloader: Refuse to override system extensions + +The website allows to "update" system extensions by installing the +upstream version into the user's home directory. + +Prevent that by refusing to download and install extensions that are +already installed system-wide. +--- + js/ui/extensionDownloader.js | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/js/ui/extensionDownloader.js b/js/ui/extensionDownloader.js +index 6a3b2b488..471ddab14 100644 +--- a/js/ui/extensionDownloader.js ++++ b/js/ui/extensionDownloader.js +@@ -17,6 +17,14 @@ var REPOSITORY_URL_UPDATE = 'https://extensions.gnome.org/update-info/'; + let _httpSession; + + function installExtension(uuid, invocation) { ++ const oldExt = Main.extensionManager.lookup(uuid); ++ if (oldExt && oldExt.type === ExtensionUtils.ExtensionType.SYSTEM) { ++ log('extensionDownloader: Trying to replace system extension %s'.format(uuid)); ++ invocation.return_dbus_error('org.gnome.Shell.InstallError', ++ 'System extensions cannot be replaced'); ++ return; ++ } ++ + let params = { uuid, + shell_version: Config.PACKAGE_VERSION }; + +-- +2.31.1 + diff --git a/0001-loginDialog-make-info-messages-themed.patch b/0001-loginDialog-make-info-messages-themed.patch new file mode 100644 index 0000000..859434c --- /dev/null +++ b/0001-loginDialog-make-info-messages-themed.patch @@ -0,0 +1,29 @@ +From cf4d6eac54e0614fe95a34a88c5c07283d49599b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 26 Jun 2017 14:35:05 -0400 +Subject: [PATCH] loginDialog: make info messages themed + +They were lacking a definition before leading them to +show up invisible. +--- + data/theme/gnome-shell-sass/widgets/_login-dialog.scss | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/data/theme/gnome-shell-sass/widgets/_login-dialog.scss b/data/theme/gnome-shell-sass/widgets/_login-dialog.scss +index 1789beca9..84539342d 100644 +--- a/data/theme/gnome-shell-sass/widgets/_login-dialog.scss ++++ b/data/theme/gnome-shell-sass/widgets/_login-dialog.scss +@@ -93,6 +93,10 @@ + .login-dialog-banner { color: darken($osd_fg_color,10%); } + .login-dialog-button-box { width: 23em; spacing: 5px; } + .login-dialog-message { text-align: center; } ++.login-dialog-message-hint, .login-dialog-message { ++ color: darken($osd_fg_color, 20%); ++ min-height: 2.75em; ++} + .login-dialog-user-selection-box { padding: 100px 0px; } + .login-dialog-not-listed-label { + padding-left: 2px; +-- +2.31.1 + diff --git a/0001-main-Dump-stack-on-segfaults-by-default.patch b/0001-main-Dump-stack-on-segfaults-by-default.patch new file mode 100644 index 0000000..57dc9f0 --- /dev/null +++ b/0001-main-Dump-stack-on-segfaults-by-default.patch @@ -0,0 +1,38 @@ +From f54c3f9f66001c210e10fda6aa17b9218fb67dc1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 29 Oct 2020 18:21:06 +0100 +Subject: [PATCH] main: Dump stack on segfaults by default + +--- + src/main.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/main.c b/src/main.c +index 5d07a4301..ed0b78dcc 100644 +--- a/src/main.c ++++ b/src/main.c +@@ -38,6 +38,7 @@ static int caught_signal = 0; + #define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 + #define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 + ++#define DEFAULT_SHELL_DEBUG SHELL_DEBUG_BACKTRACE_SEGFAULTS + enum { + SHELL_DEBUG_BACKTRACE_WARNINGS = 1, + SHELL_DEBUG_BACKTRACE_SEGFAULTS = 2, +@@ -279,8 +280,11 @@ shell_init_debug (const char *debug_env) + { "backtrace-segfaults", SHELL_DEBUG_BACKTRACE_SEGFAULTS }, + }; + +- _shell_debug = g_parse_debug_string (debug_env, keys, +- G_N_ELEMENTS (keys)); ++ if (debug_env) ++ _shell_debug = g_parse_debug_string (debug_env, keys, ++ G_N_ELEMENTS (keys)); ++ else ++ _shell_debug = DEFAULT_SHELL_DEBUG; + } + + static void +-- +2.31.1 + diff --git a/0001-panel-add-an-icon-to-the-ActivitiesButton.patch b/0001-panel-add-an-icon-to-the-ActivitiesButton.patch new file mode 100644 index 0000000..8ebfc46 --- /dev/null +++ b/0001-panel-add-an-icon-to-the-ActivitiesButton.patch @@ -0,0 +1,56 @@ +From b5db4d318546654f4e9c1e4999fa00456441f105 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 15 Jan 2014 16:45:34 -0500 +Subject: [PATCH] panel: add an icon to the ActivitiesButton + +Requested by brand +--- + data/theme/gnome-shell-sass/widgets/_panel.scss | 5 +++++ + js/ui/panel.js | 11 ++++++++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/data/theme/gnome-shell-sass/widgets/_panel.scss b/data/theme/gnome-shell-sass/widgets/_panel.scss +index 1f4650773..5f323cbc8 100644 +--- a/data/theme/gnome-shell-sass/widgets/_panel.scss ++++ b/data/theme/gnome-shell-sass/widgets/_panel.scss +@@ -85,6 +85,11 @@ $panel_transition_duration: 250ms; // same as the overview transition duration + // dimensions of the icon are hardcoded + } + ++ .panel-logo-icon { ++ padding-right: .4em; ++ icon-size: 1em; ++ } ++ + &#panelActivities { + -natural-hpadding: $base_padding * 3; + } +diff --git a/js/ui/panel.js b/js/ui/panel.js +index 1474886ef..ad11f4ba2 100644 +--- a/js/ui/panel.js ++++ b/js/ui/panel.js +@@ -390,11 +390,20 @@ class ActivitiesButton extends PanelMenu.Button { + + this.name = 'panelActivities'; + ++ const box = new St.BoxLayout(); ++ this.add_child(box); ++ const iconFile = Gio.File.new_for_path('/usr/share/icons/hicolor/scalable/apps/start-here.svg'); ++ this._icon = new St.Icon({ ++ gicon: new Gio.FileIcon({ file: iconFile }), ++ style_class: 'panel-logo-icon', ++ }); ++ box.add_child(this._icon); ++ + /* Translators: If there is no suitable word for "Activities" + in your language, you can use the word for "Overview". */ + this._label = new St.Label({ text: _("Activities"), + y_align: Clutter.ActorAlign.CENTER }); +- this.add_actor(this._label); ++ box.add_child(this._label); + + this.label_actor = this._label; + +-- +2.31.1 + diff --git a/0001-screenShield-unblank-when-inserting-smartcard.patch b/0001-screenShield-unblank-when-inserting-smartcard.patch new file mode 100644 index 0000000..3a1ac18 --- /dev/null +++ b/0001-screenShield-unblank-when-inserting-smartcard.patch @@ -0,0 +1,33 @@ +From 1e4e9248ef6bcdd95ec3b91c8c8e94c4587a876b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 3 Jul 2015 13:54:36 -0400 +Subject: [PATCH] screenShield: unblank when inserting smartcard + +If a user inserts the smartcard when the screen is locked/blanked +we should ask them their pin right away. + +At the moment they have to wiggle the mouse or do some other +action to get the screen to unblank. +--- + js/ui/screenShield.js | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js +index 9a64fc32c..bc1a0fba7 100644 +--- a/js/ui/screenShield.js ++++ b/js/ui/screenShield.js +@@ -85,8 +85,10 @@ var ScreenShield = class { + this._smartcardManager = SmartcardManager.getSmartcardManager(); + this._smartcardManager.connect('smartcard-inserted', + (manager, token) => { +- if (this._isLocked && token.UsedToLogin) ++ if (this._isLocked && token.UsedToLogin) { ++ this._wakeUpScreen(); + this._activateDialog(); ++ } + }); + + this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager(); +-- +2.31.1 + diff --git a/0001-st-texture-cache-purge-on-resume.patch b/0001-st-texture-cache-purge-on-resume.patch new file mode 100644 index 0000000..1a32fc7 --- /dev/null +++ b/0001-st-texture-cache-purge-on-resume.patch @@ -0,0 +1,66 @@ +From 483f0340bb64767bd8d6d95788058270dfdb5def Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 15 Jan 2019 12:54:32 -0500 +Subject: [PATCH] st-texture-cache: purge on resume + +With the proprietary nvidia driver, textures get garbled on suspend, +so the texture cache needs to evict all textures in that situation. +--- + js/ui/main.js | 6 +++++- + src/st/st-texture-cache.c | 10 ++++++++++ + src/st/st-texture-cache.h | 1 + + 3 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/js/ui/main.js b/js/ui/main.js +index 979fcefa5..dbf3a32d3 100644 +--- a/js/ui/main.js ++++ b/js/ui/main.js +@@ -249,7 +249,11 @@ function _initializeUI() { + return true; + }); + +- global.display.connect('gl-video-memory-purged', loadTheme); ++ global.display.connect('gl-video-memory-purged', () => { ++ let cache = St.TextureCache.get_default(); ++ cache.clear(); ++ loadTheme(); ++ }); + + // Provide the bus object for gnome-session to + // initiate logouts. +diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c +index b7b547a78..583c3f7d2 100644 +--- a/src/st/st-texture-cache.c ++++ b/src/st/st-texture-cache.c +@@ -130,6 +130,16 @@ st_texture_cache_class_init (StTextureCacheClass *klass) + G_TYPE_NONE, 1, G_TYPE_FILE); + } + ++/* Evicts all cached textures */ ++void ++st_texture_cache_clear (StTextureCache *cache) ++{ ++ g_return_if_fail (ST_IS_TEXTURE_CACHE (cache)); ++ ++ g_hash_table_remove_all (cache->priv->keyed_cache); ++ g_signal_emit (cache, signals[ICON_THEME_CHANGED], 0); ++} ++ + /* Evicts all cached textures for named icons */ + static void + st_texture_cache_evict_icons (StTextureCache *cache) +diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h +index 55d84952d..948915c30 100644 +--- a/src/st/st-texture-cache.h ++++ b/src/st/st-texture-cache.h +@@ -53,6 +53,7 @@ typedef enum { + } StTextureCachePolicy; + + StTextureCache* st_texture_cache_get_default (void); ++void st_texture_cache_clear (StTextureCache *cache); + + ClutterActor * + st_texture_cache_load_sliced_image (StTextureCache *cache, +-- +2.31.1 + diff --git a/0001-windowMenu-Bring-back-workspaces-submenu-for-static-.patch b/0001-windowMenu-Bring-back-workspaces-submenu-for-static-.patch new file mode 100644 index 0000000..980f607 --- /dev/null +++ b/0001-windowMenu-Bring-back-workspaces-submenu-for-static-.patch @@ -0,0 +1,45 @@ +From 34a7bfdade939e39c2a01cc1b0737a7bdccddd5b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 14 Mar 2017 17:04:36 +0100 +Subject: [PATCH] windowMenu: Bring back workspaces submenu for static + workspaces + +When the titlebar context menu was moved to the shell, the submenu for +moving to a specific workspace was intentionally left out; some people +are quite attached to it though, so bring it back when static workspaces +are used. +--- + js/ui/windowMenu.js | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/js/ui/windowMenu.js b/js/ui/windowMenu.js +index bb6a8df7b..3449f759d 100644 +--- a/js/ui/windowMenu.js ++++ b/js/ui/windowMenu.js +@@ -116,6 +116,23 @@ var WindowMenu = class extends PopupMenu.PopupMenu { + window.change_workspace(workspace.get_neighbor(dir)); + }); + } ++ ++ let { workspaceManager } = global; ++ let nWorkspaces = workspaceManager.n_workspaces; ++ if (nWorkspaces > 1 && !Meta.prefs_get_dynamic_workspaces()) { ++ item = new PopupMenu.PopupSubMenuMenuItem(_("Move to another workspace")); ++ this.addMenuItem(item); ++ ++ let currentIndex = workspaceManager.get_active_workspace_index(); ++ for (let i = 0; i < nWorkspaces; i++) { ++ let index = i; ++ let name = Meta.prefs_get_workspace_name(i); ++ let subitem = item.menu.addAction(name, () => { ++ window.change_workspace_by_index(index, false); ++ }); ++ subitem.setSensitive(currentIndex != i); ++ } ++ } + } + } + +-- +2.31.1 + diff --git a/disable-unlock-entry-until-question.patch b/disable-unlock-entry-until-question.patch new file mode 100644 index 0000000..20980c5 --- /dev/null +++ b/disable-unlock-entry-until-question.patch @@ -0,0 +1,176 @@ +From 6739f213965c2b6a41c21b446095f393f9d86e43 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 30 Sep 2015 12:51:24 -0400 +Subject: [PATCH 1/3] authPrompt: don't fade out auth messages if user types + password up front + +Right now we fade out any stale auth messages as soon as the user starts +typing. This behavior doesn't really make sense if the user is typing up +front, before a password is asked. +--- + js/gdm/authPrompt.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js +index 4844b9ee0..149e5ad4a 100644 +--- a/js/gdm/authPrompt.js ++++ b/js/gdm/authPrompt.js +@@ -179,7 +179,7 @@ var AuthPrompt = GObject.registerClass({ + + [this._textEntry, this._passwordEntry].forEach(entry => { + entry.clutter_text.connect('text-changed', () => { +- if (!this._userVerifier.hasPendingMessages) ++ if (!this._userVerifier.hasPendingMessages && this._queryingService && !this._preemptiveAnswer) + this._fadeOutMessage(); + }); + +-- +2.31.1 + + +From 2b84c3d611120ae2f60386d5c637b84d1958398d Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 30 Sep 2015 14:36:33 -0400 +Subject: [PATCH 2/3] authPrompt: don't spin unless answering question + +--- + js/gdm/authPrompt.js | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js +index 149e5ad4a..c5643d046 100644 +--- a/js/gdm/authPrompt.js ++++ b/js/gdm/authPrompt.js +@@ -243,13 +243,14 @@ var AuthPrompt = GObject.registerClass({ + this.verificationStatus = AuthPromptStatus.VERIFICATION_IN_PROGRESS; + this.updateSensitivity(false); + +- if (shouldSpin) +- this.startSpinning(); ++ if (this._queryingService) { ++ if (shouldSpin) ++ this.startSpinning(); + +- if (this._queryingService) + this._userVerifier.answerQuery(this._queryingService, this._entry.text); +- else ++ } else { + this._preemptiveAnswer = this._entry.text; ++ } + + this.emit('next'); + } +-- +2.31.1 + + +From 56360c872e01b0554b4d8b53dddba5407d4e889b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 5 Oct 2015 15:26:18 -0400 +Subject: [PATCH 3/3] authPrompt: stop accepting preemptive answer if user + stops typing + +We only want to allow the user to type the preemptive password in +one smooth motion. If they start to type, and then stop typing, +we should discard their preemptive password as expired. + +Typing ahead the password is just a convenience for users who don't +want to manually lift the shift before typing their passwords, after +all. +--- + js/gdm/authPrompt.js | 37 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 36 insertions(+), 1 deletion(-) + +diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js +index c5643d046..84c608b2f 100644 +--- a/js/gdm/authPrompt.js ++++ b/js/gdm/authPrompt.js +@@ -1,7 +1,7 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + /* exported AuthPrompt */ + +-const { Clutter, GLib, GObject, Pango, Shell, St } = imports.gi; ++const { Clutter, GLib, GObject, Meta, Pango, Shell, St } = imports.gi; + + const Animation = imports.ui.animation; + const Batch = imports.gdm.batch; +@@ -63,6 +63,8 @@ var AuthPrompt = GObject.registerClass({ + this._defaultButtonWellActor = null; + this._cancelledRetries = 0; + ++ this._idleMonitor = Meta.IdleMonitor.get_core(); ++ + let reauthenticationOnly; + if (this._mode == AuthPromptMode.UNLOCK_ONLY) + reauthenticationOnly = true; +@@ -119,6 +121,11 @@ var AuthPrompt = GObject.registerClass({ + } + + _onDestroy() { ++ if (this._preemptiveAnswerWatchId) { ++ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); ++ this._preemptiveAnswerWatchId = 0; ++ } ++ + this._userVerifier.destroy(); + this._userVerifier = null; + } +@@ -250,6 +257,11 @@ var AuthPrompt = GObject.registerClass({ + this._userVerifier.answerQuery(this._queryingService, this._entry.text); + } else { + this._preemptiveAnswer = this._entry.text; ++ ++ if (this._preemptiveAnswerWatchId) { ++ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); ++ this._preemptiveAnswerWatchId = 0; ++ } + } + + this.emit('next'); +@@ -429,6 +441,11 @@ var AuthPrompt = GObject.registerClass({ + } + + setQuestion(question) { ++ if (this._preemptiveAnswerWatchId) { ++ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); ++ this._preemptiveAnswerWatchId = 0; ++ } ++ + this._entry.hint_text = question; + + this._entry.show(); +@@ -530,6 +547,19 @@ var AuthPrompt = GObject.registerClass({ + this._updateEntry(false); + } + ++ _onUserStoppedTypePreemptiveAnswer() { ++ if (!this._preemptiveAnswerWatchId || ++ this._preemptiveAnswer || ++ this._queryingService) ++ return; ++ ++ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); ++ this._preemptiveAnswerWatchId = 0; ++ ++ this._entry.text = ''; ++ this.updateSensitivity(false); ++ } ++ + reset() { + let oldStatus = this.verificationStatus; + this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; +@@ -537,6 +567,11 @@ var AuthPrompt = GObject.registerClass({ + this.cancelButton.can_focus = this._hasCancelButton; + this._preemptiveAnswer = null; + ++ if (this._preemptiveAnswerWatchId) ++ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId); ++ this._preemptiveAnswerWatchId = this._idleMonitor.add_idle_watch(500, ++ this._onUserStoppedTypePreemptiveAnswer.bind(this)); ++ + if (this._userVerifier) + this._userVerifier.cancel(); + +-- +2.31.1 + diff --git a/enforce-smartcard-at-unlock.patch b/enforce-smartcard-at-unlock.patch new file mode 100644 index 0000000..ffe2316 --- /dev/null +++ b/enforce-smartcard-at-unlock.patch @@ -0,0 +1,114 @@ +From d2c12a372ea0ccbe6ba682c553d8b83b3253169f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 28 Sep 2015 10:57:02 -0400 +Subject: [PATCH 1/3] smartcardManager: add way to detect if user logged using + (any) token + +If a user uses a token at login time, we need to make sure they continue +to use the token at unlock time. + +As a prerequisite for addressing that problem we need to know up front +if a user logged in with a token at all. + +This commit adds the necessary api to detect that case. +--- + js/misc/smartcardManager.js | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/js/misc/smartcardManager.js b/js/misc/smartcardManager.js +index d9b6ff474..26f9f5aaa 100644 +--- a/js/misc/smartcardManager.js ++++ b/js/misc/smartcardManager.js +@@ -111,5 +111,12 @@ var SmartcardManager = class { + + return true; + } ++ ++ loggedInWithToken() { ++ if (this._loginToken) ++ return true; ++ ++ return false; ++ } + }; + Signals.addSignalMethods(SmartcardManager.prototype); +-- +2.31.1 + + +From 98393eef884edc9e685b712c71356751acdd552f Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 28 Sep 2015 19:56:53 -0400 +Subject: [PATCH 2/3] gdm: only unlock with smartcard, if smartcard used for + login + +If a smartcard is used for login, we need to make sure the smartcard +gets used for unlock, too. +--- + js/gdm/util.js | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 72561daab..6b92e3564 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -149,7 +149,6 @@ var ShellUserVerifier = class { + this._settings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); + this._settings.connect('changed', + this._updateDefaultService.bind(this)); +- this._updateDefaultService(); + + this._fprintManager = new FprintManagerProxy(Gio.DBus.system, + 'net.reactivated.Fprint', +@@ -166,6 +165,8 @@ var ShellUserVerifier = class { + this.smartcardDetected = false; + this._checkForSmartcard(); + ++ this._updateDefaultService(); ++ + this._smartcardInsertedId = this._smartcardManager.connect('smartcard-inserted', + this._checkForSmartcard.bind(this)); + this._smartcardRemovedId = this._smartcardManager.connect('smartcard-removed', +@@ -527,7 +528,9 @@ var ShellUserVerifier = class { + } + + _updateDefaultService() { +- if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) ++ if (this._smartcardManager.loggedInWithToken()) ++ this._defaultService = SMARTCARD_SERVICE_NAME; ++ else if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) + this._defaultService = PASSWORD_SERVICE_NAME; + else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) + this._defaultService = SMARTCARD_SERVICE_NAME; +-- +2.31.1 + + +From 57ca969a0af6f65e71dc1158163b9c826bdb7079 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 28 Sep 2015 19:57:36 -0400 +Subject: [PATCH 3/3] gdm: update default service when smartcard inserted + +Early on at start up we may not know if a smartcard is +available. Make sure we reupdate the default service +after we get a smartcard insertion event. +--- + js/gdm/util.js | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/js/gdm/util.js b/js/gdm/util.js +index 6b92e3564..e62114cb1 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -420,6 +420,8 @@ var ShellUserVerifier = class { + else if (this._preemptingService == SMARTCARD_SERVICE_NAME) + this._preemptingService = null; + ++ this._updateDefaultService(); ++ + this.emit('smartcard-status-changed'); + } + } +-- +2.31.1 + diff --git a/fix-some-js-warnings.patch b/fix-some-js-warnings.patch new file mode 100644 index 0000000..9b2b3fc --- /dev/null +++ b/fix-some-js-warnings.patch @@ -0,0 +1,62 @@ +From 53e4d69a93c8e8e6a2334a3167a5570b046f929a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 9 Jun 2020 19:42:21 +0200 +Subject: [PATCH 1/2] popupMenu: Guard against non-menu-item children + +This avoid a harmless but annoying warning. +--- + js/ui/popupMenu.js | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js +index 11528560d..144c600d7 100644 +--- a/js/ui/popupMenu.js ++++ b/js/ui/popupMenu.js +@@ -773,7 +773,8 @@ var PopupMenuBase = class { + } + + _getMenuItems() { +- return this.box.get_children().map(a => a._delegate).filter(item => { ++ const children = this.box.get_children().filter(a => a._delegate !== undefined); ++ return children.map(a => a._delegate).filter(item => { + return item instanceof PopupBaseMenuItem || item instanceof PopupMenuSection; + }); + } +-- +2.31.1 + + +From 6136be42f20c5647c283c27ab1b0fa57a6952412 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 9 Jun 2020 19:48:06 +0200 +Subject: [PATCH 2/2] st/shadow: Check pipeline when painting + +We shouldn't simply assume that st_shadow_helper_update() has been +called before paint() or that the pipeline was created successfully. +--- + src/st/st-shadow.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/st/st-shadow.c b/src/st/st-shadow.c +index ab3eaa856..d53808698 100644 +--- a/src/st/st-shadow.c ++++ b/src/st/st-shadow.c +@@ -296,9 +296,10 @@ st_shadow_helper_paint (StShadowHelper *helper, + ClutterActorBox *actor_box, + guint8 paint_opacity) + { +- _st_paint_shadow_with_opacity (helper->shadow, +- framebuffer, +- helper->pipeline, +- actor_box, +- paint_opacity); ++ if (helper->pipeline != NULL) ++ _st_paint_shadow_with_opacity (helper->shadow, ++ framebuffer, ++ helper->pipeline, ++ actor_box, ++ paint_opacity); + } +-- +2.31.1 + diff --git a/gnome-shell-favourite-apps-terminal.patch b/gnome-shell-favourite-apps-terminal.patch new file mode 100644 index 0000000..5f7207a --- /dev/null +++ b/gnome-shell-favourite-apps-terminal.patch @@ -0,0 +1,25 @@ +From 1e699b55f3dc84b2ddbc5acd03424240eddbe06c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 9 Mar 2017 14:44:32 +0100 +Subject: [PATCH 3/3] appFavorites: Add terminal + +--- + data/org.gnome.shell.gschema.xml.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in +index 35ddaf4a9..d5ea1e35f 100644 +--- a/data/org.gnome.shell.gschema.xml.in ++++ b/data/org.gnome.shell.gschema.xml.in +@@ -50,7 +50,7 @@ + + + +- [ 'firefox.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop' ] ++ [ 'firefox.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop', 'org.gnome.Terminal.desktop' ] + List of desktop file IDs for favorite applications + + The applications corresponding to these identifiers +-- +2.31.1 + diff --git a/gnome-shell-favourite-apps-yelp.patch b/gnome-shell-favourite-apps-yelp.patch new file mode 100644 index 0000000..f47dab8 --- /dev/null +++ b/gnome-shell-favourite-apps-yelp.patch @@ -0,0 +1,26 @@ +From 4e21aed64d48ddd22e40a3605084379b2fa7f1cb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 9 Mar 2017 14:44:03 +0100 +Subject: [PATCH 2/3] Add 'yelp' to default favorites + +Help should be easily available, so add it to the default favorites. +--- + data/org.gnome.shell.gschema.xml.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in +index b8a13a9cc..35ddaf4a9 100644 +--- a/data/org.gnome.shell.gschema.xml.in ++++ b/data/org.gnome.shell.gschema.xml.in +@@ -50,7 +50,7 @@ + + + +- [ 'firefox.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ] ++ [ 'firefox.desktop', 'org.gnome.Calendar.desktop', 'org.gnome.Music.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop' ] + List of desktop file IDs for favorite applications + + The applications corresponding to these identifiers +-- +2.31.1 + diff --git a/gnome-shell.spec b/gnome-shell.spec index 1a6ca89..b09dfd8 100644 --- a/gnome-shell.spec +++ b/gnome-shell.spec @@ -2,7 +2,7 @@ Name: gnome-shell Version: 40.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Window management and application launching for GNOME License: GPLv2+ @@ -11,6 +11,24 @@ Source0: http://download.gnome.org/sources/gnome-shell/40/%{name}-%{tarball_vers # Replace Epiphany with Firefox in the default favourite apps list Patch1: gnome-shell-favourite-apps-firefox.patch +Patch2: gnome-shell-favourite-apps-yelp.patch +Patch3: gnome-shell-favourite-apps-terminal.patch + +# GDM/Lock stuff +Patch10: 0001-screenShield-unblank-when-inserting-smartcard.patch +Patch11: enforce-smartcard-at-unlock.patch +Patch12: disable-unlock-entry-until-question.patch +Patch13: 0001-loginDialog-make-info-messages-themed.patch +Patch14: support-choicelist-extension.patch + +# Misc. +Patch30: 0001-panel-add-an-icon-to-the-ActivitiesButton.patch +Patch31: 0001-app-Fall-back-to-window-title-instead-of-WM_CLASS.patch +Patch32: 0001-windowMenu-Bring-back-workspaces-submenu-for-static-.patch +Patch33: 0001-main-Dump-stack-on-segfaults-by-default.patch +Patch34: 0001-extensionDownloader-Refuse-to-override-system-extens.patch +Patch35: fix-some-js-warnings.patch +Patch36: 0001-st-texture-cache-purge-on-resume.patch %define eds_version 3.33.1 %define gnome_desktop_version 3.35.91 @@ -227,6 +245,10 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/evolution-calendar.de %{_mandir}/man1/gnome-shell.1* %changelog +* Wed May 19 2021 Florian Müllner - 40.1-2 +- Re-apply RHEL8 downstream patches that are still valid + Resolves: #1949133 + * Fri May 14 2021 Florian Müllner - 40.1-1 - Update to 40.1 Resolves: #1951132 diff --git a/support-choicelist-extension.patch b/support-choicelist-extension.patch new file mode 100644 index 0000000..52d8a08 --- /dev/null +++ b/support-choicelist-extension.patch @@ -0,0 +1,489 @@ +From 0994cc8fe87cfc9d78221e2e6df074257124a81d Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 18 Jul 2017 12:58:14 -0400 +Subject: [PATCH 1/2] gdm: add AuthList control + +Ultimately, we want to add support for GDM's new ChoiceList +PAM extension. That extension allows PAM modules to present +a list of choices to the user. Before we can support that +extension, however, we need to have a list control in the +login-screen/unlock screen. This commit adds that control. + +For the most part, it's a copy-and-paste of the gdm userlist, +but with less features. It lacks API specific to the users, +lacks the built in timed login indicator, etc. It does feature +a label heading. +--- + js/gdm/authList.js | 193 ++++++++++++++++++++++++++++++++++ + js/js-resources.gresource.xml | 1 + + 2 files changed, 194 insertions(+) + create mode 100644 js/gdm/authList.js + +diff --git a/js/gdm/authList.js b/js/gdm/authList.js +new file mode 100644 +index 000000000..e4475dc20 +--- /dev/null ++++ b/js/gdm/authList.js +@@ -0,0 +1,193 @@ ++// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- ++/* ++ * Copyright 2017 Red Hat, Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++/* exported AuthList */ ++ ++const { Clutter, GObject, Meta, St } = imports.gi; ++ ++const SCROLL_ANIMATION_TIME = 500; ++ ++const AuthListItem = GObject.registerClass({ ++ Signals: { 'activate': {} }, ++}, class AuthListItem extends St.Button { ++ _init(key, text) { ++ this.key = key; ++ const label = new St.Label({ ++ style_class: 'auth-list-item-label', ++ y_align: Clutter.ActorAlign.CENTER, ++ }); ++ label.text = text; ++ ++ super._init({ ++ style_class: 'login-dialog-user-list-item', ++ button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, ++ can_focus: true, ++ child: label, ++ reactive: true, ++ x_align: St.Align.START, ++ x_fill: true, ++ }); ++ ++ this.connect('key-focus-in', ++ () => this._setSelected(true)); ++ this.connect('key-focus-out', ++ () => this._setSelected(false)); ++ this.connect('notify::hover', ++ () => this._setSelected(this.hover)); ++ ++ this.connect('clicked', this._onClicked.bind(this)); ++ } ++ ++ _onClicked() { ++ this.emit('activate'); ++ } ++ ++ _setSelected(selected) { ++ if (selected) { ++ this.add_style_pseudo_class('selected'); ++ this.grab_key_focus(); ++ } else { ++ this.remove_style_pseudo_class('selected'); ++ } ++ } ++}); ++ ++var AuthList = GObject.registerClass({ ++ Signals: { ++ 'activate': { param_types: [GObject.TYPE_STRING] }, ++ 'item-added': { param_types: [AuthListItem.$gtype] }, ++ }, ++}, class AuthList extends St.BoxLayout { ++ _init() { ++ super._init({ ++ vertical: true, ++ style_class: 'login-dialog-auth-list-layout', ++ }); ++ ++ this.label = new St.Label({ style_class: 'prompt-dialog-headline' }); ++ this.add_child(this.label); ++ ++ this._scrollView = new St.ScrollView({ ++ style_class: 'login-dialog-user-list-view', ++ }); ++ this._scrollView.set_policy( ++ St.PolicyType.NEVER, St.PolicyType.AUTOMATIC); ++ this.add_child(this._scrollView); ++ ++ this._box = new St.BoxLayout({ ++ vertical: true, ++ style_class: 'login-dialog-user-list', ++ pseudo_class: 'expanded', ++ }); ++ ++ this._scrollView.add_actor(this._box); ++ this._items = {}; ++ ++ this.connect('key-focus-in', this._moveFocusToItems.bind(this)); ++ } ++ ++ _moveFocusToItems() { ++ let hasItems = Object.keys(this._items).length > 0; ++ ++ if (!hasItems) ++ return; ++ ++ if (global.stage.get_key_focus() !== this) ++ return; ++ ++ let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); ++ if (!focusSet) { ++ Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => { ++ this._moveFocusToItems(); ++ return false; ++ }); ++ } ++ } ++ ++ _onItemActivated(activatedItem) { ++ this.emit('activate', activatedItem.key); ++ } ++ ++ scrollToItem(item) { ++ let box = item.get_allocation_box(); ++ ++ let adjustment = this._scrollView.get_vscroll_bar().get_adjustment(); ++ ++ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); ++ adjustment.ease(value, { ++ duration: SCROLL_ANIMATION_TIME, ++ mode: Clutter.AnimationMode.EASE_OUT_QUAD, ++ }); ++ } ++ ++ jumpToItem(item) { ++ let box = item.get_allocation_box(); ++ ++ let adjustment = this._scrollView.get_vscroll_bar().get_adjustment(); ++ ++ let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); ++ ++ adjustment.set_value(value); ++ } ++ ++ getItem(key) { ++ let item = this._items[key]; ++ ++ if (!item) ++ return null; ++ ++ return item; ++ } ++ ++ addItem(key, text) { ++ this.removeItem(key); ++ ++ let item = new AuthListItem(key, text); ++ this._box.add(item, { x_fill: true }); ++ ++ this._items[key] = item; ++ ++ item.connect('activate', this._onItemActivated.bind(this)); ++ ++ // Try to keep the focused item front-and-center ++ item.connect('key-focus-in', () => this.scrollToItem(item)); ++ ++ this._moveFocusToItems(); ++ ++ this.emit('item-added', item); ++ } ++ ++ removeItem(key) { ++ let item = this._items[key]; ++ ++ if (!item) ++ return; ++ ++ item.destroy(); ++ delete this._items[key]; ++ } ++ ++ numItems() { ++ return Object.keys(this._items).length; ++ } ++ ++ clear() { ++ this.label.text = ''; ++ this._box.destroy_all_children(); ++ this._items = {}; ++ } ++}); +diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml +index e65e0e9cf..b2c603a55 100644 +--- a/js/js-resources.gresource.xml ++++ b/js/js-resources.gresource.xml +@@ -1,6 +1,7 @@ + + + ++ gdm/authList.js + gdm/authPrompt.js + gdm/batch.js + gdm/loginDialog.js +-- +2.31.1 + + +From 93bf4dffcd3d7790428bdc9c3398a50a6538b055 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 17 Jul 2017 16:48:03 -0400 +Subject: [PATCH 2/2] gdmUtil: enable support for GDM's ChoiceList PAM + extension + +This commit hooks up support for GDM's ChoiceList PAM extension. +--- + js/gdm/authPrompt.js | 71 +++++++++++++++++++++++++++++++++++++++++++ + js/gdm/loginDialog.js | 5 +++ + js/gdm/util.js | 28 +++++++++++++++++ + js/ui/unlockDialog.js | 7 +++++ + 4 files changed, 111 insertions(+) + +diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js +index 84c608b2f..4b889d8b5 100644 +--- a/js/gdm/authPrompt.js ++++ b/js/gdm/authPrompt.js +@@ -4,6 +4,7 @@ + const { Clutter, GLib, GObject, Meta, Pango, Shell, St } = imports.gi; + + const Animation = imports.ui.animation; ++const AuthList = imports.gdm.authList; + const Batch = imports.gdm.batch; + const GdmUtil = imports.gdm.util; + const OVirt = imports.gdm.oVirt; +@@ -75,6 +76,7 @@ var AuthPrompt = GObject.registerClass({ + + this._userVerifier.connect('ask-question', this._onAskQuestion.bind(this)); + this._userVerifier.connect('show-message', this._onShowMessage.bind(this)); ++ this._userVerifier.connect('show-choice-list', this._onShowChoiceList.bind(this)); + this._userVerifier.connect('verification-failed', this._onVerificationFailed.bind(this)); + this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this)); + this._userVerifier.connect('reset', this._onReset.bind(this)); +@@ -107,6 +109,27 @@ var AuthPrompt = GObject.registerClass({ + capsLockPlaceholder, 'visible', + GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.INVERT_BOOLEAN); + ++ this._authList = new AuthList.AuthList(); ++ this._authList.set({ ++ x_expand: true, ++ x_align: Clutter.ActorAlign.START, ++ visible: false, ++ }); ++ this._authList.connect('activate', (list, key) => { ++ this._authList.reactive = false; ++ this._authList.ease({ ++ opacity: 0, ++ duration: MESSAGE_FADE_OUT_ANIMATION_TIME, ++ mode: Clutter.AnimationMode.EASE_OUT_QUAD, ++ onComplete: () => { ++ this._authList.clear(); ++ this._authList.hide(); ++ this._userVerifier.selectChoice(this._queryingService, key); ++ }, ++ }); ++ }); ++ this.add_child(this._authList); ++ + this._message = new St.Label({ + opacity: 0, + styleClass: 'login-dialog-message', +@@ -303,6 +326,20 @@ var AuthPrompt = GObject.registerClass({ + this.emit('prompted'); + } + ++ _onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) { ++ if (this._queryingService) ++ this.clear(); ++ ++ this._queryingService = serviceName; ++ ++ if (this._preemptiveAnswer) ++ this._preemptiveAnswer = null; ++ ++ this.setChoiceList(promptMessage, choiceList); ++ this.updateSensitivity(true); ++ this.emit('prompted'); ++ } ++ + _onCredentialManagerAuthenticated() { + if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) + this.reset(); +@@ -438,6 +475,8 @@ var AuthPrompt = GObject.registerClass({ + clear() { + this._entry.text = ''; + this.stopSpinning(); ++ this._authList.clear(); ++ this._authList.hide(); + } + + setQuestion(question) { +@@ -448,10 +487,41 @@ var AuthPrompt = GObject.registerClass({ + + this._entry.hint_text = question; + ++ this._authList.hide(); ++ this._label.show(); + this._entry.show(); + this._entry.grab_key_focus(); + } + ++ _fadeInChoiceList() { ++ this._authList.set({ ++ opacity: 0, ++ visible: true, ++ reactive: false, ++ }); ++ this._authList.ease({ ++ opacity: 255, ++ duration: MESSAGE_FADE_OUT_ANIMATION_TIME, ++ transition: Clutter.AnimationMode.EASE_OUT_QUAD, ++ onComplete: () => (this._authList.reactive = true), ++ }); ++ } ++ ++ setChoiceList(promptMessage, choiceList) { ++ this._authList.clear(); ++ this._authList.label.text = promptMessage; ++ for (let key in choiceList) { ++ let text = choiceList[key]; ++ this._authList.addItem(key, text); ++ } ++ ++ this._label.hide(); ++ this._entry.hide(); ++ if (this._message.text == '') ++ this._message.hide(); ++ this._fadeInChoiceList(); ++ } ++ + getAnswer() { + let text; + +@@ -487,6 +557,7 @@ var AuthPrompt = GObject.registerClass({ + else + this._message.remove_style_class_name('login-dialog-message-hint'); + ++ this._message.show(); + if (message) { + this._message.remove_all_transitions(); + this._message.text = message; +diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js +index d2a82b43d..41dd99646 100644 +--- a/js/gdm/loginDialog.js ++++ b/js/gdm/loginDialog.js +@@ -418,6 +418,11 @@ var LoginDialog = GObject.registerClass({ + this._userManager = AccountsService.UserManager.get_default(); + this._gdmClient = new Gdm.Client(); + ++ try { ++ this._gdmClient.set_enabled_extensions([Gdm.UserVerifierChoiceList.interface_info().name]); ++ } catch (e) { ++ } ++ + this._settings = new Gio.Settings({ schema_id: GdmUtil.LOGIN_SCREEN_SCHEMA }); + + this._settings.connect('changed::%s'.format(GdmUtil.BANNER_MESSAGE_KEY), +diff --git a/js/gdm/util.js b/js/gdm/util.js +index e62114cb1..3f327400f 100644 +--- a/js/gdm/util.js ++++ b/js/gdm/util.js +@@ -238,6 +238,10 @@ var ShellUserVerifier = class { + this._disconnectSignals(); + this._userVerifier.run_dispose(); + this._userVerifier = null; ++ if (this._userVerifierChoiceList) { ++ this._userVerifierChoiceList.run_dispose(); ++ this._userVerifierChoiceList = null; ++ } + } + } + +@@ -268,6 +272,10 @@ var ShellUserVerifier = class { + } + } + ++ selectChoice(serviceName, key) { ++ this._userVerifierChoiceList.call_select_choice(serviceName, key, this._cancellable, null); ++ } ++ + answerQuery(serviceName, answer) { + if (!this.hasPendingMessages) { + this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); +@@ -456,6 +464,11 @@ var ShellUserVerifier = class { + return; + } + ++ if (this._client.get_user_verifier_choice_list) ++ this._userVerifierChoiceList = this._client.get_user_verifier_choice_list(); ++ else ++ this._userVerifierChoiceList = null; ++ + this.reauthenticating = true; + this._connectSignals(); + this._beginVerification(); +@@ -474,6 +487,11 @@ var ShellUserVerifier = class { + return; + } + ++ if (this._client.get_user_verifier_choice_list) ++ this._userVerifierChoiceList = this._client.get_user_verifier_choice_list(); ++ else ++ this._userVerifierChoiceList = null; ++ + this._connectSignals(); + this._beginVerification(); + this._hold.release(); +@@ -499,6 +517,9 @@ var ShellUserVerifier = class { + this._signalIds.push(id); + id = this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this)); + this._signalIds.push(id); ++ ++ if (this._userVerifierChoiceList) ++ this._userVerifierChoiceList.connect('choice-query', this._onChoiceListQuery.bind(this)); + } + + _disconnectSignals() { +@@ -581,6 +602,13 @@ var ShellUserVerifier = class { + this._startService(FINGERPRINT_SERVICE_NAME); + } + ++ _onChoiceListQuery(client, serviceName, promptMessage, list) { ++ if (!this.serviceIsForeground(serviceName)) ++ return; ++ ++ this.emit('show-choice-list', serviceName, promptMessage, list.deep_unpack()); ++ } ++ + _onInfo(client, serviceName, info) { + if (this.serviceIsForeground(serviceName)) { + this._queueMessage(serviceName, info, MessageType.INFO); +diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js +index 8ddae8b03..33fbbdd8e 100644 +--- a/js/ui/unlockDialog.js ++++ b/js/ui/unlockDialog.js +@@ -484,6 +484,13 @@ var UnlockDialog = GObject.registerClass({ + + this._gdmClient = new Gdm.Client(); + ++ try { ++ this._gdmClient.set_enabled_extensions([ ++ Gdm.UserVerifierChoiceList.interface_info().name, ++ ]); ++ } catch (e) { ++ } ++ + this._adjustment = new St.Adjustment({ + actor: this, + lower: 0, +-- +2.31.1 +