From 4775687d8569d6a3bcb526506f31c03b4fff19a6 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 05 2021 10:28:07 +0000 Subject: import gnome-shell-3.32.2-40.el8 --- 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 index 3f43c64..3528e9d 100644 --- 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 @@ -1,7 +1,7 @@ -From f03c6a870820543901331d1920b7b3e423813d2c Mon Sep 17 00:00:00 2001 +From 6e80934456f0b4cc48da6a7201700dc4386a3474 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() +Subject: [PATCH] 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 @@ -19,12 +19,12 @@ Signed-off-by: Christian Hergert 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/js/ui/environment.js b/js/ui/environment.js -index e22ec7402..f3f2d17c7 100644 +index 9c125d3eb..809b48e45 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -11,6 +11,9 @@ imports.gi.versions.TelepathyLogger = '0.2'; - const { Clutter, GLib, Shell, St } = imports.gi; + const { Clutter, Gio, GLib, Shell, St } = imports.gi; const Gettext = imports.gettext; +const System = imports.system; + @@ -32,7 +32,7 @@ index e22ec7402..f3f2d17c7 100644 // We can't import shell JS modules yet, because they may have // variable initializations, etc, that depend on init() already having -@@ -110,9 +113,26 @@ function init() { +@@ -117,9 +120,26 @@ function init() { } }; @@ -61,5 +61,5 @@ index e22ec7402..f3f2d17c7 100644 let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR'); -- -2.26.2 +2.31.1 diff --git a/SOURCES/0001-extensionSystem-Handle-added-or-removed-sessionMode-.patch b/SOURCES/0001-extensionSystem-Handle-added-or-removed-sessionMode-.patch new file mode 100644 index 0000000..2a8ff21 --- /dev/null +++ b/SOURCES/0001-extensionSystem-Handle-added-or-removed-sessionMode-.patch @@ -0,0 +1,87 @@ +From 1b6eb29ade832647510b36ddc13c9b88a25036df Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= +Date: Wed, 11 Sep 2019 20:18:20 +0200 +Subject: [PATCH 1/4] extensionSystem: Handle added or removed sessionMode + extensions + +Right now we're only handling added sessionMode extensions correctly on +sessionMode updates, also handle the other case and disable removed +sessionMode extensions on sessionMode updates. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/96 +--- + js/ui/extensionSystem.js | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js +index 81804ea5e..77929f2a6 100644 +--- a/js/ui/extensionSystem.js ++++ b/js/ui/extensionSystem.js +@@ -515,62 +515,62 @@ var ExtensionManager = class { + if (!this._initted) { + this._loadExtensions(); + this._initted = true; + } else { + this._enabledExtensions.forEach(uuid => { + this._callExtensionEnable(uuid); + }); + } + this._enabled = true; + } + + _disableAllExtensions() { + if (!this._enabled) + return; + + if (this._initted) { + this._extensionOrder.slice().reverse().forEach(uuid => { + this._callExtensionDisable(uuid); + }); + } + + this._enabled = false; + } + + _sessionUpdated() { + // For now sessionMode.allowExtensions controls extensions from both the + // 'enabled-extensions' preference and the sessionMode.enabledExtensions + // property; it might make sense to make enabledExtensions independent + // from allowExtensions in the future + if (Main.sessionMode.allowExtensions) { +- if (this._initted) +- this._enabledExtensions = this._getEnabledExtensions(); ++ // Take care of added or removed sessionMode extensions ++ this._onEnabledExtensionsChanged(); + this._enableAllExtensions(); + } else { + this._disableAllExtensions(); + } + } + }; + Signals.addSignalMethods(ExtensionManager.prototype); + + class ExtensionUpdateSource extends MessageTray.Source { + constructor() { + const appSys = Shell.AppSystem.get_default(); + this._app = appSys.lookup_app('gnome-shell-extension-prefs.desktop'); + + super(this._app.get_name()); + } + + getIcon() { + return this._app.app_info.get_icon(); + } + + _createPolicy() { + return new MessageTray.NotificationApplicationPolicy(this._app.id); + } + + open() { + this._app.activate(); + Main.overview.hide(); + Main.panel.closeCalendar(); + } + } +-- +2.27.0 + diff --git a/SOURCES/0001-keyboard-Only-enable-keyboard-if-ClutterDeviceManage.patch b/SOURCES/0001-keyboard-Only-enable-keyboard-if-ClutterDeviceManage.patch new file mode 100644 index 0000000..a781d43 --- /dev/null +++ b/SOURCES/0001-keyboard-Only-enable-keyboard-if-ClutterDeviceManage.patch @@ -0,0 +1,39 @@ +From 2acede02f30833c3fb891db8483f933f7b41508c Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Wed, 21 Oct 2020 21:32:03 +0200 +Subject: [PATCH] keyboard: Only enable keyboard if + ClutterDeviceManager::touch-mode is enabled + +--- + js/ui/keyboard.js | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js +index 94b5325..b1ee270 100644 +--- a/js/ui/keyboard.js ++++ b/js/ui/keyboard.js +@@ -1051,6 +1051,9 @@ var Keyboard = class Keyboard { + this._suggestions = null; + this._emojiKeyVisible = true; + ++ let manager = Clutter.DeviceManager.get_default(); ++ manager.connect('notify::touch-mode', this._syncEnabled.bind(this)); ++ + this._focusTracker = new FocusTracker(); + this._focusTracker.connect('position-changed', this._onFocusPositionChanged.bind(this)); + this._focusTracker.connect('reset', () => { +@@ -1120,8 +1123,10 @@ var Keyboard = class Keyboard { + + _syncEnabled() { + let wasEnabled = this._enabled; ++ let manager = Clutter.DeviceManager.get_default(); ++ let autoEnabled = manager.get_touch_mode() && this._lastDeviceIsTouchscreen(); + this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD); +- this._enabled = this._enableKeyboard || this._lastDeviceIsTouchscreen(); ++ this._enabled = this._enableKeyboard || autoEnabled; + if (!this._enabled && !this._keyboardController) + return; + +-- +2.26.2 + diff --git a/SOURCES/0001-shellEntry-Determine-if-password-entry-from-content-.patch b/SOURCES/0001-shellEntry-Determine-if-password-entry-from-content-.patch new file mode 100644 index 0000000..4d5bbe1 --- /dev/null +++ b/SOURCES/0001-shellEntry-Determine-if-password-entry-from-content-.patch @@ -0,0 +1,91 @@ +From e6cd96a9f6a89f77ca0fab72aff8c56354b59f38 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 21 Aug 2019 15:01:34 -0400 +Subject: [PATCH 1/4] shellEntry: Determine if password entry from content + purpose not menu item + +Right now shellEntry decides whether or not it's a password entry based +on whether or not it has a "Show Text" context menu. + +That's a little roundabout, and gets in the way off providing lockdown +that disables the menu. + +This commit changes shellEntry to base whether or not it's a password +entry from it's input content purpose instead of from the presence +or absence of a context menu. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/687 +--- + js/ui/shellEntry.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index 53bd1daa1..cac4ec9c2 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -14,61 +14,61 @@ var EntryMenu = class extends PopupMenu.PopupMenu { + + this._entry = entry; + this._clipboard = St.Clipboard.get_default(); + + // Populate menu + let item; + item = new PopupMenu.PopupMenuItem(_("Copy")); + item.connect('activate', this._onCopyActivated.bind(this)); + this.addMenuItem(item); + this._copyItem = item; + + item = new PopupMenu.PopupMenuItem(_("Paste")); + item.connect('activate', this._onPasteActivated.bind(this)); + this.addMenuItem(item); + this._pasteItem = item; + + this._passwordItem = null; + + Main.uiGroup.add_actor(this.actor); + this.actor.hide(); + } + + _makePasswordItem() { + let item = new PopupMenu.PopupMenuItem(''); + item.connect('activate', this._onPasswordActivated.bind(this)); + this.addMenuItem(item); + this._passwordItem = item; + } + + get isPassword() { +- return this._passwordItem != null; ++ return this._entry.input_purpose == Clutter.InputContentPurpose.PASSWORD; + } + + set isPassword(v) { + if (v == this.isPassword) + return; + + if (v) { + this._makePasswordItem(); + this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD; + } else { + this._passwordItem.destroy(); + this._passwordItem = null; + this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL; + } + } + + open(animate) { + this._updatePasteItem(); + this._updateCopyItem(); + if (this._passwordItem) + this._updatePasswordItem(); + + super.open(animate); + this._entry.add_style_pseudo_class('focus'); + + let direction = St.DirectionType.TAB_FORWARD; + if (!this.actor.navigate_focus(null, direction, false)) + this.actor.grab_key_focus(); + } + +-- +2.27.0 + diff --git a/SOURCES/0001-shellEntry-Disconnect-handler-on-destroy.patch b/SOURCES/0001-shellEntry-Disconnect-handler-on-destroy.patch new file mode 100644 index 0000000..abf32bd --- /dev/null +++ b/SOURCES/0001-shellEntry-Disconnect-handler-on-destroy.patch @@ -0,0 +1,36 @@ +From fd8c4dc073b121b1093d68472cac3292d2c6605c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 14 Jun 2021 17:59:39 +0200 +Subject: [PATCH] shellEntry: Disconnect handler on destroy + +Actors will get unmapped on destroy, so unless we disconnect from +the notify::mapped signal, the handler will run one last time and +try to access methods/properties on the invalidated actor. +--- + js/ui/shellEntry.js | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index 4a30b22f7..53bd1daa1 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -186,7 +186,7 @@ class CapsLockWarning extends St.Label { + this._keymap = Clutter.get_default_backend().get_keymap(); + this._stateChangedId = 0; + +- this.connect('notify::mapped', () => { ++ const mappedId = this.connect('notify::mapped', () => { + if (this.is_mapped()) { + this._stateChangedId = this._keymap.connect('state-changed', + () => this._sync(true)); +@@ -201,6 +201,7 @@ class CapsLockWarning extends St.Label { + this.connect('destroy', () => { + if (this._stateChangedId) + this._keymap.disconnect(this._stateChangedId); ++ this.disconnect(mappedId); + }); + } + +-- +2.31.1 + diff --git a/SOURCES/0001-theme-Update-window-preview-style.patch b/SOURCES/0001-theme-Update-window-preview-style.patch new file mode 100644 index 0000000..2871e60 --- /dev/null +++ b/SOURCES/0001-theme-Update-window-preview-style.patch @@ -0,0 +1,76 @@ +From c68fd3c94c6debdbf11020940c5a6aaee8bc230d Mon Sep 17 00:00:00 2001 +From: Feichtmeier +Date: Fri, 15 Mar 2019 14:41:55 +0100 +Subject: [PATCH] theme: Update window preview style + + - simplify the close button to use blue, lighter blue and darker blue + solid disks for normal, hover and active states + + - use a milky, transparent white border for the hover effect of the border + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/461 +--- + data/theme/gnome-shell-sass/_common.scss | 29 ++++++++++++------------ + 1 file changed, 14 insertions(+), 15 deletions(-) + +diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss +index 9e0751c8c..8bf368f6e 100644 +--- a/data/theme/gnome-shell-sass/_common.scss ++++ b/data/theme/gnome-shell-sass/_common.scss +@@ -1164,25 +1164,23 @@ StScrollBar { + //close buttons + + .window-close { +- background-color: white; ++ background-color: $selected_bg_color; ++ color: white; + border-radius: 24px; +- border: 4px solid $selected_bg_color; +- box-shadow: inset 0 -4px 0 0 transparentize($selected_bg_color, 0.5); +- color: $selected_bg_color; ++ border: 2px solid $selected_bg_color; + height: 24px; + width: 24px; +- -shell-close-overlap: 14px; ++ -shell-close-overlap: 11px; ++ box-shadow: -1px 1px 5px 0px transparentize(black, 0.5); + + &:hover { +- background-color: $selected_bg_color; +- border-color: white; +- color: white; ++ background-color: lighten($selected_bg_color, 5%); ++ border-color: lighten($selected_bg_color, 5%); + } + + &:active { +- background-color: mix(white, $selected_bg_color, 75%); +- border-color: $selected_bg_color; +- color: $selected_bg_color; ++ background-color: darken($selected_bg_color, 5%); ++ border-color: darken($selected_bg_color, 5%); + } + } + +@@ -1247,13 +1245,14 @@ StScrollBar { + } + + .window-clone-border { +- border: 4px solid $selected_bg_color; +- border-radius: 4px; ++ $_bg: transparentize(white, 0.65); ++ border: 5px solid $_bg; ++ border-radius: 6px; + // For window decorations with round corners we can't match + // the exact shape when the window is scaled. So apply a shadow + // to fix that case +- box-shadow: inset 0px 0px 0px 1px $selected_bg_color; +- } ++ box-shadow: inset 0 0 0 1px $_bg; ++} + .window-caption { + spacing: 25px; + color: $selected_fg_color; +-- +2.31.1 + diff --git a/SOURCES/0002-extensionSystem-Get-rid-of-_enabled-boolean-optimiza.patch b/SOURCES/0002-extensionSystem-Get-rid-of-_enabled-boolean-optimiza.patch new file mode 100644 index 0000000..5dba07a --- /dev/null +++ b/SOURCES/0002-extensionSystem-Get-rid-of-_enabled-boolean-optimiza.patch @@ -0,0 +1,237 @@ +From b70cf463e08bff43b242b851fc7c79244f54e76b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Aug 2021 13:25:57 -0400 +Subject: [PATCH 2/4] extensionSystem: Get rid of _enabled boolean optimization + +At the moment a session mode either allows extensions or it doesn't. +If it allows extensions, then the entire available list of +configured extensions get enabled as soon as the session mode is +entered. + +Since enabling or disabling extensions is an all or nothing situation, +the code tracks whether extensions are already enabled when entering +the session mode, and if so, avoids iterating through the extension list +needlessly. It does this using a boolean named _enabled. + +In the future, the extensions themselves will be given some say on +whether or not they should be enabled in a given session mode. This +means, the configured extension list may contain extensions that +shouldn't be enabled for a given session mode, and the _enabled boolean +will no longer be appropriated. + +This commit drops the _enabled boolean optimization. +--- + js/ui/extensionSystem.js | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js +index 77929f2a6..05630ed54 100644 +--- a/js/ui/extensionSystem.js ++++ b/js/ui/extensionSystem.js +@@ -1,53 +1,52 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + + const { GLib, Gio, GObject, St } = imports.gi; + const Signals = imports.signals; + + const ExtensionDownloader = imports.ui.extensionDownloader; + const ExtensionUtils = imports.misc.extensionUtils; + const FileUtils = imports.misc.fileUtils; + const Main = imports.ui.main; + const MessageTray = imports.ui.messageTray; + + const { ExtensionState, ExtensionType } = ExtensionUtils; + + const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; + const DISABLE_USER_EXTENSIONS_KEY = 'disable-user-extensions'; + const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation'; + + const UPDATE_CHECK_TIMEOUT = 24 * 60 * 60; // 1 day in seconds + + var ExtensionManager = class { + constructor() { + this._initted = false; +- this._enabled = false; + this._updateNotified = false; + + this._extensions = new Map(); + this._enabledExtensions = []; + this._extensionOrder = []; + + Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + } + + init() { + this._installExtensionUpdates(); + this._sessionUpdated(); + + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, UPDATE_CHECK_TIMEOUT, () => { + ExtensionDownloader.checkForUpdates(); + return GLib.SOURCE_CONTINUE; + }); + ExtensionDownloader.checkForUpdates(); + } + + lookup(uuid) { + return this._extensions.get(uuid); + } + + getUuids() { + return [...this._extensions.keys()]; + } + + _callExtensionDisable(uuid) { + let extension = this.lookup(uuid); +@@ -375,63 +374,60 @@ var ExtensionManager = class { + let hasError = + extension.state == ExtensionState.ERROR || + extension.state == ExtensionState.OUT_OF_DATE; + + let isMode = this._getModeExtensions().includes(extension.uuid); + let modeOnly = global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY); + + extension.canChange = + !hasError && + global.settings.is_writable(ENABLED_EXTENSIONS_KEY) && + (isMode || !modeOnly); + } + + _getEnabledExtensions() { + let extensions = this._getModeExtensions(); + + if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY)) + return extensions; + + return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY)); + } + + _onUserExtensionsEnabledChanged() { + this._onEnabledExtensionsChanged(); + this._onSettingsWritableChanged(); + } + + _onEnabledExtensionsChanged() { + let newEnabledExtensions = this._getEnabledExtensions(); + +- if (!this._enabled) +- return; +- + // Find and enable all the newly enabled extensions: UUIDs found in the + // new setting, but not in the old one. + newEnabledExtensions.filter( + uuid => !this._enabledExtensions.includes(uuid) + ).forEach(uuid => { + this._callExtensionEnable(uuid); + }); + + // Find and disable all the newly disabled extensions: UUIDs found in the + // old setting, but not in the new one. + this._enabledExtensions.filter( + item => !newEnabledExtensions.includes(item) + ).forEach(uuid => { + this._callExtensionDisable(uuid); + }); + + this._enabledExtensions = newEnabledExtensions; + } + + _onSettingsWritableChanged() { + for (let extension of this._extensions.values()) { + this._updateCanChange(extension); + this.emit('extension-state-changed', extension); + } + } + + _onVersionValidationChanged() { + // we want to reload all extensions, but only enable + // extensions when allowed by the sessionMode, so + // temporarily disable them all +@@ -482,85 +478,76 @@ var ExtensionManager = class { + + this._enabledExtensions = this._getEnabledExtensions(); + + let perUserDir = Gio.File.new_for_path(global.userdatadir); + FileUtils.collectFromDatadirs('extensions', true, (dir, info) => { + let fileType = info.get_file_type(); + if (fileType != Gio.FileType.DIRECTORY) + return; + let uuid = info.get_name(); + let existing = this.lookup(uuid); + if (existing) { + log(`Extension ${uuid} already installed in ${existing.path}. ${dir.get_path()} will not be loaded`); + return; + } + + let extension; + let type = dir.has_prefix(perUserDir) + ? ExtensionType.PER_USER + : ExtensionType.SYSTEM; + try { + extension = this.createExtensionObject(uuid, dir, type); + } catch (e) { + logError(e, `Could not load extension ${uuid}`); + return; + } + this.loadExtension(extension); + }); + } + + _enableAllExtensions() { +- if (this._enabled) +- return; +- + if (!this._initted) { + this._loadExtensions(); + this._initted = true; + } else { + this._enabledExtensions.forEach(uuid => { + this._callExtensionEnable(uuid); + }); + } +- this._enabled = true; + } + + _disableAllExtensions() { +- if (!this._enabled) +- return; +- + if (this._initted) { + this._extensionOrder.slice().reverse().forEach(uuid => { + this._callExtensionDisable(uuid); + }); + } +- +- this._enabled = false; + } + + _sessionUpdated() { + // For now sessionMode.allowExtensions controls extensions from both the + // 'enabled-extensions' preference and the sessionMode.enabledExtensions + // property; it might make sense to make enabledExtensions independent + // from allowExtensions in the future + if (Main.sessionMode.allowExtensions) { + // Take care of added or removed sessionMode extensions + this._onEnabledExtensionsChanged(); + this._enableAllExtensions(); + } else { + this._disableAllExtensions(); + } + } + }; + Signals.addSignalMethods(ExtensionManager.prototype); + + class ExtensionUpdateSource extends MessageTray.Source { + constructor() { + const appSys = Shell.AppSystem.get_default(); + this._app = appSys.lookup_app('gnome-shell-extension-prefs.desktop'); + + super(this._app.get_name()); + } + + getIcon() { + return this._app.app_info.get_icon(); + } + +-- +2.27.0 + diff --git a/SOURCES/0002-shellEntry-Give-password-menu-item-text-when-it-s-cr.patch b/SOURCES/0002-shellEntry-Give-password-menu-item-text-when-it-s-cr.patch new file mode 100644 index 0000000..55b3c6f --- /dev/null +++ b/SOURCES/0002-shellEntry-Give-password-menu-item-text-when-it-s-cr.patch @@ -0,0 +1,92 @@ +From de7df6c7248c39d7cce1c70485df72a398da92a3 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 21 Aug 2019 15:48:33 -0400 +Subject: [PATCH 2/4] shellEntry: Give password menu item text when it's + created + +At the moment, the "Show Text" menu item is only given its text +at the time the menu is opened. This is because the text might +be "Hide Text" or "Show Text" depending on state, so the text +is set up lazily. + +That behavior means the menu item can't get added after the +menu is already shown, which is something we'ree going to need +in the future to support lockdown of the "Show Text" item. + +This commit ensures the menu item is given text when it's first +created, in addition to when the menu is opened. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/687 +--- + js/ui/shellEntry.js | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index cac4ec9c2..603a9c64a 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -11,60 +11,61 @@ const Tweener = imports.ui.tweener; + var EntryMenu = class extends PopupMenu.PopupMenu { + constructor(entry) { + super(entry, 0, St.Side.TOP); + + this._entry = entry; + this._clipboard = St.Clipboard.get_default(); + + // Populate menu + let item; + item = new PopupMenu.PopupMenuItem(_("Copy")); + item.connect('activate', this._onCopyActivated.bind(this)); + this.addMenuItem(item); + this._copyItem = item; + + item = new PopupMenu.PopupMenuItem(_("Paste")); + item.connect('activate', this._onPasteActivated.bind(this)); + this.addMenuItem(item); + this._pasteItem = item; + + this._passwordItem = null; + + Main.uiGroup.add_actor(this.actor); + this.actor.hide(); + } + + _makePasswordItem() { + let item = new PopupMenu.PopupMenuItem(''); + item.connect('activate', this._onPasswordActivated.bind(this)); + this.addMenuItem(item); + this._passwordItem = item; ++ this._updatePasswordItem(); + } + + get isPassword() { + return this._entry.input_purpose == Clutter.InputContentPurpose.PASSWORD; + } + + set isPassword(v) { + if (v == this.isPassword) + return; + + if (v) { + this._makePasswordItem(); + this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD; + } else { + this._passwordItem.destroy(); + this._passwordItem = null; + this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL; + } + } + + open(animate) { + this._updatePasteItem(); + this._updateCopyItem(); + if (this._passwordItem) + this._updatePasswordItem(); + + super.open(animate); + this._entry.add_style_pseudo_class('focus'); + + let direction = St.DirectionType.TAB_FORWARD; +-- +2.27.0 + diff --git a/SOURCES/0003-extensionSystem-Allow-extensions-to-run-on-the-login.patch b/SOURCES/0003-extensionSystem-Allow-extensions-to-run-on-the-login.patch new file mode 100644 index 0000000..c2dcedb --- /dev/null +++ b/SOURCES/0003-extensionSystem-Allow-extensions-to-run-on-the-login.patch @@ -0,0 +1,393 @@ +From 7300ae2eac743fa06f40f6459ac8fbf739ab28ea Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Aug 2021 15:03:50 -0400 +Subject: [PATCH 3/4] extensionSystem: Allow extensions to run on the login + screen + +At the moment it's not realy possible to extend the login screen to do +things it doesn't have built-in support for. This means in order +to support niche use cases, those cases have to change the main +code base. For instance, oVirt and Vmware deployments want to be able +to automaticaly log in guest VMs when a user pre-authenticates through a +console on a management host. To support those use cases, we added +code to the login screen directly, even though most machines will never +be associated with oVirt or Vmware management hosts. + +We also get requests from e.g. government users that need certain features +at the login screen that wouldn't get used much outside of government +deployments. For instance, we've gotten requests that a machine contains +prominently displays that it has "Top Secret" information. + +All of these use cases seem like they would better handled via +extensions that could be installed in the specific deployments. The +problem is extensions only run in the user session, and get +disabled at the login screen automatically. + +This commit changes that. Now extensions can specify in their metadata +via a new sessionModes property, which modes that want to run in. For +backward compatibility, if an extension doesn't specify which session +modes it works in, its assumed the extension only works in the user +session. +--- + js/ui/extensionSystem.js | 43 ++++++++++++++++++++++++++++++++++++---- + 1 file changed, 39 insertions(+), 4 deletions(-) + +diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js +index 05630ed54..dfe82821e 100644 +--- a/js/ui/extensionSystem.js ++++ b/js/ui/extensionSystem.js +@@ -21,119 +21,147 @@ var ExtensionManager = class { + constructor() { + this._initted = false; + this._updateNotified = false; + + this._extensions = new Map(); + this._enabledExtensions = []; + this._extensionOrder = []; + + Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); + } + + init() { + this._installExtensionUpdates(); + this._sessionUpdated(); + + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, UPDATE_CHECK_TIMEOUT, () => { + ExtensionDownloader.checkForUpdates(); + return GLib.SOURCE_CONTINUE; + }); + ExtensionDownloader.checkForUpdates(); + } + + lookup(uuid) { + return this._extensions.get(uuid); + } + + getUuids() { + return [...this._extensions.keys()]; + } + ++ _extensionSupportsSessionMode(uuid) { ++ let extension = this.lookup(uuid); ++ ++ if (!extension) ++ return false; ++ ++ if (extension.sessionModes.includes(Main.sessionMode.currentMode)) ++ return true; ++ ++ if (extension.sessionModes.includes(Main.sessionMode.parentMode)) ++ return true; ++ ++ return false; ++ } ++ ++ _sessionModeCanUseExtension(uuid) { ++ if (!Main.sessionMode.allowExtensions) ++ return false; ++ ++ if (!this._extensionSupportsSessionMode(uuid)) ++ return false; ++ ++ return true; ++ } ++ + _callExtensionDisable(uuid) { + let extension = this.lookup(uuid); + if (!extension) + return; + + if (extension.state != ExtensionState.ENABLED) + return; + + // "Rebase" the extension order by disabling and then enabling extensions + // in order to help prevent conflicts. + + // Example: + // order = [A, B, C, D, E] + // user disables C + // this should: disable E, disable D, disable C, enable D, enable E + + let orderIdx = this._extensionOrder.indexOf(uuid); + let order = this._extensionOrder.slice(orderIdx + 1); + let orderReversed = order.slice().reverse(); + + for (let i = 0; i < orderReversed.length; i++) { + let uuid = orderReversed[i]; + try { + this.lookup(uuid).stateObj.disable(); + } catch (e) { + this.logExtensionError(uuid, e); + } + } + + if (extension.stylesheet) { + let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); + theme.unload_stylesheet(extension.stylesheet); + delete extension.stylesheet; + } + + try { + extension.stateObj.disable(); + } catch(e) { + this.logExtensionError(uuid, e); + } + + for (let i = 0; i < order.length; i++) { + let uuid = order[i]; + try { + this.lookup(uuid).stateObj.enable(); + } catch (e) { + this.logExtensionError(uuid, e); + } + } + + this._extensionOrder.splice(orderIdx, 1); + + if (extension.state != ExtensionState.ERROR) { + extension.state = ExtensionState.DISABLED; + this.emit('extension-state-changed', extension); + } + } + + _callExtensionEnable(uuid) { ++ if (!this._sessionModeCanUseExtension(uuid)) ++ return; ++ + let extension = this.lookup(uuid); + if (!extension) + return; + + if (extension.state == ExtensionState.INITIALIZED) + this._callExtensionInit(uuid); + + if (extension.state != ExtensionState.DISABLED) + return; + + this._extensionOrder.push(uuid); + + let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css']; + let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); + for (let i = 0; i < stylesheetNames.length; i++) { + try { + let stylesheetFile = extension.dir.get_child(stylesheetNames[i]); + theme.load_stylesheet(stylesheetFile); + extension.stylesheet = stylesheetFile; + break; + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) + continue; // not an error + log(`Failed to load stylesheet for extension ${uuid}: ${e.message}`); + return; + } + } + + try { + extension.stateObj.enable(); +@@ -231,61 +259,62 @@ var ExtensionManager = class { + throw new Error(`Failed to load metadata.json: ${e}`); + } + let meta; + try { + meta = JSON.parse(metadataContents); + } catch (e) { + throw new Error(`Failed to parse metadata.json: ${e}`); + } + + let requiredProperties = ['uuid', 'name', 'description', 'shell-version']; + for (let i = 0; i < requiredProperties.length; i++) { + let prop = requiredProperties[i]; + if (!meta[prop]) { + throw new Error(`missing "${prop}" property in metadata.json`); + } + } + + if (uuid != meta.uuid) { + throw new Error(`uuid "${meta.uuid}" from metadata.json does not match directory name "${uuid}"`); + } + + let extension = { + metadata: meta, + uuid: meta.uuid, + type, + dir, + path: dir.get_path(), + error: '', + hasPrefs: dir.get_child('prefs.js').query_exists(null), + hasUpdate: false, +- canChange: false ++ canChange: false, ++ sessionModes: meta['session-modes'] ? meta['session-modes'] : [ 'user' ], + }; + this._extensions.set(uuid, extension); + + return extension; + } + + loadExtension(extension) { + // Default to error, we set success as the last step + extension.state = ExtensionState.ERROR; + + let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY); + + if (checkVersion && ExtensionUtils.isOutOfDate(extension)) { + extension.state = ExtensionState.OUT_OF_DATE; + } else { + let enabled = this._enabledExtensions.includes(extension.uuid); + if (enabled) { + if (!this._callExtensionInit(extension.uuid)) + return; + if (extension.state == ExtensionState.DISABLED) + this._callExtensionEnable(extension.uuid); + } else { + extension.state = ExtensionState.INITIALIZED; + } + } + + this._updateCanChange(extension); + this.emit('extension-state-changed', extension); + } + +@@ -296,60 +325,63 @@ var ExtensionManager = class { + this._callExtensionDisable(extension.uuid); + + extension.state = ExtensionState.UNINSTALLED; + this.emit('extension-state-changed', extension); + + this._extensions.delete(extension.uuid); + return true; + } + + reloadExtension(oldExtension) { + // Grab the things we'll need to pass to createExtensionObject + // to reload it. + let { uuid: uuid, dir: dir, type: type } = oldExtension; + + // Then unload the old extension. + this.unloadExtension(oldExtension); + + // Now, recreate the extension and load it. + let newExtension; + try { + newExtension = this.createExtensionObject(uuid, dir, type); + } catch (e) { + this.logExtensionError(uuid, e); + return; + } + + this.loadExtension(newExtension); + } + + _callExtensionInit(uuid) { ++ if (!this._sessionModeCanUseExtension(uuid)) ++ return false; ++ + let extension = this.lookup(uuid); + let dir = extension.dir; + + if (!extension) + throw new Error("Extension was not properly created. Call loadExtension first"); + + let extensionJs = dir.get_child('extension.js'); + if (!extensionJs.query_exists(null)) { + this.logExtensionError(uuid, new Error('Missing extension.js')); + return false; + } + + let extensionModule; + let extensionState = null; + + ExtensionUtils.installImporter(extension); + try { + extensionModule = extension.imports.extension; + } catch(e) { + this.logExtensionError(uuid, e); + return false; + } + + if (extensionModule.init) { + try { + extensionState = extensionModule.init(extension); + } catch (e) { + this.logExtensionError(uuid, e); + return false; + } +@@ -377,69 +409,72 @@ var ExtensionManager = class { + + let isMode = this._getModeExtensions().includes(extension.uuid); + let modeOnly = global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY); + + extension.canChange = + !hasError && + global.settings.is_writable(ENABLED_EXTENSIONS_KEY) && + (isMode || !modeOnly); + } + + _getEnabledExtensions() { + let extensions = this._getModeExtensions(); + + if (global.settings.get_boolean(DISABLE_USER_EXTENSIONS_KEY)) + return extensions; + + return extensions.concat(global.settings.get_strv(ENABLED_EXTENSIONS_KEY)); + } + + _onUserExtensionsEnabledChanged() { + this._onEnabledExtensionsChanged(); + this._onSettingsWritableChanged(); + } + + _onEnabledExtensionsChanged() { + let newEnabledExtensions = this._getEnabledExtensions(); + + // Find and enable all the newly enabled extensions: UUIDs found in the + // new setting, but not in the old one. + newEnabledExtensions.filter( +- uuid => !this._enabledExtensions.includes(uuid) ++ uuid => !this._enabledExtensions.includes(uuid) && ++ this._extensionSupportsSessionMode(uuid) + ).forEach(uuid => { + this._callExtensionEnable(uuid); + }); + + // Find and disable all the newly disabled extensions: UUIDs found in the +- // old setting, but not in the new one. ++ // old setting, but not in the new one, and extensions that don't work with ++ // the current session mode. + this._enabledExtensions.filter( +- item => !newEnabledExtensions.includes(item) ++ item => !newEnabledExtensions.includes(item) || ++ !this._extensionSupportsSessionMode(item) + ).forEach(uuid => { + this._callExtensionDisable(uuid); + }); + + this._enabledExtensions = newEnabledExtensions; + } + + _onSettingsWritableChanged() { + for (let extension of this._extensions.values()) { + this._updateCanChange(extension); + this.emit('extension-state-changed', extension); + } + } + + _onVersionValidationChanged() { + // we want to reload all extensions, but only enable + // extensions when allowed by the sessionMode, so + // temporarily disable them all + this._enabledExtensions = []; + + // The loop modifies the extensions map, so iterate over a copy + let extensions = [...this._extensions.values()]; + for (let extension of extensions) + this.reloadExtension(extension); + this._enabledExtensions = this._getEnabledExtensions(); + + if (Main.sessionMode.allowExtensions) { + this._enabledExtensions.forEach(uuid => { + this._callExtensionEnable(uuid); + }); +-- +2.27.0 + diff --git a/SOURCES/0003-shellEntry-Handle-password-item-from-dedication-func.patch b/SOURCES/0003-shellEntry-Handle-password-item-from-dedication-func.patch new file mode 100644 index 0000000..46afc47 --- /dev/null +++ b/SOURCES/0003-shellEntry-Handle-password-item-from-dedication-func.patch @@ -0,0 +1,119 @@ +From 39cf97176e2a92506081ee151ea546e2c6cf213a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 21 Aug 2019 15:06:46 -0400 +Subject: [PATCH 3/4] shellEntry: Handle password item from dedication function + +At the moment, shellEntry handles creating and destroying its +"Show Text" password menu item directly from its isPassword +setter function. + +This commit moves that handling to a dedicated _resetPasswordItem +function, as prep work for adding lockdown support of the "Show Text" +menu item. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/687 +--- + js/ui/shellEntry.js | 23 +++++++++++++++++------ + 1 file changed, 17 insertions(+), 6 deletions(-) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index 603a9c64a..765cede06 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -14,76 +14,87 @@ var EntryMenu = class extends PopupMenu.PopupMenu { + + this._entry = entry; + this._clipboard = St.Clipboard.get_default(); + + // Populate menu + let item; + item = new PopupMenu.PopupMenuItem(_("Copy")); + item.connect('activate', this._onCopyActivated.bind(this)); + this.addMenuItem(item); + this._copyItem = item; + + item = new PopupMenu.PopupMenuItem(_("Paste")); + item.connect('activate', this._onPasteActivated.bind(this)); + this.addMenuItem(item); + this._pasteItem = item; + + this._passwordItem = null; + + Main.uiGroup.add_actor(this.actor); + this.actor.hide(); + } + + _makePasswordItem() { + let item = new PopupMenu.PopupMenuItem(''); + item.connect('activate', this._onPasswordActivated.bind(this)); + this.addMenuItem(item); + this._passwordItem = item; + this._updatePasswordItem(); + } + ++ _resetPasswordItem() { ++ if (!this.isPassword) { ++ if (this._passwordItem) { ++ this._passwordItem.destroy(); ++ this._passwordItem = null; ++ } ++ this._entry.clutter_text.set_password_char('\u25cf'); ++ } else { ++ if (!this._passwordItem) ++ this._makePasswordItem(); ++ } ++ } ++ + get isPassword() { + return this._entry.input_purpose == Clutter.InputContentPurpose.PASSWORD; + } + + set isPassword(v) { + if (v == this.isPassword) + return; + +- if (v) { +- this._makePasswordItem(); ++ if (v) + this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD; +- } else { +- this._passwordItem.destroy(); +- this._passwordItem = null; ++ else + this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL; +- } ++ ++ this._resetPasswordItem(); + } + + open(animate) { + this._updatePasteItem(); + this._updateCopyItem(); + if (this._passwordItem) + this._updatePasswordItem(); + + super.open(animate); + this._entry.add_style_pseudo_class('focus'); + + let direction = St.DirectionType.TAB_FORWARD; + if (!this.actor.navigate_focus(null, direction, false)) + this.actor.grab_key_focus(); + } + + _updateCopyItem() { + let selection = this._entry.clutter_text.get_selection(); + this._copyItem.setSensitive(!this._entry.clutter_text.password_char && + selection && selection != ''); + } + + _updatePasteItem() { + this._clipboard.get_text(St.ClipboardType.CLIPBOARD, + (clipboard, text) => { + this._pasteItem.setSensitive(text && text != ''); + }); + } + + _updatePasswordItem() { +-- +2.27.0 + diff --git a/SOURCES/0004-sessionMode-Allow-extensions-at-the-login-and-unlock.patch b/SOURCES/0004-sessionMode-Allow-extensions-at-the-login-and-unlock.patch new file mode 100644 index 0000000..f19fecd --- /dev/null +++ b/SOURCES/0004-sessionMode-Allow-extensions-at-the-login-and-unlock.patch @@ -0,0 +1,116 @@ +From 5fad989ca773f9e0ff6fdbeb0cb7c9cb70cc6148 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 10 Aug 2021 15:31:00 -0400 +Subject: [PATCH 4/4] sessionMode: Allow extensions at the login and unlock + screens + +Now extensions can specify which session modes they work in, +but specifying the login screen or unlock screen session modes in +an extensions metadata still won't work, because those session +modes disallow extensions. + +This commit fixes that. +--- + js/ui/sessionMode.js | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js +index fa7f83416..8d8ce1a64 100644 +--- a/js/ui/sessionMode.js ++++ b/js/ui/sessionMode.js +@@ -12,89 +12,92 @@ const Config = imports.misc.config; + const DEFAULT_MODE = 'restrictive'; + + const _modes = { + 'restrictive': { + parentMode: null, + stylesheetName: 'gnome-shell.css', + hasOverview: false, + showCalendarEvents: false, + allowSettings: false, + allowExtensions: false, + allowScreencast: false, + enabledExtensions: [], + hasRunDialog: false, + hasWorkspaces: false, + hasWindows: false, + hasNotifications: false, + isLocked: false, + isGreeter: false, + isPrimary: false, + unlockDialog: null, + components: [], + panel: { + left: [], + center: [], + right: [] + }, + panelStyle: null + }, + + 'gdm': { ++ allowExtensions: true, + hasNotifications: true, + isGreeter: true, + isPrimary: true, + unlockDialog: imports.gdm.loginDialog.LoginDialog, + components: Config.HAVE_NETWORKMANAGER + ? ['networkAgent', 'polkitAgent'] + : ['polkitAgent'], + panel: { + left: [], + center: ['dateMenu'], + right: ['a11y', 'keyboard', 'aggregateMenu'] + }, + panelStyle: 'login-screen' + }, + + 'lock-screen': { ++ allowExtensions: true, + isLocked: true, + isGreeter: undefined, + unlockDialog: undefined, + components: ['polkitAgent', 'telepathyClient'], + panel: { + left: [], + center: [], + right: ['aggregateMenu'] + }, + panelStyle: 'lock-screen' + }, + + 'unlock-dialog': { ++ allowExtensions: true, + isLocked: true, + unlockDialog: undefined, + components: ['polkitAgent', 'telepathyClient'], + panel: { + left: [], + center: [], + right: ['a11y', 'keyboard', 'aggregateMenu'] + }, + panelStyle: 'unlock-screen' + }, + + 'user': { + hasOverview: true, + showCalendarEvents: true, + allowSettings: true, + allowExtensions: true, + allowScreencast: true, + hasRunDialog: true, + hasWorkspaces: true, + hasWindows: true, + hasNotifications: true, + isLocked: false, + isPrimary: true, + unlockDialog: imports.ui.unlockDialog.UnlockDialog, + components: Config.HAVE_NETWORKMANAGER ? + ['networkAgent', 'polkitAgent', 'telepathyClient', + 'keyring', 'autorunManager', 'automountManager'] : + ['polkitAgent', 'telepathyClient', + 'keyring', 'autorunManager', 'automountManager'], + +-- +2.27.0 + diff --git a/SOURCES/0004-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch b/SOURCES/0004-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch new file mode 100644 index 0000000..8d8a7d7 --- /dev/null +++ b/SOURCES/0004-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch @@ -0,0 +1,116 @@ +From ee64cd773bdeef845d02dc84063f926d77090dec Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 21 Aug 2019 15:06:46 -0400 +Subject: [PATCH 4/4] shellEntry: Support lockdown of "Show Text" menu in + password entries + +Some deployments require being able to prevent users from showing +the password they're currently typing. + +This commit adds support for that kind of lockdown. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/687 +--- + js/ui/shellEntry.js | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index 765cede06..c45e4545a 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -1,81 +1,89 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +-const { Clutter, GObject, Pango, Shell, St } = imports.gi; ++const { Clutter, Gio, GObject, Pango, Shell, St } = imports.gi; + + const BoxPointer = imports.ui.boxpointer; + const Main = imports.ui.main; + const Params = imports.misc.params; + const PopupMenu = imports.ui.popupMenu; + const Tweener = imports.ui.tweener; + ++const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; ++const DISABLE_SHOW_PASSWORD_KEY = 'disable-show-password'; ++ + var EntryMenu = class extends PopupMenu.PopupMenu { + constructor(entry) { + super(entry, 0, St.Side.TOP); + ++ this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA }); ++ this._lockdownSettings.connect('changed::' + DISABLE_SHOW_PASSWORD_KEY, this._resetPasswordItem.bind(this)); ++ + this._entry = entry; + this._clipboard = St.Clipboard.get_default(); + + // Populate menu + let item; + item = new PopupMenu.PopupMenuItem(_("Copy")); + item.connect('activate', this._onCopyActivated.bind(this)); + this.addMenuItem(item); + this._copyItem = item; + + item = new PopupMenu.PopupMenuItem(_("Paste")); + item.connect('activate', this._onPasteActivated.bind(this)); + this.addMenuItem(item); + this._pasteItem = item; + + this._passwordItem = null; + + Main.uiGroup.add_actor(this.actor); + this.actor.hide(); + } + + _makePasswordItem() { + let item = new PopupMenu.PopupMenuItem(''); + item.connect('activate', this._onPasswordActivated.bind(this)); + this.addMenuItem(item); + this._passwordItem = item; + this._updatePasswordItem(); + } + + _resetPasswordItem() { +- if (!this.isPassword) { ++ let passwordDisabled = this._lockdownSettings.get_boolean(DISABLE_SHOW_PASSWORD_KEY); ++ ++ if (!this.isPassword || passwordDisabled) { + if (this._passwordItem) { + this._passwordItem.destroy(); + this._passwordItem = null; + } + this._entry.clutter_text.set_password_char('\u25cf'); +- } else { ++ } else if (this.isPassword && !passwordDisabled) { + if (!this._passwordItem) + this._makePasswordItem(); + } + } + + get isPassword() { + return this._entry.input_purpose == Clutter.InputContentPurpose.PASSWORD; + } + + set isPassword(v) { + if (v == this.isPassword) + return; + + if (v) + this._entry.input_purpose = Clutter.InputContentPurpose.PASSWORD; + else + this._entry.input_purpose = Clutter.InputContentPurpose.NORMAL; + + this._resetPasswordItem(); + } + + open(animate) { + this._updatePasteItem(); + this._updateCopyItem(); + if (this._passwordItem) + this._updatePasswordItem(); + + super.open(animate); + this._entry.add_style_pseudo_class('focus'); + +-- +2.27.0 + diff --git a/SOURCES/0005-shellEntry-Only-mask-text-in-password-entries.patch b/SOURCES/0005-shellEntry-Only-mask-text-in-password-entries.patch new file mode 100644 index 0000000..a87a327 --- /dev/null +++ b/SOURCES/0005-shellEntry-Only-mask-text-in-password-entries.patch @@ -0,0 +1,36 @@ +From 7a264550c5f3a98b1786b1a75cff01cde1d084eb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 29 Jul 2021 17:17:43 +0200 +Subject: [PATCH 5/5] shellEntry: Only mask text in password entries + +When "Show Text" is locked down, we not only remove the corresponding +menu item, but also make sure the password is masked. + +Except that the current code is too eager, and masks the text in +any entries. +--- + js/ui/shellEntry.js | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js +index c45e4545a..64b389050 100644 +--- a/js/ui/shellEntry.js ++++ b/js/ui/shellEntry.js +@@ -55,11 +55,13 @@ var EntryMenu = class extends PopupMenu.PopupMenu { + this._passwordItem.destroy(); + this._passwordItem = null; + } +- this._entry.clutter_text.set_password_char('\u25cf'); + } else if (this.isPassword && !passwordDisabled) { + if (!this._passwordItem) + this._makePasswordItem(); + } ++ ++ if (this.isPassword && passwordDisabled) ++ this._entry.clutter_text.set_password_char('\u25cf'); + } + + get isPassword() { +-- +2.31.1 + diff --git a/SOURCES/extension-updates.patch b/SOURCES/extension-updates.patch index fb2dbcd..acc7826 100644 --- a/SOURCES/extension-updates.patch +++ b/SOURCES/extension-updates.patch @@ -3808,4 +3808,3 @@ index 0bd77e125..1e6f5340a 100644 -- 2.29.2 - diff --git a/SOURCES/fix-login-lock-screen.patch b/SOURCES/fix-login-lock-screen.patch new file mode 100644 index 0000000..f714a38 --- /dev/null +++ b/SOURCES/fix-login-lock-screen.patch @@ -0,0 +1,161 @@ +From 214c4f390faa40199c03a80594313760ffe9c5a6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 20 Sep 2019 13:17:40 +0200 +Subject: [PATCH 1/2] unlockDialog: Use inheritance instead of composition + +The screen shield creates the unlock dialog based on the session mode. + +However since commit 0c0d76f7d6990 turned LoginDialog into an actor +subclass (while UnlockDialog kept using the delegate pattern), it is +no longer possible to handle both objects the same way without warnings. + +Allow this again by turning UnlockDialog into an actor subclass as well. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/736 +--- + js/ui/unlockDialog.js | 46 ++++++++++++++++++++++++------------------- + 1 file changed, 26 insertions(+), 20 deletions(-) + +diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js +index 4b0470f4b..55abb652d 100644 +--- a/js/ui/unlockDialog.js ++++ b/js/ui/unlockDialog.js +@@ -1,8 +1,7 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + + const { AccountsService, Atk, Clutter, +- Gdm, Gio, GLib, Meta, Shell, St } = imports.gi; +-const Signals = imports.signals; ++ Gdm, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; + + const Layout = imports.ui.layout; + const Main = imports.ui.main; +@@ -12,15 +11,19 @@ const AuthPrompt = imports.gdm.authPrompt; + // The timeout before going back automatically to the lock screen (in seconds) + const IDLE_TIMEOUT = 2 * 60; + +-var UnlockDialog = class { +- constructor(parentActor) { +- this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW, +- style_class: 'login-dialog', +- layout_manager: new Clutter.BoxLayout(), +- visible: false }); ++var UnlockDialog = GObject.registerClass({ ++ Signals: { 'failed': {} }, ++}, class UnlockDialog extends St.Widget { ++ _init(parentActor) { ++ super._init({ ++ accessible_role: Atk.Role.WINDOW, ++ style_class: 'login-dialog', ++ layout_manager: new Clutter.BoxLayout(), ++ visible: false, ++ }); + +- this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true })); +- parentActor.add_child(this.actor); ++ this.add_constraint(new Layout.MonitorConstraint({ primary: true })); ++ parentActor.add_child(this); + + this._userManager = AccountsService.UserManager.get_default(); + this._userName = GLib.get_user_name(); +@@ -31,7 +34,7 @@ var UnlockDialog = class { + y_align: Clutter.ActorAlign.CENTER, + x_expand: true, + y_expand: true }); +- this.actor.add_child(this._promptBox); ++ this.add_child(this._promptBox); + + this._gdmClient = new Gdm.Client(); + +@@ -70,10 +73,12 @@ var UnlockDialog = class { + this._authPrompt.reset(); + this._updateSensitivity(true); + +- Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic'); ++ Main.ctrlAltTabManager.addGroup(this, _("Unlock Window"), 'dialog-password-symbolic'); + + this._idleMonitor = Meta.IdleMonitor.get_core(); + this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, this._escape.bind(this)); ++ ++ this.connect('destroy', this._onDestroy.bind(this)); + } + + _updateSensitivity(sensitive) { +@@ -112,9 +117,8 @@ var UnlockDialog = class { + this._authPrompt.cancel(); + } + +- destroy() { ++ _onDestroy() { + this.popModal(); +- this.actor.destroy(); + + if (this._idleWatchId) { + this._idleMonitor.remove_watch(this._idleWatchId); +@@ -137,13 +141,16 @@ var UnlockDialog = class { + } + + open(timestamp) { +- this.actor.show(); ++ this.show(); + + if (this._isModal) + return true; + +- if (!Main.pushModal(this.actor, { timestamp: timestamp, +- actionMode: Shell.ActionMode.UNLOCK_SCREEN })) ++ let modalParams = { ++ timestamp, ++ actionMode: Shell.ActionMode.UNLOCK_SCREEN, ++ }; ++ if (!Main.pushModal(this, modalParams)) + return false; + + this._isModal = true; +@@ -153,9 +160,8 @@ var UnlockDialog = class { + + popModal(timestamp) { + if (this._isModal) { +- Main.popModal(this.actor, timestamp); ++ Main.popModal(this, timestamp); + this._isModal = false; + } + } +-}; +-Signals.addSignalMethods(UnlockDialog.prototype); ++}); +-- +2.31.1 + + +From cddeb2f4e38928e0d5e0f3a852961f639536aff3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 20 Sep 2019 13:14:40 +0200 +Subject: [PATCH 2/2] screenShield: Stop using deprecated actor property + +Both LoginDialog and UnlockDialog are now actor subclasses, so stop +using the deprecated actor delegate that will trigger a warning. + +https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/736 +--- + js/ui/screenShield.js | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js +index 2d0a429be..f97a9288a 100644 +--- a/js/ui/screenShield.js ++++ b/js/ui/screenShield.js +@@ -917,8 +917,8 @@ var ScreenShield = class { + this._lockScreenGroup.hide(); + + if (this._dialog) { +- this._dialog.actor.grab_key_focus(); +- this._dialog.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); ++ this._dialog.grab_key_focus(); ++ this._dialog.navigate_focus(null, St.DirectionType.TAB_FORWARD, false); + } + } + +-- +2.31.1 + diff --git a/SOURCES/gdm-networking.patch b/SOURCES/gdm-networking.patch new file mode 100644 index 0000000..4f5e2fc --- /dev/null +++ b/SOURCES/gdm-networking.patch @@ -0,0 +1,246 @@ +From ed0699886f49e5dd8d6ca9ffb60ba17cd76a810f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 7 Jun 2021 17:49:57 +0200 +Subject: [PATCH 1/5] status/network: Disable modem connection when windows + aren't allowed + +The item launches the corresponding Settings panel when activated, which +doesn't work when windows are disabled by the session mode. Rather than +failing silently, turn the item insensitive. +--- + js/ui/status/network.js | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/js/ui/status/network.js b/js/ui/status/network.js +index b3bb7589c..3ad7b04dd 100644 +--- a/js/ui/status/network.js ++++ b/js/ui/status/network.js +@@ -514,6 +514,10 @@ var NMDeviceModem = class extends NMConnectionDevice { + this._iconChanged(); + }); + } ++ ++ this._sessionUpdatedId = ++ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); ++ this._sessionUpdated(); + } + + get category() { +@@ -525,6 +529,10 @@ var NMDeviceModem = class extends NMConnectionDevice { + 'connect-3g', this._device.get_path()]); + } + ++ _sessionUpdated() { ++ this._autoConnectItem.sensitive = Main.sessionMode.hasWindows; ++ } ++ + destroy() { + if (this._operatorNameId) { + this._mobileDevice.disconnect(this._operatorNameId); +@@ -534,6 +542,10 @@ var NMDeviceModem = class extends NMConnectionDevice { + this._mobileDevice.disconnect(this._signalQualityId); + this._signalQualityId = 0; + } ++ if (this._sessionUpdatedId) { ++ Main.sessionMode.disconnect(this._sessionUpdatedId); ++ this._sessionUpdatedId = 0; ++ } + + super.destroy(); + } +-- +2.31.1 + + +From 59d52e1591e1522fff22320c657496ca978a7926 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 7 Jun 2021 18:28:32 +0200 +Subject: [PATCH 2/5] status/network: Only list wifi networks that can be + activated + +Setting up a connection for an Enterprise WPA(2) encrypted wireless +network requires Settings. That's not available when windows are +disabled via the session mode, so filter out affected entries. +--- + js/ui/status/network.js | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/js/ui/status/network.js b/js/ui/status/network.js +index 3ad7b04dd..c023022a7 100644 +--- a/js/ui/status/network.js ++++ b/js/ui/status/network.js +@@ -1,5 +1,5 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +-const { Clutter, Gio, GLib, GObject, NM, St } = imports.gi; ++const { Clutter, Gio, GLib, GObject, Meta, NM, St } = imports.gi; + const Mainloop = imports.mainloop; + const Signals = imports.signals; + +@@ -751,6 +751,11 @@ var NMWirelessDialog = class extends ModalDialog.ModalDialog { + this._scanTimeoutId = 0; + } + ++ if (this._syncVisibilityId) { ++ Meta.later_remove(this._syncVisibilityId); ++ this._syncVisibilityId = 0; ++ } ++ + super.destroy(); + } + +@@ -1081,9 +1086,31 @@ var NMWirelessDialog = class extends ModalDialog.ModalDialog { + this._itemBox.insert_child_at_index(network.item.actor, newPos); + } + ++ this._queueSyncItemVisibility(); + this._syncView(); + } + ++ _queueSyncItemVisibility() { ++ if (this._syncVisibilityId) ++ return; ++ ++ this._syncVisibilityId = Meta.later_add( ++ Meta.LaterType.BEFORE_REDRAW, ++ () => { ++ const { hasWindows } = Main.sessionMode; ++ const { WPA2_ENT, WPA_ENT } = NMAccessPointSecurity; ++ ++ for (const network of this._networks) { ++ const [firstAp] = network.accessPoints; ++ network.item.visible = ++ hasWindows || ++ network.connections.length > 0 || ++ (firstAp._secType !== WPA2_ENT && firstAp._secType !== WPA_ENT); ++ } ++ return GLib.SOURCE_REMOVE; ++ }); ++ } ++ + _accessPointRemoved(device, accessPoint) { + let res = this._findExistingNetwork(accessPoint); + +-- +2.31.1 + + +From 9d204cdb38bcfee214dbe0b0bf9c2073dc50fe93 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 8 Jun 2021 00:17:48 +0200 +Subject: [PATCH 3/5] status/network: Consider network-control action + +NetworkManager installs a `network-control` polkit action that can +be used to disallow network configuration, except that we happily +ignore it. Add it to the conditions that turn a network section +insensitive. +--- + js/ui/status/network.js | 20 +++++++++++++++++--- + 1 file changed, 17 insertions(+), 3 deletions(-) + +diff --git a/js/ui/status/network.js b/js/ui/status/network.js +index c023022a7..79729e01b 100644 +--- a/js/ui/status/network.js ++++ b/js/ui/status/network.js +@@ -1,5 +1,5 @@ + // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +-const { Clutter, Gio, GLib, GObject, Meta, NM, St } = imports.gi; ++const { Clutter, Gio, GLib, GObject, Meta, NM, Polkit, St } = imports.gi; + const Mainloop = imports.mainloop; + const Signals = imports.signals; + +@@ -1683,11 +1683,25 @@ var NMApplet = class extends PanelMenu.SystemIndicator { + this._client.connect('connection-removed', this._connectionRemoved.bind(this)); + + Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); +- this._sessionUpdated(); ++ ++ this._configPermission = null; ++ Polkit.Permission.new( ++ 'org.freedesktop.NetworkManager.network-control', null, null, ++ (o, res) => { ++ try { ++ this._configPermission = Polkit.Permission.new_finish(res); ++ } catch (e) { ++ log('No permission to control network connections: %s'.format(e.toString())); ++ } ++ this._sessionUpdated(); ++ }); + } + + _sessionUpdated() { +- let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; ++ const sensitive = ++ !Main.sessionMode.isLocked && ++ !Main.sessionMode.isGreeter && ++ this._configPermission && this._configPermission.allowed; + this.menu.setSensitive(sensitive); + } + +-- +2.31.1 + + +From 7d2c8aabb86b9942c99ae9b7157dbffb875acde9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 10 Jun 2021 23:12:27 +0200 +Subject: [PATCH 4/5] sessionMode: Enable networkAgent on login screen + +We will soon enable the network sections in the status menu on the +login screen, so enable the network agent to handle authentication +requests (like wifi/VPN passwords). +--- + js/ui/sessionMode.js | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js +index 25aa75a3d..fa7f83416 100644 +--- a/js/ui/sessionMode.js ++++ b/js/ui/sessionMode.js +@@ -43,7 +43,9 @@ const _modes = { + isGreeter: true, + isPrimary: true, + unlockDialog: imports.gdm.loginDialog.LoginDialog, +- components: ['polkitAgent'], ++ components: Config.HAVE_NETWORKMANAGER ++ ? ['networkAgent', 'polkitAgent'] ++ : ['polkitAgent'], + panel: { + left: [], + center: ['dateMenu'], +-- +2.31.1 + + +From 07ce899bcb9d30991262d6c484508e6c5fa14c85 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Tue, 8 Jun 2021 00:19:26 +0200 +Subject: [PATCH 5/5] status/network: Do not disable on login screen + +We currently disable all network items on both the lock- and login +screen. While it makes sense to be very restrictive on the lock screen, +there are some (fringe) use cases for being more permissive on the +login screen (like remote home directories only accessible via VPN). + +There's precedence with the power-off/restart actions to be less +restrictive on the login screen, and since we started respecting +the `network-control` polkit action, it's possible to restore the +old behavior if desired. +--- + js/ui/status/network.js | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/js/ui/status/network.js b/js/ui/status/network.js +index 79729e01b..914dbbd99 100644 +--- a/js/ui/status/network.js ++++ b/js/ui/status/network.js +@@ -1700,7 +1700,6 @@ var NMApplet = class extends PanelMenu.SystemIndicator { + _sessionUpdated() { + const sensitive = + !Main.sessionMode.isLocked && +- !Main.sessionMode.isGreeter && + this._configPermission && this._configPermission.allowed; + this.menu.setSensitive(sensitive); + } +-- +2.31.1 + diff --git a/SOURCES/warn-less.patch b/SOURCES/warn-less.patch new file mode 100644 index 0000000..f5dd837 --- /dev/null +++ b/SOURCES/warn-less.patch @@ -0,0 +1,279 @@ +From 37bbb9175bbd061d4ae14e86c35e4211602dbeaa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 23 Mar 2020 17:57:38 +0100 +Subject: [PATCH 1/4] shell/util: Add touch_file_async() helper + +Add a small helper method to asynchronously "touch" a file and return +whether the file was created or not. + +As g_file_make_directory_with_parents() doesn't have an async variant, +we need a C helper to make the entire operation non-blocking. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432 +--- + src/shell-util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ + src/shell-util.h | 7 ++++++ + 2 files changed, 69 insertions(+) + +diff --git a/src/shell-util.c b/src/shell-util.c +index fa3fc08c8..eec67f3d7 100644 +--- a/src/shell-util.c ++++ b/src/shell-util.c +@@ -323,6 +323,68 @@ shell_get_file_contents_utf8_sync (const char *path, + return contents; + } + ++static void ++touch_file (GTask *task, ++ gpointer object, ++ gpointer task_data, ++ GCancellable *cancellable) ++{ ++ GFile *file = object; ++ g_autoptr (GFile) parent = NULL; ++ g_autoptr (GFileOutputStream) stream = NULL; ++ GError *error = NULL; ++ ++ parent = g_file_get_parent (file); ++ g_file_make_directory_with_parents (parent, cancellable, &error); ++ ++ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) ++ { ++ g_task_return_error (task, error); ++ return; ++ } ++ g_clear_error (&error); ++ ++ stream = g_file_create (file, G_FILE_CREATE_NONE, cancellable, &error); ++ ++ if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) ++ { ++ g_task_return_error (task, error); ++ return; ++ } ++ g_clear_error (&error); ++ ++ if (stream) ++ g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, NULL); ++ ++ g_task_return_boolean (task, stream != NULL); ++} ++ ++void ++shell_util_touch_file_async (GFile *file, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ g_autoptr (GTask) task = NULL; ++ ++ g_return_if_fail (G_IS_FILE (file)); ++ ++ task = g_task_new (file, NULL, callback, user_data); ++ g_task_set_source_tag (task, shell_util_touch_file_async); ++ ++ g_task_run_in_thread (task, touch_file); ++} ++ ++gboolean ++shell_util_touch_file_finish (GFile *file, ++ GAsyncResult *res, ++ GError **error) ++{ ++ g_return_val_if_fail (G_IS_FILE (file), FALSE); ++ g_return_val_if_fail (G_IS_TASK (res), FALSE); ++ ++ return g_task_propagate_boolean (G_TASK (res), error); ++} ++ + /** + * shell_util_wifexited: + * @status: the status returned by wait() or waitpid() +diff --git a/src/shell-util.h b/src/shell-util.h +index 02b8404e9..bedf516ba 100644 +--- a/src/shell-util.h ++++ b/src/shell-util.h +@@ -32,6 +32,13 @@ gboolean shell_write_string_to_stream (GOutputStream *stream, + char *shell_get_file_contents_utf8_sync (const char *path, + GError **error); + ++void shell_util_touch_file_async (GFile *file, ++ GAsyncReadyCallback callback, ++ gpointer user_data); ++gboolean shell_util_touch_file_finish (GFile *file, ++ GAsyncResult *res, ++ GError **error); ++ + gboolean shell_util_wifexited (int status, + int *exit); + +-- +2.31.1 + + +From 1f75494bea1ef7017d50d77cf5c7ad6b9668d4f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Mon, 23 Mar 2020 18:00:27 +0100 +Subject: [PATCH 2/4] environment: Hook up touch_file to GFile prototype + +We don't usually extend introspected types with our own API, but in +this case it's too tempting to make the helper functions usable with +Gio._promisify() ... + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432 +--- + js/ui/environment.js | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/js/ui/environment.js b/js/ui/environment.js +index e22ec7402..9c125d3eb 100644 +--- a/js/ui/environment.js ++++ b/js/ui/environment.js +@@ -9,7 +9,7 @@ imports.gi.versions.Gtk = '3.0'; + imports.gi.versions.TelepathyGLib = '0.12'; + imports.gi.versions.TelepathyLogger = '0.2'; + +-const { Clutter, GLib, Shell, St } = imports.gi; ++const { Clutter, Gio, GLib, Shell, St } = imports.gi; + const Gettext = imports.gettext; + + // We can't import shell JS modules yet, because they may have +@@ -97,6 +97,13 @@ function init() { + return St.describe_actor(this); + }; + ++ Gio._LocalFilePrototype.touch_async = function (callback) { ++ Shell.util_touch_file_async(this, callback); ++ }; ++ Gio._LocalFilePrototype.touch_finish = function (result) { ++ return Shell.util_touch_file_finish(this, result); ++ }; ++ + let origToString = Object.prototype.toString; + Object.prototype.toString = function() { + let base = origToString.call(this); +-- +2.31.1 + + +From 4bef23c7176a43f4dcf146e70bbb8aaa701b8cd2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Fri, 20 Mar 2020 12:42:04 +0100 +Subject: [PATCH 3/4] main: Do not warn about missing GDM on each login + +We now warn on startup if screen locking isn't available, however for +users who choose not to use GDM or logind, repeating the warning on +each login is more annoying than helpful. + +Instead, limit the warning to the first login on which the screen lock +became unavailable. That way the notification will still serve the +intended purpose of informing the user, but without being perceived +as nagging. + +https://gitlab.gnome.org/GNOME/gnome-shell/issues/2432 +--- + js/ui/main.js | 36 +++++++++++++++++++++++++++++++----- + 1 file changed, 31 insertions(+), 5 deletions(-) + +diff --git a/js/ui/main.js b/js/ui/main.js +index 1203b3c39..a3fad158c 100644 +--- a/js/ui/main.js ++++ b/js/ui/main.js +@@ -81,6 +81,9 @@ let _a11ySettings = null; + let _themeResource = null; + let _oskResource = null; + ++Gio._promisify(Gio._LocalFilePrototype, 'delete_async', 'delete_finish'); ++Gio._promisify(Gio._LocalFilePrototype, 'touch_async', 'touch_finish'); ++ + function _sessionUpdated() { + if (sessionMode.isPrimary) + _loadDefaultStylesheet(); +@@ -242,11 +245,8 @@ function _initializeUI() { + } + + if (sessionMode.currentMode !== 'gdm' && +- sessionMode.currentMode !== 'initial-setup' && +- screenShield === null) { +- notify(_('Screen Lock disabled'), +- _('Screen Locking requires the GNOME display manager.')); +- } ++ sessionMode.currentMode !== 'initial-setup') ++ _handleLockScreenWarning(); + + let perfModuleName = GLib.getenv("SHELL_PERF_MODULE"); + if (perfModuleName) { +@@ -257,6 +257,32 @@ function _initializeUI() { + }); + } + ++async function _handleLockScreenWarning() { ++ const path = '%s/lock-warning-shown'.format(global.userdatadir); ++ const file = Gio.File.new_for_path(path); ++ ++ const hasLockScreen = screenShield !== null; ++ if (hasLockScreen) { ++ try { ++ await file.delete_async(0, null); ++ } catch (e) { ++ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) ++ logError(e); ++ } ++ } else { ++ try { ++ if (!await file.touch_async()) ++ return; ++ } catch (e) { ++ logError(e); ++ } ++ ++ notify( ++ _('Screen Lock disabled'), ++ _('Screen Locking requires the GNOME display manager.')); ++ } ++} ++ + function _getStylesheet(name) { + let stylesheet; + +-- +2.31.1 + + +From c3f34e786826d0ed1af4150190159fed50d9fb87 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Florian=20M=C3=BCllner?= +Date: Thu, 22 Oct 2020 20:11:14 +0200 +Subject: [PATCH 4/4] messageTray: Default to generic policy + +How and if notifications are shown is controlled by NotificationPolicy +objects. But ever since 098bd45, only notification daemon sources or +notifications associated with an app are hooked up to GSettings. + +The hardcoded default policy for built-in notifications (including +those provided by extensions) arguably made sense back then, but +now that the main setting has been rebranded as "Do Not Disturb" +and is exposed prominently in the calendar drop-down, following +GSettings is a better default. + +https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3291 + +Part-of: +--- + js/ui/messageTray.js | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js +index 8f8130451..f6bdae8e4 100644 +--- a/js/ui/messageTray.js ++++ b/js/ui/messageTray.js +@@ -731,7 +731,7 @@ var Source = class Source { + } + + _createPolicy() { +- return new NotificationPolicy(); ++ return new NotificationGenericPolicy(); + } + + get narrowestPrivacyScope() { +-- +2.31.1 + diff --git a/SPECS/gnome-shell.spec b/SPECS/gnome-shell.spec index 6f557c2..ce18ea8 100644 --- a/SPECS/gnome-shell.spec +++ b/SPECS/gnome-shell.spec @@ -1,6 +1,6 @@ Name: gnome-shell Version: 3.32.2 -Release: 29%{?dist} +Release: 40%{?dist} Summary: Window management and application launching for GNOME Group: User Interface/Desktops @@ -26,6 +26,14 @@ Patch18: 0001-gdm-add-AuthList-control.patch Patch19: 0002-gdmUtil-enable-support-for-GDM-s-ChoiceList-PAM-exte.patch Patch20: wake-up-on-deactivate.patch Patch21: caps-lock-warning.patch +Patch22: gdm-networking.patch +Patch23: 0001-shellEntry-Disconnect-handler-on-destroy.patch +Patch24: fix-login-lock-screen.patch +Patch25: 0001-shellEntry-Determine-if-password-entry-from-content-.patch +Patch26: 0002-shellEntry-Give-password-menu-item-text-when-it-s-cr.patch +Patch27: 0003-shellEntry-Handle-password-item-from-dedication-func.patch +Patch28: 0004-shellEntry-Support-lockdown-of-Show-Text-menu-in-pas.patch +Patch29: 0005-shellEntry-Only-mask-text-in-password-entries.patch # Misc. Patch30: 0001-shellDBus-Add-a-DBus-method-to-load-a-single-extensi.patch @@ -51,11 +59,13 @@ Patch49: 0001-Do-not-change-Wacom-LEDs-through-g-s-d.patch Patch50: 0001-st-texture-cache-Cancel-pending-requests-on-icon-the.patch Patch51: introspect-backports.patch Patch52: 0001-popupMenu-Handle-keypress-if-numlock-is-enabled.patch +Patch53: 0001-theme-Update-window-preview-style.patch +Patch54: warn-less.patch # Backport JS invalid access warnings (#1651894, #1663171, #1642482, #1637622) -Patch54: fix-invalid-access-warnings.patch -Patch55: more-spurious-allocation-warnings.patch -Patch56: fix-some-js-warnings.patch +Patch55: fix-invalid-access-warnings.patch +Patch56: more-spurious-allocation-warnings.patch +Patch57: fix-some-js-warnings.patch # Backport performance fixes under load (#1820760) Patch60: 0001-environment-reduce-calls-to-g_time_zone_new_local.patch @@ -70,6 +80,7 @@ Patch70: 0001-screencast-Stop-recording-when-screen-size-or-resour.patch # Backport OSK fixes (#1871041) Patch75: osk-fixes.patch +Patch76: 0001-keyboard-Only-enable-keyboard-if-ClutterDeviceManage.patch # suspend/resume fix on nvidia (#1663440) Patch10001: 0001-background-refresh-after-suspend-on-wayland.patch @@ -77,6 +88,12 @@ Patch10002: 0002-background-rebuild-background-not-just-animation-on-.patch Patch10003: 0003-st-texture-cache-purge-on-resume.patch Patch10004: 0004-background-refresh-background-on-gl-video-memory-pur.patch +# Allow login screen extensions (#1651378) +Patch20001: 0001-extensionSystem-Handle-added-or-removed-sessionMode-.patch +Patch20002: 0002-extensionSystem-Get-rid-of-_enabled-boolean-optimiza.patch +Patch20003: 0003-extensionSystem-Allow-extensions-to-run-on-the-login.patch +Patch20004: 0004-sessionMode-Allow-extensions-at-the-login-and-unlock.patch + %define libcroco_version 0.6.8 %define eds_version 3.17.2 %define gnome_desktop_version 3.7.90 @@ -84,9 +101,9 @@ Patch10004: 0004-background-refresh-background-on-gl-video-memory-pur.patch %define gobject_introspection_version 1.49.1 %define gjs_version 1.54.0 %define gtk3_version 3.15.0 -%define mutter_version 3.32 +%define mutter_version 3.32.2-57 %define polkit_version 0.100 -%define gsettings_desktop_schemas_version 3.32.0-3 +%define gsettings_desktop_schemas_version 3.32.0-6 %define ibus_version 1.5.2 %define gnome_bluetooth_version 1:3.9.0 %define gstreamer_version 1.4.5 @@ -175,6 +192,8 @@ Requires: bolt%{?_isa} # Needed for launching flatpak apps etc Requires: xdg-desktop-portal-gtk +Provides: PolicyKit-authentication-agent = %{version}-%{release} + %description GNOME Shell provides core user interface functions for the GNOME 3 desktop, like switching to windows and launching applications. GNOME Shell takes @@ -252,6 +271,55 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/evolution-calendar.de %{_mandir}/man1/%{name}.1.gz %changelog +* Tue Aug 31 2021 Ray Strode - 3.32.2-40 +- Add bugs introduced in backport for #1651378 + Related: #1999758 +- Tidy up patch list a bit + +* Wed Aug 25 2021 Ray Strode - 3.32.2-39 +- Allow extensions on the login screen + Related: #1651378 + +* Thu Jul 29 2021 Florian Müllner - 3.32.2-38 +- Only mask text in password entries + Resolves: #1987233 + +* Wed Jul 28 2021 Florian Müllner - 3.32.2-37 +- Only warn once when not running under GDM + Resolves: #1980661 + +* Tue Jul 20 2021 Ray Strode - 3.32.2-36 +- Add ability to lock down password showing + Resolves: #1770302 +- Add requires on newer mutter version + Related: #1937866 + +* Tue Jul 13 2021 Florian Müllner - 3.32.2-35 +- Improve style of window preview close buttons + Resolves: #1981420 + +* Mon Jul 12 2021 Florian Müllner - 3.32.2-34 +- Add PolicyKit-authentication-agent virtual provides + Resolves: #1978287 + +* Mon Jun 14 2021 Florian Müllner - 3.32.2-33 +- Fix warnings on unlock + Resolves: #1971534 +- Fix gdm lock screen + Resolves: #1971507 + +* Thu Jun 10 2021 Florian Müllner - 3.32.2-32 +- Fix network secret requests on login screen + Related: #1935261 + +* Wed Jun 09 2021 Florian Müllner - 3.32.2-31 +- Backport of touch mode + Resolves: #1937866 + +* Tue Jun 08 2021 Florian Müllner - 3.32.2-30 +- Do not disable network actions on login screen + Resolves: #1935261 + * Mon Feb 01 2021 Florian Müllner - 3.32.2-29 - Refuse to override system extensions Related: #1802105