import gnome-shell-3.32.2-55.el8

i8c-beta changed/i8c-beta/gnome-shell-3.32.2-55.el8
MSVSphere Packaging Team 8 months ago
commit b0edc5b3c6

1
.gitignore vendored

@ -0,0 +1 @@
SOURCES/gnome-shell-3.32.2.tar.xz

@ -0,0 +1 @@
331e9cf71cd1d2a4e9238d87d216da4c6f3a400e SOURCES/gnome-shell-3.32.2.tar.xz

@ -0,0 +1,25 @@
From f5ddd0fc02e99597e4b8506ac35523a6fa8ac22f Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Wed, 4 Mar 2020 16:08:31 +0100
Subject: [PATCH] Do not change Wacom LEDs through g-s-d
Let the wacom kernel driver sort it out by itself.
---
js/ui/windowManager.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index dfe1b44..b2e938c 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -1037,7 +1037,6 @@ var WindowManager = class {
if (this._gsdWacomProxy) {
this._gsdWacomProxy.SetOLEDLabelsRemote(pad.get_device_node(), labels);
- this._gsdWacomProxy.SetGroupModeLEDRemote(pad.get_device_node(), group, mode);
}
});
--
2.24.1

@ -0,0 +1,58 @@
From a94260b4f2f72ea9328a0194b8656f1fb3e98675 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sat, 14 Dec 2019 19:15:53 +0100
Subject: [PATCH] a11y: Change HC icon theme first
There are two ways for applications to provide a high contrast icon:
1. install an icon into the HighContrast theme
2. install a symbolic icon into the default hicolor theme
The latter is preferred nowadays, and implemented in the high-contrast
CSS variant by enforcing the symbolic icon style.
However together with the way we currently enable/disable high-contrast,
this can lead to the following race:
1. the GTK theme is changed from HighContrast
2. we reload the default stylesheet
3. the icon style changes to "regular", so we request a
new icon from the HighContrast icon theme
4. the icon theme is changed from HighContrast
5. we evict existing icons from the cache
6. we reload icons for the new icon theme; however as we
find a pending request (from 3), we re-use it
7. the request from 3 finishes, and we end up with a
wrong icon in the cache
The simplest fix is to change the icon theme before the GTK theme: Unlike the
theme name, the icon style is encoded in the cache key, so we won't re-use
an old (and incorrect) request in that case.
---
js/ui/status/accessibility.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js
index 10223ec84..90948d465 100644
--- a/js/ui/status/accessibility.js
+++ b/js/ui/status/accessibility.js
@@ -154,14 +154,14 @@ class ATIndicator extends PanelMenu.Button {
interfaceSettings.is_writable(KEY_ICON_THEME),
enabled => {
if (enabled) {
- interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
interfaceSettings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
+ interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
} else if(!hasHC) {
- interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme);
interfaceSettings.set_string(KEY_ICON_THEME, iconTheme);
+ interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme);
} else {
- interfaceSettings.reset(KEY_GTK_THEME);
interfaceSettings.reset(KEY_ICON_THEME);
+ interfaceSettings.reset(KEY_GTK_THEME);
}
});
return highContrast;
--
2.23.0

@ -0,0 +1,40 @@
From 34e6bbeebef37ae688ca0527bde03fa26b143bb7 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 27 Jun 2019 14:27:34 -0400
Subject: [PATCH] animation: fix unintentional loop while polkit dialog is
active
The polkit password dialog has a spinner that gets displayed
while the users password is being verified.
Unfortunately, the spinner stop method unintentionally calls
back into itself after the stop fade out animation is complete.
The stop method is called at startup, so the looping begins as
soon as the dialog is visible and continues until the dialog is
dismissed.
This commit fixes the loop by having the stop method cease
calling itself, and instead having it call the stop method on the
superclass.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/602
---
js/ui/animation.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/ui/animation.js b/js/ui/animation.js
index c21b22565..58d7f4018 100644
--- a/js/ui/animation.js
+++ b/js/ui/animation.js
@@ -162,7 +162,7 @@ var Spinner = class extends AnimatedIcon {
time: SPINNER_ANIMATION_TIME,
transition: 'linear',
onComplete: () => {
- this.stop(false);
+ super.stop();
}
});
} else {
--
2.21.0

@ -0,0 +1,28 @@
From 23755cc20f3c05b97f769e27553f2ab482d60137 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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 10efa9135..7d40186c9 100644
--- a/src/shell-app.c
+++ b/src/shell-app.c
@@ -259,7 +259,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.21.0

@ -0,0 +1,111 @@
From a1c35ebb8f29103035526e6f48eba4ff37551964 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 21 Jun 2018 18:03:31 +0200
Subject: [PATCH] appDisplay: Show full app name on hover
---
data/theme/gnome-shell-sass/_common.scss | 8 ++++
js/ui/appDisplay.js | 48 ++++++++++++++++++++++++
2 files changed, 56 insertions(+)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index 3b0d2bf04..293ea2ab9 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -1411,6 +1411,14 @@ StScrollBar {
}
+ .app-well-hover-text {
+ text-align: center;
+ color: $osd_fg_color;
+ background-color: $osd_bg_color;
+ border-radius: 5px;
+ padding: 3px;
+ }
+
.app-well-app-running-dot { //running apps indicator
width: 10px; height: 3px;
background-color: $selected_bg_color;
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index adaefa7dd..a07db6573 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1478,6 +1478,20 @@ var AppIcon = class AppIcon {
this.actor.connect('clicked', this._onClicked.bind(this));
this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
+ this._hoverText = null;
+ this._hoverTimeoutId = 0;
+
+ if (this.icon.label) {
+ this._hoverText = new St.Label({ style_class: 'app-well-hover-text',
+ text: this.icon.label.text,
+ visible: false });
+ this._hoverText.clutter_text.line_wrap = true;
+ Main.layoutManager.addChrome(this._hoverText);
+
+ this.actor.connect('notify::hover', this._syncHoverText.bind(this));
+ this.connect('sync-tooltip', this._syncHoverText.bind(this));
+ }
+
this._menu = null;
this._menuManager = new PopupMenu.PopupMenuManager(this);
@@ -1509,12 +1523,39 @@ var AppIcon = class AppIcon {
this.app.disconnect(this._stateChangedId);
this._stateChangedId = 0;
this._removeMenuTimeout();
+ this._removeHoverTimeout();
+ if (this._hoverText)
+ this._hoverText.destroy();
+ this._hoverText = null;
}
_createIcon(iconSize) {
return this.app.create_icon_texture(iconSize);
}
+ _syncHoverText() {
+ if (this.shouldShowTooltip()) {
+ if (this._hoverTimeoutId)
+ return;
+
+ this._hoverTimeoutId = Mainloop.timeout_add(300, () => {
+ this._hoverText.style = `max-width: ${2 * this.icon.iconSize}px;`;
+ this._hoverText.ensure_style();
+
+ let [x, y] = this.icon.label.get_transformed_position();
+ let offset = (this._hoverText.width - this.icon.label.width) / 2;
+ this._hoverText.set_position(Math.floor(x - offset), Math.floor(y));
+ this._hoverText.show();
+
+ this._hoverTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ } else {
+ this._removeHoverTimeout();
+ this._hoverText.hide();
+ }
+ }
+
_removeMenuTimeout() {
if (this._menuTimeoutId > 0) {
Mainloop.source_remove(this._menuTimeoutId);
@@ -1522,6 +1563,13 @@ var AppIcon = class AppIcon {
}
}
+ _removeHoverTimeout() {
+ if (this._hoverTimeoutId > 0) {
+ Mainloop.source_remove(this._hoverTimeoutId);
+ this._hoverTimeoutId = 0;
+ }
+ }
+
_updateRunningStyle() {
if (this.app.state != Shell.AppState.STOPPED)
this._dot.show();
--
2.21.0

@ -0,0 +1,29 @@
From 165fc5147cd2c9bf4bc10a1c5a9a940ec4ddd8d9 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 15 Jan 2019 12:51:16 -0500
Subject: [PATCH 1/4] background: refresh after suspend on wayland
At the moment we only refresh after suspend on Xorg.
We need to do it on wayland, too.
---
src/shell-util.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/shell-util.c b/src/shell-util.c
index 31bb18e70..c6e5abed6 100644
--- a/src/shell-util.c
+++ b/src/shell-util.c
@@ -395,9 +395,6 @@ get_gl_vendor (void)
gboolean
shell_util_need_background_refresh (void)
{
- if (!clutter_check_windowing_backend (CLUTTER_WINDOWING_X11))
- return FALSE;
-
if (g_strcmp0 (get_gl_vendor (), "NVIDIA Corporation") == 0)
return TRUE;
--
2.21.0

@ -0,0 +1,37 @@
From 1dcae7bbba222a1c8bdfc2d76a9f716e638b0334 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 8 Jun 2017 12:04:31 -0400
Subject: [PATCH] data: install process-working.svg to filesystem
This helps prevent unlock failure on inplace upgrades between
7.3 and 7.4
---
data/theme/meson.build | 2 ++
meson.build | 1 +
2 files changed, 3 insertions(+)
diff --git a/data/theme/meson.build b/data/theme/meson.build
index 22bae3dd2..d5acb8d10 100644
--- a/data/theme/meson.build
+++ b/data/theme/meson.build
@@ -23,3 +23,5 @@ foreach style: styles
],
depend_files: theme_sources)
endforeach
+
+install_data('process-working.svg', install_dir: themedir)
diff --git a/meson.build b/meson.build
index 21a80bcc8..0acaba705 100644
--- a/meson.build
+++ b/meson.build
@@ -57,6 +57,7 @@ localedir = join_paths(datadir, 'locale')
portaldir = join_paths(datadir, 'xdg-desktop-portal', 'portals')
schemadir = join_paths(datadir, 'glib-2.0', 'schemas')
servicedir = join_paths(datadir, 'dbus-1', 'services')
+themedir = join_paths(pkgdatadir, 'theme')
# XXX: Once https://github.com/systemd/systemd/issues/9595 is fixed and we can
# depend on this version, replace with something like:
--
2.21.0

@ -0,0 +1,65 @@
From 6e80934456f0b4cc48da6a7201700dc4386a3474 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 27 Feb 2020 13:46:44 -0800
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
/etc/localtime.
This patch was provided by Florian, and I've tested it as far back as
3.28.4 to ensure that we are really reducing the number of open() calls
on the compositor thread.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1051
Signed-off-by: Christian Hergert <chergert@redhat.com>
---
js/ui/environment.js | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/js/ui/environment.js b/js/ui/environment.js
index 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, Gio, GLib, Shell, St } = imports.gi;
const Gettext = imports.gettext;
+const System = imports.system;
+
+let _localTimeZone = null;
// We can't import shell JS modules yet, because they may have
// variable initializations, etc, that depend on init() already having
@@ -117,9 +120,26 @@ function init() {
}
};
+ // Override to clear our own timezone cache as well
+ const origClearDateCaches = System.clearDateCaches;
+ System.clearDateCaches = function () {
+ _localTimeZone = null;
+ origClearDateCaches();
+ };
+
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
Date.prototype.toLocaleFormat = function(format) {
- return Shell.util_format_date(format, this.getTime());
+ if (_localTimeZone === null)
+ _localTimeZone = GLib.TimeZone.new_local();
+
+ let dt = GLib.DateTime.new(_localTimeZone,
+ this.getYear(),
+ this.getMonth() + 1,
+ this.getDate(),
+ this.getHours(),
+ this.getMinutes(),
+ this.getSeconds());
+ return dt ? dt.format(format) : '';
};
let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
--
2.31.1

@ -0,0 +1,87 @@
From 1b6eb29ade832647510b36ddc13c9b88a25036df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
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

@ -0,0 +1,42 @@
From 720eb83ba0b0e5e37185d7e7ed86fe9175cf18f4 Mon Sep 17 00:00:00 2001
From: Rui Matos <tiagomatos@gmail.com>
Date: Fri, 8 Nov 2013 13:58:09 +0100
Subject: [PATCH] extensions: Add a SESSION_MODE extension type
This allows e.g. gnome-tweak-tool to present these extensions in a
different way since they can't be disabled.
---
js/misc/extensionUtils.js | 3 ++-
js/ui/extensionSystem.js | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/js/misc/extensionUtils.js b/js/misc/extensionUtils.js
index cf308b31f..fb1e2b506 100644
--- a/js/misc/extensionUtils.js
+++ b/js/misc/extensionUtils.js
@@ -13,7 +13,8 @@ const FileUtils = imports.misc.fileUtils;
var ExtensionType = {
SYSTEM: 1,
- PER_USER: 2
+ PER_USER: 2,
+ SESSION_MODE: 3
};
// Maps uuid -> metadata object
diff --git a/js/ui/extensionSystem.js b/js/ui/extensionSystem.js
index 6244c39b4..9ffdb4f3d 100644
--- a/js/ui/extensionSystem.js
+++ b/js/ui/extensionSystem.js
@@ -322,6 +322,8 @@ function _loadExtensions() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', (finder, extension) => {
loadExtension(extension);
+ if (Main.sessionMode.enabledExtensions.indexOf(extension.uuid) != -1)
+ extension.type = ExtensionUtils.ExtensionType.SESSION_MODE;
});
finder.scanExtensions();
}
--
2.21.0

@ -0,0 +1,237 @@
From 592bf9b4ba879a365375a7edcb6c48258386e413 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 | 195 ++++++++++++++++++++++++++++++++++
js/js-resources.gresource.xml | 1 +
2 files changed, 196 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..fc1c3d6e4
--- /dev/null
+++ b/js/gdm/authList.js
@@ -0,0 +1,195 @@
+// -*- 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 <http://www.gnu.org/licenses/>.
+ */
+
+const Clutter = imports.gi.Clutter;
+const GObject = imports.gi.GObject;
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Signals = imports.signals;
+const St = imports.gi.St;
+
+const Tweener = imports.ui.tweener;
+
+const _SCROLL_ANIMATION_TIME = 0.5;
+
+const AuthListItem = new Lang.Class({
+ Name: 'AuthListItem',
+
+ _init(key, text) {
+ this.key = key;
+ let label = new St.Label({ style_class: 'auth-list-item-label',
+ y_align: Clutter.ActorAlign.CENTER });
+ label.text = text;
+
+ this.actor = new St.Button({ 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.actor.connect('key-focus-in', () => {
+ this._setSelected(true);
+ });
+ this.actor.connect('key-focus-out', () => {
+ this._setSelected(false);
+ });
+ this.actor.connect('notify::hover', () => {
+ this._setSelected(this.actor.hover);
+ });
+
+ this.actor.connect('clicked', this._onClicked.bind(this));
+ },
+
+ _onClicked() {
+ this.emit('activate');
+ },
+
+ _setSelected(selected) {
+ if (selected) {
+ this.actor.add_style_pseudo_class('selected');
+ this.actor.grab_key_focus();
+ } else {
+ this.actor.remove_style_pseudo_class('selected');
+ }
+ }
+});
+Signals.addSignalMethods(AuthListItem.prototype);
+
+var AuthList = new Lang.Class({
+ Name: 'AuthList',
+
+ _init() {
+ this.actor = new St.BoxLayout({ vertical: true,
+ style_class: 'login-dialog-auth-list-layout' });
+
+ this.label = new St.Label({ style_class: 'prompt-dialog-headline' });
+ this.actor.add_actor(this.label);
+
+ this._scrollView = new St.ScrollView({ style_class: 'login-dialog-user-list-view'});
+ this._scrollView.set_policy(Gtk.PolicyType.NEVER,
+ Gtk.PolicyType.AUTOMATIC);
+ this.actor.add_actor(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.actor.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.actor)
+ return;
+
+ let focusSet = this.actor.navigate_focus(null, Gtk.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.actor.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);
+ Tweener.removeTweens(adjustment);
+ Tweener.addTween (adjustment,
+ { value: value,
+ time: _SCROLL_ANIMATION_TIME,
+ transition: 'easeOutQuad' });
+ },
+
+ jumpToItem(item) {
+ let box = item.actor.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.actor, { x_fill: true });
+
+ this._items[key] = item;
+
+ item.connect('activate',
+ this._onItemActivated.bind(this));
+
+ // Try to keep the focused item front-and-center
+ item.actor.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.actor.destroy();
+ delete this._items[key];
+ },
+
+ numItems() {
+ return Object.keys(this._items).length;
+ },
+
+ clear() {
+ this.label.text = "";
+ this._box.destroy_all_children();
+ this._items = {};
+ }
+});
+Signals.addSignalMethods(AuthList.prototype);
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 836d1c674..002b202f8 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell">
+ <file>gdm/authList.js</file>
<file>gdm/authPrompt.js</file>
<file>gdm/batch.js</file>
<file>gdm/fingerprint.js</file>
--
2.21.0

@ -0,0 +1,39 @@
From 2acede02f30833c3fb891db8483f933f7b41508c Mon Sep 17 00:00:00 2001
From: rpm-build <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

@ -0,0 +1,51 @@
From d2661753076a60a7981836e4a85e88c4588fb1b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 17 Nov 2022 15:21:42 +0100
Subject: [PATCH] layout: Initialize regions unconditionally
We currently initialize regions in all code paths except for the
greeter. But while there are no windows on the login screen, the
work area can still be used for positioning, for example for
notifications.
Part-of:
<https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2546>
---
js/ui/layout.js | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/js/ui/layout.js b/js/ui/layout.js
index beb4c0a5d..bb51946b7 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -624,20 +624,19 @@ var LayoutManager = GObject.registerClass({
reactive: true });
this.addChrome(this._coverPane);
+ // Force an update of the regions before we scale the UI group to
+ // get the correct allocation for the struts.
+ // Do this even when we don't animate on restart, so that maximized
+ // windows restore to the right size.
+ this._updateRegions();
+
if (Meta.is_restart()) {
- // On restart, we don't do an animation. Force an update of the
- // regions immediately so that maximized windows restore to the
- // right size taking struts into account.
- this._updateRegions();
+ // On restart, we don't do an animation.
} else if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
} else {
this._updateBackgrounds();
- // We need to force an update of the regions now before we scale
- // the UI group to get the correct allocation for the struts.
- this._updateRegions();
-
this.keyboardBox.hide();
let monitor = this.primaryMonitor;
--
2.38.1

@ -0,0 +1,56 @@
From 35cbad572120125d3b823f37d2100b2beee4c1d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 8 Jun 2017 17:07:56 +0200
Subject: [PATCH] layout: Make the hot corner optional
Whether people love or hate the hot corner depends in large extents
on hardware sensitivity and habits, which is hard to get right
universally. So bite the bullet and support an option to enable or
disable hot corners ...
https://bugzilla.gnome.org/show_bug.cgi?id=688320
---
js/ui/layout.js | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 2b3bb7442..beb4c0a5d 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const { Clutter, GLib, GObject, Meta, Shell, St } = imports.gi;
+const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
const Signals = imports.signals;
const Background = imports.ui.background;
@@ -267,6 +267,13 @@ var LayoutManager = GObject.registerClass({
this._backgroundGroup.lower_bottom();
this._bgManagers = [];
+ this._interfaceSettings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.interface'
+ });
+
+ this._interfaceSettings.connect('changed::enable-hot-corners',
+ this._updateHotCorners.bind(this));
+
// Need to update struts on new workspaces when they are added
let workspaceManager = global.workspace_manager;
workspaceManager.connect('notify::n-workspaces',
@@ -358,6 +365,11 @@ var LayoutManager = GObject.registerClass({
});
this.hotCorners = [];
+ if (!this._interfaceSettings.get_boolean('enable-hot-corners')) {
+ this.emit('hot-corners-changed');
+ return;
+ }
+
let size = this.panelBox.height;
// build new hot corners
--
2.21.0

@ -0,0 +1,49 @@
From 6d26b6f9f66e14843f175305441a2464dd255fd1 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 27 Jul 2020 10:58:49 -0400
Subject: [PATCH] loginDialog: Reset auth prompt on vt switch before fade in
At the moment, if a user switches to the login screen vt,
the login screen fades in whatever was on screen prior, and
then does a reset.
It makes more sense to reset first, so we fade in what the
user is going to interact with instead of what they interacted
with before.
Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2997
---
js/gdm/loginDialog.js | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index 214c2f512..eb6846d5c 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -923,6 +923,9 @@ var LoginDialog = GObject.registerClass({
if (this.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
return;
+ if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
+ this._authPrompt.reset();
+
Tweener.addTween(this,
{ opacity: 255,
time: _FADE_ANIMATION_TIME,
@@ -935,12 +938,7 @@ var LoginDialog = GObject.registerClass({
children[i].opacity = this.opacity;
}
},
- onUpdateScope: this,
- onComplete() {
- if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
- this._authPrompt.reset();
- },
- onCompleteScope: this });
+ onUpdateScope: this });
}
_gotGreeterSessionProxy(proxy) {
--
2.32.0

@ -0,0 +1,32 @@
From 9cfa56d4f3c5fe513630c58c09bd2421f3ca580b Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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/_common.scss | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index c2df28279..a382ce561 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -1801,7 +1801,12 @@ StScrollBar {
.login-dialog-banner { color: darken($osd_fg_color,10%); }
.login-dialog-button-box { spacing: 5px; }
.login-dialog-message-warning { color: $warning_color; }
- .login-dialog-message-hint { padding-top: 0; padding-bottom: 20px; }
+ .login-dialog-message-hint, .login-dialog-message {
+ color: darken($osd_fg_color, 20%);
+ padding-top: 0;
+ padding-bottom: 20px;
+ min-height: 2.75em;
+ }
.login-dialog-user-selection-box { padding: 100px 0px; }
.login-dialog-not-listed-label {
padding-left: 2px;
--
2.21.0

@ -0,0 +1,38 @@
From ba3ce64fbbce20192a55f9d438d1032c0bac0557 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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 245837783..788309de7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -39,6 +39,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,
@@ -268,8 +269,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.29.2

@ -0,0 +1,49 @@
From 0d95c2087aba7f0b07cb303c1f15d097b45f1b09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Tue, 28 Apr 2020 23:26:11 +0200
Subject: [PATCH] main: Unset the right prevFocus actor after the focus stack
got shifted
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When a modal that's not on top of the modalActorFocusStack gets popped,
we shift the focus stack as described in popModal() to ensure the chain
remains correct. That however destroys the association of a modal actor
and its prevFocus actor on the focus stack, because the prevFocus actors
are now moved to different entries of the stack.
Now when a prevFocus actor gets destroyed, we don't handle that case
correctly and search for the modal actor that was associated with the
prevFocus actor before the stack was shifted, which means we end up
unsetting the wrong prevFocus actor.
So fix that and search the stack for the prevFocus actor which is being
destroyed instead to unset the correct entry.
Thanks to Florian Müllner for figuring out the actual issue and
proposing this fix.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2446
---
js/ui/main.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/js/ui/main.js b/js/ui/main.js
index dd1d8463d..ca3dcaa3c 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -486,7 +486,9 @@ function pushModal(actor, params) {
let prevFocusDestroyId;
if (prevFocus != null) {
prevFocusDestroyId = prevFocus.connect('destroy', () => {
- let index = _findModal(actor);
+ const index = modalActorFocusStack.findIndex(
+ record => record.prevFocus === prevFocus);
+
if (index >= 0)
modalActorFocusStack[index].prevFocus = null;
});
--
2.35.1

@ -0,0 +1,29 @@
From bd4a3186dc21f2c8d3e0f851cf262a34ddb6b625 Mon Sep 17 00:00:00 2001
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Fri, 4 Oct 2019 14:21:25 +0200
Subject: [PATCH] networkAgent: add support for SAE secrets
NetworkManager supports "WPA3 Personal" networks for some time now, they
use the SAE authentication. Add support for it alongside other
password-based mechanisms.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/751
---
js/ui/components/networkAgent.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/ui/components/networkAgent.js b/js/ui/components/networkAgent.js
index 32d40fb2b..3ff957bf6 100644
--- a/js/ui/components/networkAgent.js
+++ b/js/ui/components/networkAgent.js
@@ -216,6 +216,7 @@ var NetworkSecretDialog = class extends ModalDialog.ModalDialog {
// First the easy ones
case 'wpa-none':
case 'wpa-psk':
+ case 'sae':
secrets.push({ label: _("Password: "), key: 'psk',
value: wirelessSecuritySetting.psk || '',
validate: this._validateWpaPsk, password: true });
--
2.32.0

@ -0,0 +1,38 @@
From 2ddb51234ca69c0e637b35071b4e760d1b72527a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Mon, 24 Feb 2020 11:19:28 +0100
Subject: [PATCH] overview: Hide the overview on session mode hasOverview
changes
If the sessionMode does not allow to show the overview, we should also
hide an already visible overview.
This fixes a bug where, if the lockscreen was shown while the overview
was visible, the Ctrl+Alt+Tab popup would allow navigating inside the
overview because the overview actor is still mapped.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1043
---
js/ui/overview.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 5bad4cbd62..03cecb6dbc 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -196,7 +196,11 @@ var Overview = class {
}
_sessionUpdated() {
- this.isDummy = !Main.sessionMode.hasOverview;
+ const { hasOverview } = Main.sessionMode;
+ if (!hasOverview)
+ this.hide();
+
+ this.isDummy = !hasOverview;
this._createOverview();
}
--
2.43.0

@ -0,0 +1,80 @@
From 2bb826291c420dd1b601758c7a686ac48e1086a6 Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Mon, 16 Dec 2019 12:39:49 +0100
Subject: [PATCH] padOsd: Re-query action labels after mode switches
Do this so the pad OSD is able to update dynamically to mode changes,
showing immediately the new actions for the current mode(s).
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/898
---
js/ui/padOsd.js | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/js/ui/padOsd.js b/js/ui/padOsd.js
index a4af47297..b4b3fe453 100644
--- a/js/ui/padOsd.js
+++ b/js/ui/padOsd.js
@@ -555,6 +555,14 @@ var PadDiagram = GObject.registerClass({
this.add_actor(label);
}
+ updateLabels(callback) {
+ for (let i = 0; i < this._labels.length; i++) {
+ let [label, action, idx, dir] = this._labels[i];
+ let str = callback(action, idx, dir);
+ label.set_text(str);
+ }
+ }
+
_applyLabel(label, action, idx, dir, str) {
if (str != null) {
label.set_text(str);
@@ -758,17 +766,29 @@ var PadOsd = class {
global.display.request_pad_osd(pad, editionMode);
}
- _createLabel(type, number, dir) {
+ _getActionText(type, number) {
let str = global.display.get_pad_action_label(this.padDevice, type, number);
- let label = new St.Label({ text: str ? str : _("None") });
+ return str ? str : _("None");
+ }
+
+ _createLabel(type, number, dir) {
+ let label = new St.Label({ text: this._getActionText(type, number) });
this._padDiagram.addLabel(label, type, number, dir);
}
+ _updateActionLabels() {
+ this._padDiagram.updateLabels(this._getActionText.bind(this));
+ }
+
_onCapturedEvent(actor, event) {
+ let isModeSwitch =
+ (event.type() == Clutter.EventType.PAD_BUTTON_PRESS ||
+ event.type() == Clutter.EventType.PAD_BUTTON_RELEASE) &&
+ this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
+
if (event.type() == Clutter.EventType.PAD_BUTTON_PRESS &&
event.get_source_device() == this.padDevice) {
this._padDiagram.activateButton(event.get_button());
- let isModeSwitch = this.padDevice.get_mode_switch_button_group(event.get_button()) >= 0;
/* Buttons that switch between modes cannot be edited */
if (this._editionMode && !isModeSwitch)
@@ -777,6 +797,11 @@ var PadOsd = class {
} else if (event.type() == Clutter.EventType.PAD_BUTTON_RELEASE &&
event.get_source_device() == this.padDevice) {
this._padDiagram.deactivateButton(event.get_button());
+
+ if (isModeSwitch) {
+ this._endActionEdition();
+ this._updateActionLabels();
+ }
return Clutter.EVENT_STOP;
} else if (event.type() == Clutter.EventType.KEY_PRESS &&
(!this._editionMode || event.get_key_symbol() == Clutter.Escape)) {
--
2.24.0

@ -0,0 +1,54 @@
From aadb0e19999c339ac1d6501a2e52b363e57e26ef Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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/_common.scss | 5 +++++
js/ui/panel.js | 9 ++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index a382ce561..3b0d2bf04 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -769,6 +769,11 @@ StScrollBar {
//dimensions of the icon are hardcoded
}
+ .panel-logo-icon {
+ padding-right: .4em;
+ icon-size: 1em;
+ }
+
&:hover {
color: lighten($fg_color, 10%);
}
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 16484850a..ede1c2b82 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -465,11 +465,18 @@ class ActivitiesButton extends PanelMenu.Button {
this.actor.name = 'panelActivities';
+ let box = new St.BoxLayout();
+ this.actor.add_actor(box);
+ let 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_actor(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.actor.add_actor(this._label);
+ box.add_actor(this._label);
this.actor.label_actor = this._label;
--
2.21.0

@ -0,0 +1,36 @@
From fb0a9a60ab8f1c0dd96e789969ab9b6e48a9fce4 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Tue, 21 Jul 2020 16:33:04 +0200
Subject: [PATCH] popupMenu: Handle keypress if numlock is enabled
On Wayland, navigating menus with the keyboard would not open drop-down
menus when NumLock is enabled.
That's old issue (gnome-shell#550) that was not completely fixed with
commit 88556226 because the lock mask needs to be filtered out in
_onKeyPress() as well.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/550
---
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 6de081ce81..9835face19 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -801,9 +801,10 @@ var PopupMenu = class extends PopupMenuBase {
let state = event.get_state();
- // if user has a modifier down (except capslock)
+ // if user has a modifier down (except capslock and numlock)
// then don't handle the key press here
state &= ~Clutter.ModifierType.LOCK_MASK;
+ state &= ~Clutter.ModifierType.MOD2_MASK;
state &= Clutter.ModifierType.MODIFIER_MASK;
if (state)
--
2.26.2

@ -0,0 +1,33 @@
From cacce594f07295bb1b9e0685913a287e3cea2453 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 a005a206b..cd38f11fc 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -513,8 +513,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._liftShield(true, 0);
+ }
});
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
--
2.21.0

@ -0,0 +1,253 @@
From 67a4506d4d8a0cbbaca5df4adfc309e54e557aee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 5 Jan 2021 12:04:23 +0100
Subject: [PATCH] screencast: Stop recording when screen size or resource scale
change
Video encoders don't really handle changing the size of the video, and if
we'd e.g. change resolution while recording, we would end up with a corrupt
video file. Handle this more gracefully by stopping the recording if the
conditions change.
---
js/ui/screencast.js | 92 +++++++++++++++++++++++++++++++++++++++++---
src/shell-recorder.c | 50 ++++++++++++------------
src/shell-recorder.h | 1 +
3 files changed, 114 insertions(+), 29 deletions(-)
diff --git a/js/ui/screencast.js b/js/ui/screencast.js
index 0b0b14a8e..54f8fb5ae 100644
--- a/js/ui/screencast.js
+++ b/js/ui/screencast.js
@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const { Gio, GLib, Shell } = imports.gi;
+const { Gio, GLib, Meta, Shell } = imports.gi;
const Signals = imports.signals;
const Main = imports.ui.main;
@@ -53,16 +53,27 @@ var ScreencastService = class {
this._stopRecordingForSender(name);
}
- _stopRecordingForSender(sender) {
+ _stopRecordingForSender(sender, closeNow=false) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
- recorder.close();
+ if (closeNow)
+ recorder.close_now();
+ else
+ recorder.close();
this._recorders.delete(sender);
this.emit('updated');
+ let connection = this._dbusImpl.get_connection();
+ let info = this._dbusImpl.get_info();
+ connection.emit_signal(sender,
+ this._dbusImpl.get_object_path(),
+ info ? info.name : null,
+ 'Stopped',
+ null);
+
return true;
}
@@ -78,6 +89,53 @@ var ScreencastService = class {
recorder.set_draw_cursor(options['draw-cursor']);
}
+ _ensureResourceScaleChangedHandler() {
+ if (this._resourceScaleChangedHandlerId)
+ return;
+
+ this._resourceScaleChangedHandlerId =
+ global.stage.connect('notify::resource-scale',
+ () => {
+ for (let sender of this._recorders.keys()) {
+ let recorder = this._recorders.get(sender);
+
+ if (!recorder.is_recording())
+ continue;
+
+ this._stopRecordingForSender(sender, true);
+ }
+ });
+ }
+
+ _ensureMonitorsChangedHandler() {
+ if (this._monitorsChangedHandlerId)
+ return;
+
+ this._monitorsChangedHandlerId = Main.layoutManager.connect('monitors-changed',
+ () => {
+ for (let sender of this._recorders.keys()) {
+ let recorder = this._recorders.get(sender);
+
+ if (!recorder.is_recording())
+ continue;
+
+ let geometry = recorder._geometry;
+ let screenWidth = global.screen_width;
+ let screenHeight = global.screen_height;
+
+ if (recorder._isAreaScreecast) {
+ if (geometry.x + geometry.width > screenWidth ||
+ geometry.y + geometry.height > screenHeight)
+ this._stopRecordingForSender(sender, true);
+ } else {
+ if (geometry.width != screenWidth ||
+ geometry.height != screenHeight)
+ this._stopRecordingForSender(sender, true);
+ }
+ }
+ });
+ }
+
ScreencastAsync(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast ||
@@ -95,8 +153,20 @@ var ScreencastService = class {
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
- if (!success)
+ if (success) {
+ recorder._isAreaScreecast = false;
+ recorder._geometry =
+ new Meta.Rectangle({
+ x: 0,
+ y: 0,
+ width: global.screen_width,
+ height: global.screen_height
+ });
+ this._ensureResourceScaleChangedHandler();
+ this._ensureMonitorsChangedHandler();
+ } else {
this._stopRecordingForSender(sender);
+ }
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
@@ -131,8 +201,20 @@ var ScreencastService = class {
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
- if (!success)
+ if (success) {
+ recorder._isAreaScreecast = true;
+ recorder._geometry =
+ new Meta.Rectangle({
+ x: x,
+ y: y,
+ width: width,
+ height: height
+ });
+ this._ensureResourceScaleChangedHandler();
+ this._ensureMonitorsChangedHandler();
+ } else {
this._stopRecordingForSender(sender);
+ }
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
diff --git a/src/shell-recorder.c b/src/shell-recorder.c
index 0203ecf1c..e561a0152 100644
--- a/src/shell-recorder.c
+++ b/src/shell-recorder.c
@@ -511,21 +511,6 @@ recorder_update_size (ShellRecorder *recorder)
}
}
-static void
-recorder_on_stage_notify_size (GObject *object,
- GParamSpec *pspec,
- ShellRecorder *recorder)
-{
- recorder_update_size (recorder);
-
- /* This breaks the recording but tweaking the GStreamer pipeline a bit
- * might make it work, at least if the codec can handle a stream where
- * the frame size changes in the middle.
- */
- if (recorder->current_pipeline)
- recorder_pipeline_set_caps (recorder->current_pipeline);
-}
-
static gboolean
recorder_idle_redraw (gpointer data)
{
@@ -622,12 +607,6 @@ recorder_connect_stage_callbacks (ShellRecorder *recorder)
G_CALLBACK (recorder_on_stage_destroy), recorder);
g_signal_connect_after (recorder->stage, "paint",
G_CALLBACK (recorder_on_stage_paint), recorder);
- g_signal_connect (recorder->stage, "notify::width",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
- g_signal_connect (recorder->stage, "notify::height",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
- g_signal_connect (recorder->stage, "notify::resource-scale",
- G_CALLBACK (recorder_on_stage_notify_size), recorder);
}
static void
@@ -639,9 +618,6 @@ recorder_disconnect_stage_callbacks (ShellRecorder *recorder)
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_paint,
recorder);
- g_signal_handlers_disconnect_by_func (recorder->stage,
- (void *)recorder_on_stage_notify_size,
- recorder);
/* We don't don't deselect for cursor changes in case someone else just
* happened to be selecting for cursor events on the same window; sending
@@ -1578,6 +1554,32 @@ shell_recorder_record (ShellRecorder *recorder,
return TRUE;
}
+/**
+ * shell_recorder_close_now:
+ * @recorder: the #ShellRecorder
+ *
+ * Stops recording immediately. It's possible to call shell_recorder_record()
+ * again to reopen a new recording stream, but unless change the recording
+ * filename, this may result in the old recording being overwritten.
+ */
+void
+shell_recorder_close_now (ShellRecorder *recorder)
+{
+ g_return_if_fail (SHELL_IS_RECORDER (recorder));
+ g_return_if_fail (recorder->state != RECORDER_STATE_CLOSED);
+
+ recorder_remove_update_pointer_timeout (recorder);
+ recorder_close_pipeline (recorder);
+
+ recorder->state = RECORDER_STATE_CLOSED;
+
+ /* Reenable after the recording */
+ meta_enable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
+
+ /* Release the refcount we took when we started recording */
+ g_object_unref (recorder);
+}
+
/**
* shell_recorder_close:
* @recorder: the #ShellRecorder
diff --git a/src/shell-recorder.h b/src/shell-recorder.h
index c1e0e6368..1c3e6aab4 100644
--- a/src/shell-recorder.h
+++ b/src/shell-recorder.h
@@ -37,6 +37,7 @@ void shell_recorder_set_area (ShellRecorder *recorder,
gboolean shell_recorder_record (ShellRecorder *recorder,
char **filename_used);
void shell_recorder_close (ShellRecorder *recorder);
+void shell_recorder_close_now (ShellRecorder *recorder);
void shell_recorder_pause (ShellRecorder *recorder);
gboolean shell_recorder_is_recording (ShellRecorder *recorder);
--
2.27.0

@ -0,0 +1,78 @@
From 391f262aee82ac12fcf99951d6b2df362f734b31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Mon, 15 Jun 2020 20:41:45 +0200
Subject: [PATCH] shell/app: Handle workspace from startup notifications
Launching applications on a particular workspace works through
launch contexts and startup notifications. While this is no
longer required by a launcher/WM split, in theory this allows
us to reliably identify the correct window to apply startup
properties to.
However in practice we fail more often than not: Missing support in
toolkits, differences between display protocols, D-Bus activation
and single-instance applications all provide their own pitfalls.
So instead, take advantage of the fact that launcher and WM live in
the same process, and go with the unsophisticated approach: Just
remember the last workspace that was requested when launching an
app, then move the next window that is associated with the app to
that workspace.
This will break X11 applications that set an initial workspace, but
that's legacy functionality anyway (given that there's no wayland
protocol for that functionality), and seems a price worth paying
for making launching apps on workspaces more reliable.
---
src/shell-app.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/src/shell-app.c b/src/shell-app.c
index 7d40186c9..f716bc5f8 100644
--- a/src/shell-app.c
+++ b/src/shell-app.c
@@ -1067,6 +1067,10 @@ _shell_app_add_window (ShellApp *app,
if (!app->running_state)
create_running_state (app);
+ if (app->started_on_workspace >= 0)
+ meta_window_change_workspace_by_index (window, app->started_on_workspace, FALSE);
+ app->started_on_workspace = -1;
+
app->running_state->window_sort_stale = TRUE;
app->running_state->windows = g_slist_prepend (app->running_state->windows, g_object_ref (window));
g_signal_connect_object (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app, 0);
@@ -1156,16 +1160,14 @@ _shell_app_handle_startup_sequence (ShellApp *app,
shell_app_state_transition (app, SHELL_APP_STATE_STARTING);
meta_x11_display_focus_the_no_focus_window (x11_display,
meta_startup_sequence_get_timestamp (sequence));
- app->started_on_workspace = meta_startup_sequence_get_workspace (sequence);
}
- if (!starting)
- {
- if (app->running_state && app->running_state->windows)
- shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
- else /* application have > 1 .desktop file */
- shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
- }
+ if (starting)
+ app->started_on_workspace = meta_startup_sequence_get_workspace (sequence);
+ else if (app->running_state && app->running_state->windows)
+ shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
+ else /* application have > 1 .desktop file */
+ shell_app_state_transition (app, SHELL_APP_STATE_STOPPED);
}
/**
@@ -1473,6 +1475,7 @@ static void
shell_app_init (ShellApp *self)
{
self->state = SHELL_APP_STATE_STOPPED;
+ self->started_on_workspace = -1;
}
static void
--
2.29.2

@ -0,0 +1,28 @@
From 3182ad73c8f88628cb51a96feba0fc32ce7f01c9 Mon Sep 17 00:00:00 2001
From: Illya Klymov <xanf@xanf.me>
Date: Mon, 8 Jul 2019 03:29:36 +0000
Subject: [PATCH] shell-recorder: Restore cursor recording
Due to changes introduced in 5357e0a1 cursor recording interaction with
magnifier was reversed. This fix restores original correct behavior
Related issue: https://gitlab.gnome.org/GNOME/gnome-shell/issues/1208
---
src/shell-recorder.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shell-recorder.c b/src/shell-recorder.c
index 0203ecf1c..cf1cc336f 100644
--- a/src/shell-recorder.c
+++ b/src/shell-recorder.c
@@ -465,7 +465,7 @@ recorder_record_frame (ShellRecorder *recorder,
g_object_get (settings, "magnifier-active", &magnifier_active, NULL);
- if (magnifier_active)
+ if (!magnifier_active)
recorder_draw_cursor (recorder, buffer);
}
--
2.35.1

@ -0,0 +1,66 @@
From 660ebe0125b591355116934ee57b08010e05246c Mon Sep 17 00:00:00 2001
From: Rui Matos <tiagomatos@gmail.com>
Date: Fri, 8 Nov 2013 11:36:04 +0100
Subject: [PATCH] shellDBus: Add a DBus method to load a single extension
This allows e.g. gnome-tweak-tool to install an extension from a zip
file and load it into the running shell.
---
.../org.gnome.Shell.Extensions.xml | 13 +++++++++++++
js/ui/shellDBus.js | 16 ++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/data/dbus-interfaces/org.gnome.Shell.Extensions.xml b/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
index 34a65af44..ce69439fc 100644
--- a/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
+++ b/data/dbus-interfaces/org.gnome.Shell.Extensions.xml
@@ -189,6 +189,19 @@
-->
<method name="CheckForUpdates"/>
+ <!--
+ LoadUserExtension:
+ @uuid: The UUID of the extension
+ @success: Whether the operation was successful
+
+ Load a newly installed user extension
+ -->
+
+ <method name="LoadUserExtension">
+ <arg type="s" direction="in" name="uuid"/>
+ <arg type="b" direction="out" name="success"/>
+ </method>
+
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index 19d07acce..112d60feb 100644
--- a/js/ui/shellDBus.js
+++ b/js/ui/shellDBus.js
@@ -341,6 +341,22 @@ var GnomeShellExtensions = class {
ExtensionDownloader.checkForUpdates();
}
+ LoadUserExtension(uuid) {
+ let extension = ExtensionUtils.extensions[uuid];
+ if (extension)
+ return true;
+
+ let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
+ try {
+ extension = ExtensionUtils.createExtensionObject(uuid, dir, ExtensionUtils.ExtensionType.PER_USER);
+ ExtensionSystem.loadExtension(extension);
+ } catch (e) {
+ log('Could not load user extension from %s'.format(dir.get_path()));
+ return false;
+ }
+ return true;
+ }
+
get ShellVersion() {
return Config.PACKAGE_VERSION;
}
--
2.21.0

@ -0,0 +1,91 @@
From e6cd96a9f6a89f77ca0fab72aff8c56354b59f38 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,36 @@
From fd8c4dc073b121b1093d68472cac3292d2c6605c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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

@ -0,0 +1,59 @@
From 96404287bc4269dea7b037e7b178e54ebf616d47 Mon Sep 17 00:00:00 2001
From: Daniel van Vugt <daniel.van.vugt@canonical.com>
Date: Tue, 24 Nov 2020 17:34:08 +0800
Subject: [PATCH] st-bin: Disallow st_bin_set_child with already-parented
children
Not checking for this would result in `clutter_actor_add_child`
failing, but StBin keeping a copy in `priv->child`. So later on,
`st_bin_remove` would never be called on it and this assertion
would fail and crash the whole shell:
```
static void
st_bin_destroy (ClutterActor *actor)
{
StBinPrivate *priv = st_bin_get_instance_private (ST_BIN (actor));
if (priv->child)
clutter_actor_destroy (priv->child);
g_assert (priv->child == NULL);
```
By disallowing spurious `st_bin_set_child` calls we now prevent StBin
from entering such a corrupt state and the above assertion won't fail
anymore.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1507>
---
src/st/st-bin.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/src/st/st-bin.c b/src/st/st-bin.c
index f013909e8..7959a4e95 100644
--- a/src/st/st-bin.c
+++ b/src/st/st-bin.c
@@ -434,6 +434,19 @@ st_bin_set_child (StBin *bin,
if (priv->child == child)
return;
+ if (child)
+ {
+ ClutterActor *parent = clutter_actor_get_parent (child);
+
+ if (parent)
+ {
+ g_warning ("%s: The provided 'child' actor %p already has a "
+ "(different) parent %p and can't be made a child of %p.",
+ G_STRFUNC, child, parent, bin);
+ return;
+ }
+ }
+
if (priv->child)
clutter_actor_remove_child (CLUTTER_ACTOR (bin), priv->child);
--
2.38.1

@ -0,0 +1,94 @@
From 1bf28eea64056846547ec33d783c7f2e0dad78a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 22 May 2020 22:53:39 +0200
Subject: [PATCH] st/texture-cache: Cancel pending requests on icon-theme
changes
As outlined in commit 36b8dcbe07, we can end up with wrong icons
if the icon theme changes right after a GTK theme change to/from
HighContrast triggered a theme reload.
That's because when we reload icons for the new icon theme, there
are already pending requests due to the icon-style change; those
requests are simply re-used for the new icons, with the existing
icon infos from the old theme.
The above commit applied a simple work-around by changing the
icon theme before the GTK theme, but that only works for the
HighContrast switch in our own UI.
It turns out that Settings also uses the "wrong" order, so the
issue still reproduces with the Universal Access panel.
So instead of relying on everything changing the settings in the
order we expect, cancel all ongoing requests on icon-theme changes.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1277
---
src/st/st-texture-cache.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c
index 35e9d036f..6dc351282 100644
--- a/src/st/st-texture-cache.c
+++ b/src/st/st-texture-cache.c
@@ -48,6 +48,8 @@ struct _StTextureCachePrivate
/* File monitors to evict cache data on changes */
GHashTable *file_monitors; /* char * -> GFileMonitor * */
+
+ GCancellable *cancellable;
};
static void st_texture_cache_dispose (GObject *object);
@@ -152,6 +154,9 @@ on_icon_theme_changed (StSettings *settings,
{
g_autofree gchar *theme;
+ g_cancellable_cancel (cache->priv->cancellable);
+ g_cancellable_reset (cache->priv->cancellable);
+
st_texture_cache_evict_icons (cache);
g_object_get (settings, "gtk-icon-theme", &theme, NULL);
@@ -186,6 +191,8 @@ st_texture_cache_init (StTextureCache *self)
self->priv->file_monitors = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
g_object_unref, g_object_unref);
+ self->priv->cancellable = g_cancellable_new ();
+
on_icon_theme_changed (settings, NULL, self);
}
@@ -194,8 +201,11 @@ st_texture_cache_dispose (GObject *object)
{
StTextureCache *self = (StTextureCache*)object;
+ g_cancellable_cancel (self->priv->cancellable);
+
g_clear_object (&self->priv->settings);
g_clear_object (&self->priv->icon_theme);
+ g_clear_object (&self->priv->cancellable);
g_clear_pointer (&self->priv->keyed_cache, g_hash_table_destroy);
g_clear_pointer (&self->priv->keyed_surface_cache, g_hash_table_destroy);
@@ -675,11 +685,14 @@ load_texture_async (StTextureCache *cache,
gtk_icon_info_load_symbolic_async (data->icon_info,
&foreground_color, &success_color,
&warning_color, &error_color,
- NULL, on_symbolic_icon_loaded, data);
+ cache->priv->cancellable,
+ on_symbolic_icon_loaded, data);
}
else
{
- gtk_icon_info_load_icon_async (data->icon_info, NULL, on_icon_loaded, data);
+ gtk_icon_info_load_icon_async (data->icon_info,
+ cache->priv->cancellable,
+ on_icon_loaded, data);
}
}
else
--
2.26.2

@ -0,0 +1,30 @@
From 4e555e0efeb4b31918e199d29bee99b2a4ed1c8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 11 May 2022 02:34:21 +0200
Subject: [PATCH] status/volume: Hide sliders initially
We update the visibility on state or stream changes, but those
changes may never happen if pipewire-pulse/pulseaudio isn't
available (for example when running as root).
Hiding the sliders is preferable in that case to showing non-working
controls.
---
js/ui/status/volume.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index d555b426e..ab5065683 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -30,6 +30,7 @@ var StreamSlider = class {
this._control = control;
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
+ this.item.actor.hide();
this._slider = new Slider.Slider(0);
--
2.35.1

@ -0,0 +1,76 @@
From c68fd3c94c6debdbf11020940c5a6aaee8bc230d Mon Sep 17 00:00:00 2001
From: Feichtmeier <frederik.feichtmeier@gmail.com>
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

@ -0,0 +1,45 @@
From 20640a92f98e2145b9b6581209c978e9f6f78801 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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 628f145ea..f8eb4398c 100644
--- a/js/ui/windowMenu.js
+++ b/js/ui/windowMenu.js
@@ -115,6 +115,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.21.0

@ -0,0 +1,26 @@
From e1eb24fdf731af8736cdb76dc28aa2b10679aa5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 28 Sep 2023 14:34:24 +0200
Subject: [PATCH] windowMenu: Ignore release
If the menu was open on button-press, make sure it is kept open
until explicitly dismissed, regardless of the pointer position.
---
js/ui/windowMenu.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/ui/windowMenu.js b/js/ui/windowMenu.js
index f8eb4398c3..2ec11c7879 100644
--- a/js/ui/windowMenu.js
+++ b/js/ui/windowMenu.js
@@ -205,6 +205,7 @@ var WindowMenuManager = class {
let menu = new WindowMenu(window, this._sourceActor);
this._manager.addMenu(menu);
+ this._manager.ignoreRelease();
menu.connect('activate', () => {
window.check_alive(global.get_current_time());
--
2.41.0

@ -0,0 +1,31 @@
From 9115f6e7962b97c3ee2fbef7b195b7116e62c070 Mon Sep 17 00:00:00 2001
From: Carlos Garnacho <carlosg@gnome.org>
Date: Fri, 13 Dec 2019 18:14:51 +0100
Subject: [PATCH] workspace: Pass device to startDrag()
This is necessary to make DnD operations work from tablet devices on
wayland, as it's not the same onscreen pointer sprite than mice. Fixes
window DnD in the overview on tablet devices, no longer having them stick
to the wrong pointer.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/897
---
js/ui/workspace.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 1e9bedc28..d470f7f40 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -431,7 +431,7 @@ var WindowClone = GObject.registerClass({
return;
let [x, y] = action.get_coords();
action.release();
- this._draggable.startDrag(x, y, global.get_current_time(), this._dragTouchSequence);
+ this._draggable.startDrag(x, y, global.get_current_time(), this._dragTouchSequence, event.get_device());
});
} else {
this.emit('show-chrome');
--
2.23.0

@ -0,0 +1,37 @@
From b69b404118852f7955f60d1814f5e19ad61ce449 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 12 Jul 2019 03:26:51 +0000
Subject: [PATCH] workspacesView: Work around spurious allocation changes
For some reason, people are still seeing those after commit d5ebd8c8.
While this is something we really should figure out, we can work around
the issue by keeping the view actors hidden until the update is complete.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/1065
---
js/ui/workspacesView.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 069937d5a..e302296a6 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -666,10 +666,15 @@ var WorkspacesDisplay = class {
this._scrollValueChanged.bind(this));
}
+ // HACK: Avoid spurious allocation changes while updating views
+ view.actor.hide();
+
this._workspacesViews.push(view);
Main.layoutManager.overviewGroup.add_actor(view.actor);
}
+ this._workspacesViews.forEach(v => v.actor.show());
+
this._updateWorkspacesFullGeometry();
this._updateWorkspacesActualGeometry();
}
--
2.21.0

@ -0,0 +1,31 @@
From f27c4224aa96975ae44641612f5fff3772f5c294 Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Mon, 22 Aug 2022 13:06:05 +0200
Subject: [PATCH] [PATCH 2/4] background: rebuild background, not just
animation on resume
Previously, we would only refresh the animation on resume
(to handle clock skew).
But we actually need to rebuild the background, too, on nvidia,
so we should just do a full background change.
---
js/ui/background.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/ui/background.js b/js/ui/background.js
index 2a404ae..dd11e3e 100644
--- a/js/ui/background.js
+++ b/js/ui/background.js
@@ -254,7 +254,7 @@ var Background = class Background {
(lm, aboutToSuspend) => {
if (aboutToSuspend)
return;
- this._refreshAnimation();
+ this.emit('changed');
});
this._settingsChangedSignalId =
--
2.35.3

@ -0,0 +1,33 @@
From 189add05c07fe9d9bed6c1399b30e51a4a934bd3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Mon, 2 Mar 2020 13:46:04 +0100
Subject: [PATCH 2/6] environment: Fix date conversion
This is a regression from commit 06b690ff21204:
GLib.DateTime.new() expects the full four-digit year, so passing
the abbreviated year from Date() will result in a bogus datetime.
Today is *not* Saturday March 2nd, 120 ...
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1061
---
js/ui/environment.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/js/ui/environment.js b/js/ui/environment.js
index f3f2d17c7..a9cc16dee 100644
--- a/js/ui/environment.js
+++ b/js/ui/environment.js
@@ -126,7 +126,7 @@ function init() {
_localTimeZone = GLib.TimeZone.new_local();
let dt = GLib.DateTime.new(_localTimeZone,
- this.getYear(),
+ this.getFullYear(),
this.getMonth() + 1,
this.getDate(),
this.getHours(),
--
2.26.2

@ -0,0 +1,237 @@
From b70cf463e08bff43b242b851fc7c79244f54e76b Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,267 @@
From c3ab03f8721ea96df6ac91c0393ed13ba750ab7e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 | 74 ++++++++++++++++++++++++++++++++++++++++++-
js/gdm/loginDialog.js | 5 +++
js/gdm/util.js | 28 ++++++++++++++++
js/ui/unlockDialog.js | 9 +++++-
4 files changed, 114 insertions(+), 2 deletions(-)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index cf77b3f26..71069e93b 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -4,6 +4,7 @@ const { Clutter, GLib, Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const Animation = imports.ui.animation;
+const AuthList = imports.gdm.authList;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const Meta = imports.gi.Meta;
@@ -54,6 +55,7 @@ var AuthPrompt = class {
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));
@@ -116,6 +118,28 @@ var AuthPrompt = class {
this.actor.add(this._timedLoginIndicator);
+ this._authList = new AuthList.AuthList();
+ this._authList.connect('activate', (list, key) => {
+ this._authList.actor.reactive = false;
+ Tweener.addTween(this._authList.actor,
+ { opacity: 0,
+ time: MESSAGE_FADE_OUT_ANIMATION_TIME,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ this._authList.clear();
+ this._authList.actor.hide();
+ this._userVerifier.selectChoice(this._queryingService, key);
+
+ }
+ });
+ });
+ this._authList.actor.hide();
+ this.actor.add(this._authList.actor,
+ { expand: true,
+ x_fill: true,
+ y_fill: false,
+ x_align: St.Align.START });
+
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true;
@@ -258,6 +282,21 @@ var AuthPrompt = class {
this.emit('prompted');
}
+ _onShowChoiceList(userVerifier, serviceName, promptMessage, choiceList) {
+ if (this._queryingService)
+ this.clear();
+
+ this._queryingService = serviceName;
+
+ if (this._preemptiveAnswer)
+ this._preemptiveAnswer = null;
+
+ this.nextButton.label = _("Next");
+ this.setChoiceList(promptMessage, choiceList);
+ this.updateSensitivity(true);
+ this.emit('prompted');
+ }
+
_onOVirtUserAuthenticated() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
@@ -386,6 +425,8 @@ var AuthPrompt = class {
clear() {
this._entry.text = '';
this.stopSpinning();
+ this._authList.clear();
+ this._authList.actor.hide();
}
setPasswordChar(passwordChar) {
@@ -401,12 +442,42 @@ var AuthPrompt = class {
this._label.set_text(question);
+ this._authList.actor.hide();
this._label.show();
this._entry.show();
this._entry.grab_key_focus();
}
+ _fadeInChoiceList() {
+ this._authList.actor.opacity = 0;
+ this._authList.actor.show();
+ this._authList.actor.reactive = false;
+ Tweener.addTween(this._authList.actor,
+ { opacity: 255,
+ time: MESSAGE_FADE_OUT_ANIMATION_TIME,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ this._authList.actor.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;
@@ -442,6 +513,7 @@ var AuthPrompt = class {
else
this._message.remove_style_class_name('login-dialog-message-hint');
+ this._message.show();
if (message) {
Tweener.removeTweens(this._message);
this._message.text = message;
@@ -457,7 +529,7 @@ var AuthPrompt = class {
}
updateSensitivity(sensitive) {
- this._updateNextButtonSensitivity(sensitive && (this._entry.text.length > 0 || this.verificationStatus == AuthPromptStatus.VERIFYING));
+ this._updateNextButtonSensitivity(sensitive && !this._authList.actor.visible && (this._entry.text.length > 0 || this.verificationStatus == AuthPromptStatus.VERIFYING));
this._entry.reactive = sensitive;
this._entry.clutter_text.editable = sensitive;
}
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index 9aaa013d8..942f5a0e5 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -406,6 +406,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::' + GdmUtil.BANNER_MESSAGE_KEY,
diff --git a/js/gdm/util.js b/js/gdm/util.js
index 6e940d2ab..9e249139d 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -192,6 +192,10 @@ var ShellUserVerifier = class {
if (this._userVerifier) {
this._userVerifier.run_dispose();
this._userVerifier = null;
+ if (this._userVerifierChoiceList) {
+ this._userVerifierChoiceList.run_dispose();
+ this._userVerifierChoiceList = null;
+ }
}
}
@@ -219,6 +223,10 @@ var ShellUserVerifier = class {
this._oVirtCredentialsManager = null;
}
+ 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);
@@ -362,6 +370,11 @@ var ShellUserVerifier = class {
return;
}
+ if (client.get_user_verifier_choice_list)
+ this._userVerifierChoiceList = client.get_user_verifier_choice_list();
+ else
+ this._userVerifierChoiceList = null;
+
this.reauthenticating = true;
this._connectSignals();
this._beginVerification();
@@ -379,6 +392,11 @@ var ShellUserVerifier = class {
return;
}
+ if (client.get_user_verifier_choice_list)
+ this._userVerifierChoiceList = client.get_user_verifier_choice_list();
+ else
+ this._userVerifierChoiceList = null;
+
this._connectSignals();
this._beginVerification();
this._hold.release();
@@ -392,6 +410,9 @@ var ShellUserVerifier = class {
this._userVerifier.connect('conversation-stopped', this._onConversationStopped.bind(this));
this._userVerifier.connect('reset', this._onReset.bind(this));
this._userVerifier.connect('verification-complete', this._onVerificationComplete.bind(this));
+
+ if (this._userVerifierChoiceList)
+ this._userVerifierChoiceList.connect('choice-query', this._onChoiceListQuery.bind(this));
}
_getForegroundService() {
@@ -468,6 +489,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(info, MessageType.INFO);
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
index 5c9d46021..4b0470f4b 100644
--- a/js/ui/unlockDialog.js
+++ b/js/ui/unlockDialog.js
@@ -33,7 +33,14 @@ var UnlockDialog = class {
y_expand: true });
this.actor.add_child(this._promptBox);
- this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
+ this._gdmClient = new Gdm.Client();
+
+ try {
+ this._gdmClient.set_enabled_extensions([Gdm.UserVerifierChoiceList.interface_info().name]);
+ } catch(e) {
+ }
+
+ this._authPrompt = new AuthPrompt.AuthPrompt(this._gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
this._authPrompt.connect('failed', this._fail.bind(this));
this._authPrompt.connect('cancelled', this._fail.bind(this));
this._authPrompt.connect('reset', this._onReset.bind(this));
--
2.21.0

@ -0,0 +1,92 @@
From de7df6c7248c39d7cce1c70485df72a398da92a3 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,393 @@
From 7300ae2eac743fa06f40f6459ac8fbf739ab28ea Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,152 @@
From c9277326055c96185a80b68d4228eee360bb0e7c Mon Sep 17 00:00:00 2001
From: Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
Date: Thu, 1 Aug 2019 20:58:20 -0300
Subject: [PATCH 3/6] shell/app-system: Monitor for icon theme changes
Whenever an app is installed, the usual routine is
to run 'gtk-update-icon-cache' after installing all
of the app's files.
The side effect of that is that the .desktop file of
the application is installed before the icon theme
is updated. By the time GAppInfoMonitor emits the
'changed' signal, the icon theme is not yet updated,
leading to StIcon use the fallback icon.
Under some circumstances (e.g. on very slow spinning
disks) the app icon is never actually loaded, and we
see the fallback icon forever.
Monitor the icon theme for changes when an app is
installed. Try as many as 6 times before giving up
on detecting an icon theme update.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/661
---
src/shell-app-system.c | 54 +++++++++++++++++++++++++++++++++++++++
src/st/st-texture-cache.c | 8 ++++++
src/st/st-texture-cache.h | 2 ++
3 files changed, 64 insertions(+)
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
index f632cbe54..127f29ef0 100644
--- a/src/shell-app-system.c
+++ b/src/shell-app-system.c
@@ -14,6 +14,14 @@
#include "shell-app-system-private.h"
#include "shell-global.h"
#include "shell-util.h"
+#include "st.h"
+
+/* Rescan for at most RESCAN_TIMEOUT_MS * MAX_RESCAN_RETRIES. That
+ * should be plenty of time for even a slow spinning drive to update
+ * the icon cache.
+ */
+#define RESCAN_TIMEOUT_MS 2500
+#define MAX_RESCAN_RETRIES 6
/* Vendor prefixes are something that can be preprended to a .desktop
* file name. Undo this.
@@ -51,6 +59,9 @@ struct _ShellAppSystemPrivate {
GHashTable *id_to_app;
GHashTable *startup_wm_class_to_id;
GList *installed_apps;
+
+ guint rescan_icons_timeout_id;
+ guint n_rescan_retries;
};
static void shell_app_system_finalize (GObject *object);
@@ -157,12 +168,54 @@ stale_app_remove_func (gpointer key,
return app_is_stale (value);
}
+static gboolean
+rescan_icon_theme_cb (gpointer user_data)
+{
+ ShellAppSystemPrivate *priv;
+ ShellAppSystem *self;
+ StTextureCache *texture_cache;
+ gboolean rescanned;
+
+ self = (ShellAppSystem *) user_data;
+ priv = self->priv;
+
+ texture_cache = st_texture_cache_get_default ();
+ rescanned = st_texture_cache_rescan_icon_theme (texture_cache);
+
+ priv->n_rescan_retries++;
+
+ if (rescanned || priv->n_rescan_retries >= MAX_RESCAN_RETRIES)
+ {
+ priv->n_rescan_retries = 0;
+ priv->rescan_icons_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+rescan_icon_theme (ShellAppSystem *self)
+{
+ ShellAppSystemPrivate *priv = self->priv;
+
+ priv->n_rescan_retries = 0;
+
+ if (priv->rescan_icons_timeout_id > 0)
+ return;
+
+ priv->rescan_icons_timeout_id = g_timeout_add (RESCAN_TIMEOUT_MS,
+ rescan_icon_theme_cb,
+ self);
+}
+
static void
installed_changed (GAppInfoMonitor *monitor,
gpointer user_data)
{
ShellAppSystem *self = user_data;
+ rescan_icon_theme (self);
scan_startup_wm_class_to_id (self);
g_hash_table_foreach_remove (self->priv->id_to_app, stale_app_remove_func, NULL);
@@ -200,6 +253,7 @@ shell_app_system_finalize (GObject *object)
g_hash_table_destroy (priv->id_to_app);
g_hash_table_destroy (priv->startup_wm_class_to_id);
g_list_free_full (priv->installed_apps, g_object_unref);
+ g_clear_handle_id (&priv->rescan_icons_timeout_id, g_source_remove);
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
}
diff --git a/src/st/st-texture-cache.c b/src/st/st-texture-cache.c
index c1331747f..4d0d617c4 100644
--- a/src/st/st-texture-cache.c
+++ b/src/st/st-texture-cache.c
@@ -1554,3 +1554,11 @@ st_texture_cache_get_default (void)
instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
return instance;
}
+
+gboolean
+st_texture_cache_rescan_icon_theme (StTextureCache *cache)
+{
+ StTextureCachePrivate *priv = cache->priv;
+
+ return gtk_icon_theme_rescan_if_needed (priv->icon_theme);
+}
diff --git a/src/st/st-texture-cache.h b/src/st/st-texture-cache.h
index 11d1c4e64..a99316da8 100644
--- a/src/st/st-texture-cache.h
+++ b/src/st/st-texture-cache.h
@@ -113,4 +113,6 @@ CoglTexture * st_texture_cache_load (StTextureCache *cache,
void *data,
GError **error);
+gboolean st_texture_cache_rescan_icon_theme (StTextureCache *cache);
+
#endif /* __ST_TEXTURE_CACHE_H__ */
--
2.26.2

@ -0,0 +1,119 @@
From 39cf97176e2a92506081ee151ea546e2c6cf213a Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,66 @@
From 2ebeda3385fb679df4bc13ba4b80bdeba5e2ad13 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 15 Jan 2019 12:54:32 -0500
Subject: [PATCH 3/4] 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 061303cf3..8d1755cf1 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -200,7 +200,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 cbe3afaba..40a11dd6d 100644
--- a/src/st/st-texture-cache.c
+++ b/src/st/st-texture-cache.c
@@ -113,6 +113,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 11d1c4e64..9079d1fda 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.21.0

@ -0,0 +1,115 @@
From 055bc14c70af66fe1893dcd4c42c65662ae1f9d0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 21 Jan 2019 15:07:15 -0500
Subject: [PATCH 4/4] background: refresh background on gl-video-memory-purged
signal
Right now we refresh the background when resuming and when NVIDIA.
But mutter has a signal to tell us specifically when to refresh,
and the signal is only emitted for NVIDIA, so use that instead.
---
js/ui/background.js | 9 +++++++--
js/ui/layout.js | 12 ------------
src/shell-util.c | 27 ---------------------------
src/shell-util.h | 2 --
4 files changed, 7 insertions(+), 43 deletions(-)
diff --git a/js/ui/background.js b/js/ui/background.js
index 75b76a57e..466cc4de7 100644
--- a/js/ui/background.js
+++ b/js/ui/background.js
@@ -527,10 +527,15 @@ var BackgroundSource = class BackgroundSource {
let monitorManager = Meta.MonitorManager.get();
this._monitorsChangedId =
monitorManager.connect('monitors-changed',
- this._onMonitorsChanged.bind(this));
+ this._refresh.bind(this));
+
+ global.display.connect('gl-video-memory-purged', () => {
+ Meta.Background.refresh_all();
+ this._refresh();
+ });
}
- _onMonitorsChanged() {
+ _refresh() {
for (let monitorIndex in this._backgrounds) {
let background = this._backgrounds[monitorIndex];
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 30e750dc5..2b3bb7442 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -282,18 +282,6 @@ var LayoutManager = GObject.registerClass({
monitorManager.connect('monitors-changed',
this._monitorsChanged.bind(this));
this._monitorsChanged();
-
- // NVIDIA drivers don't preserve FBO contents across
- // suspend/resume, see
- // https://bugzilla.gnome.org/show_bug.cgi?id=739178
- if (Shell.util_need_background_refresh()) {
- LoginManager.getLoginManager().connect('prepare-for-sleep',
- (lm, suspending) => {
- if (suspending)
- return;
- Meta.Background.refresh_all();
- });
- }
}
// This is called by Main after everything else is constructed
diff --git a/src/shell-util.c b/src/shell-util.c
index c6e5abed6..9c25643c6 100644
--- a/src/shell-util.c
+++ b/src/shell-util.c
@@ -374,33 +374,6 @@ shell_util_create_pixbuf_from_data (const guchar *data,
(GdkPixbufDestroyNotify) g_free, NULL);
}
-typedef const gchar *(*ShellGLGetString) (GLenum);
-
-static const gchar *
-get_gl_vendor (void)
-{
- static const gchar *vendor = NULL;
-
- if (!vendor)
- {
- ShellGLGetString gl_get_string;
- gl_get_string = (ShellGLGetString) cogl_get_proc_address ("glGetString");
- if (gl_get_string)
- vendor = gl_get_string (GL_VENDOR);
- }
-
- return vendor;
-}
-
-gboolean
-shell_util_need_background_refresh (void)
-{
- if (g_strcmp0 (get_gl_vendor (), "NVIDIA Corporation") == 0)
- return TRUE;
-
- return FALSE;
-}
-
static gboolean
canvas_draw_cb (ClutterContent *content,
cairo_t *cr,
diff --git a/src/shell-util.h b/src/shell-util.h
index 6904f43bc..049c3fe18 100644
--- a/src/shell-util.h
+++ b/src/shell-util.h
@@ -44,8 +44,6 @@ GdkPixbuf *shell_util_create_pixbuf_from_data (const guchar *data,
int height,
int rowstride);
-gboolean shell_util_need_background_refresh (void);
-
ClutterContent * shell_util_get_content_for_window_actor (MetaWindowActor *window_actor,
MetaRectangle *window_rect);
--
2.21.0

@ -0,0 +1,124 @@
From 2a4f33df723d4b9ce68e5948b568a89675d37411 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 26 Feb 2020 14:46:20 -0800
Subject: [PATCH 4/6] global: force fsync() to worker thread when saving state
The g_file_replace_contents_async() API can potentially call fsync() from
the thread calling into it upon completion. This can have disasterous
effects when run from the compositor main thread such as complete stalls.
This is a followup to 86a00b6872375a266449beee1ea6d5e94f1ebbcb which
assumed (like the rest of us) that the fsync() would be performed on the
thread that was doing the I/O operations.
You can verify this with an strace -e fsync and cause terminal to display
a command completed notification (eg: from a backdrop window).
This also fixes a lifecycle bug for the variant, as
g_file_replace_contents_async() does not copy the data during the operation
as that is the responsibility of the caller. Instead, we just use a GBytes
variant and reference the variant there.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/1050
---
src/shell-global.c | 70 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 63 insertions(+), 7 deletions(-)
diff --git a/src/shell-global.c b/src/shell-global.c
index df84b6b0d..4b33778e0 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -1572,6 +1572,55 @@ delete_variant_cb (GObject *object,
g_hash_table_remove (global->save_ops, object);
}
+static void
+replace_contents_worker (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GFile *file = source_object;
+ GBytes *bytes = task_data;
+ GError *error = NULL;
+ const gchar *data;
+ gsize len;
+
+ data = g_bytes_get_data (bytes, &len);
+
+ if (!g_file_replace_contents (file, data, len, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ NULL, cancellable, &error))
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static void
+replace_contents_async (GFile *path,
+ GBytes *bytes,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_assert (G_IS_FILE (path));
+ g_assert (bytes != NULL);
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (path, cancellable, callback, user_data);
+ g_task_set_source_tag (task, replace_contents_async);
+ g_task_set_task_data (task, g_bytes_ref (bytes), (GDestroyNotify)g_bytes_unref);
+ g_task_run_in_thread (task, replace_contents_worker);
+}
+
+static gboolean
+replace_contents_finish (GFile *file,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
static void
replace_variant_cb (GObject *object,
GAsyncResult *result,
@@ -1580,7 +1629,7 @@ replace_variant_cb (GObject *object,
ShellGlobal *global = user_data;
GError *error = NULL;
- if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error))
+ if (!replace_contents_finish (G_FILE (object), result, &error))
{
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
@@ -1616,12 +1665,19 @@ save_variant (ShellGlobal *global,
}
else
{
- g_file_replace_contents_async (path,
- g_variant_get_data (variant),
- g_variant_get_size (variant),
- NULL, FALSE,
- G_FILE_CREATE_REPLACE_DESTINATION,
- cancellable, replace_variant_cb, global);
+ g_autoptr(GBytes) bytes = NULL;
+
+ bytes = g_bytes_new_with_free_func (g_variant_get_data (variant),
+ g_variant_get_size (variant),
+ (GDestroyNotify)g_variant_unref,
+ g_variant_ref (variant));
+ /* g_file_replace_contents_async() can potentially fsync() from the
+ * calling thread when completing the asynchronous task. Instead, we
+ * want to force that fsync() to a thread to avoid blocking the
+ * compository main loop. Using our own replace_contents_async()
+ * simply executes the operation synchronously from a thread.
+ */
+ replace_contents_async (path, bytes, cancellable, replace_variant_cb, global);
}
g_object_unref (path);
--
2.26.2

@ -0,0 +1,116 @@
From 5fad989ca773f9e0ff6fdbeb0cb7c9cb70cc6148 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

@ -0,0 +1,116 @@
From ee64cd773bdeef845d02dc84063f926d77090dec Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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

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

@ -0,0 +1,36 @@
From 7a264550c5f3a98b1786b1a75cff01cde1d084eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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

@ -0,0 +1,66 @@
From a0df79f8de4c13c36ed3b22cfdbb78e324424ef1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Sat, 14 Mar 2020 14:45:42 +0100
Subject: [PATCH 6/6] js: Always use AppSystem to lookup apps
There is no good reason for bypassing the application cache in
AppSystem and loading .desktop files again.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1093
---
js/ui/appDisplay.js | 4 ++--
js/ui/calendar.js | 16 ++++++++++------
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index a2d691085..cb2be7d3c 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1001,8 +1001,8 @@ var AppSearchProvider = class AppSearchProvider {
let results = [];
groups.forEach(group => {
group = group.filter(appID => {
- let app = Gio.DesktopAppInfo.new(appID);
- return app && app.should_show();
+ const app = this._appSys.lookup_app(appID);
+ return app && app.app_info.should_show();
});
results = results.concat(group.sort(
(a, b) => usage.compare(a, b)
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index cd3e879c4..3ae2e44f8 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -791,8 +791,9 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
this._title.connect('clicked', this._onTitleClicked.bind(this));
this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this));
- Shell.AppSystem.get_default().connect('installed-changed',
- this._appInstalledChanged.bind(this));
+ this._appSys = Shell.AppSystem.get_default();
+ this._appSys.connect('installed-changed',
+ this._appInstalledChanged.bind(this));
this._appInstalledChanged();
}
@@ -883,10 +884,13 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
Main.overview.hide();
Main.panel.closeCalendar();
- let app = this._getCalendarApp();
- if (app.get_id() == 'evolution.desktop')
- app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
- app.launch([], global.create_app_launch_context(0, -1));
+ let appInfo = this._getCalendarApp();
+ if (app.get_id() == 'evolution.desktop') {
+ let app = this._appSys.lookup_app('evolution-calendar.desktop');
+ if (app)
+ appInfo = app.app_info;
+ }
+ appInfo.launch([], global.create_app_launch_context(0, -1));
}
setDate(date) {
--
2.26.2

@ -0,0 +1,159 @@
From 3252f05b8745a5d3118986474793fe3ecc2b041c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 19 Apr 2016 13:12:46 -0400
Subject: [PATCH] loginDialog: allow timed login with disabled user list
At the moment the timed login feature is implemented in the user list.
If there's no user list, we don't show the indicator anywhere and
don't proceed with timed login.
This commit allows timed login to work when the user list is disabled.
It accomplishes this by putting the timed login indicator on the
auth prompt, in that scenario.
---
data/theme/gnome-shell-sass/_common.scss | 4 +++
js/gdm/authPrompt.js | 41 +++++++++++++++++++++++-
js/gdm/loginDialog.js | 25 +++++++++++++--
3 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index a6357baad..c2df28279 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -1856,6 +1856,10 @@ StScrollBar {
padding-bottom: 12px;
spacing: 8px;
width: 23em;
+ .login-dialog-timed-login-indicator {
+ height: 2px;
+ background-color: darken($fg_color,40%);
+ }
}
.login-dialog-prompt-label {
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 27eb31a89..cf77b3f26 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const { Clutter, Pango, Shell, St } = imports.gi;
+const { Clutter, GLib, Pango, Shell, St } = imports.gi;
const Signals = imports.signals;
const Animation = imports.ui.animation;
@@ -111,6 +111,11 @@ var AuthPrompt = class {
this._entry.grab_key_focus();
+ this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
+ scale_x: 0 });
+
+ this.actor.add(this._timedLoginIndicator);
+
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true;
@@ -135,6 +140,40 @@ var AuthPrompt = class {
this._defaultButtonWell.add_child(this._spinner.actor);
}
+ showTimedLoginIndicator(time) {
+ let hold = new Batch.Hold();
+
+ this.hideTimedLoginIndicator();
+
+ let startTime = GLib.get_monotonic_time();
+
+ this._timedLoginTimeoutId = GLib.timeout_add (GLib.PRIORITY_DEFAULT, 33,
+ () => {
+ let currentTime = GLib.get_monotonic_time();
+ let elapsedTime = (currentTime - startTime) / GLib.USEC_PER_SEC;
+ this._timedLoginIndicator.scale_x = elapsedTime / time;
+ if (elapsedTime >= time) {
+ this._timedLoginTimeoutId = 0;
+ hold.release();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ return GLib.SOURCE_CONTINUE;
+ });
+
+ GLib.Source.set_name_by_id(this._timedLoginTimeoutId, '[gnome-shell] this._timedLoginTimeoutId');
+
+ return hold;
+ }
+
+ hideTimedLoginIndicator() {
+ if (this._timedLoginTimeoutId) {
+ GLib.source_remove(this._timedLoginTimeoutId);
+ this._timedLoginTimeoutId = 0;
+ }
+ this._timedLoginIndicator.scale_x = 0.;
+ }
+
_onDestroy() {
if (this._preemptiveAnswerWatchId) {
this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId);
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index 6c4d1357d..b4df6e959 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -734,6 +734,9 @@ var LoginDialog = GObject.registerClass({
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
+
+ if (this._disableUserList && this._timedLoginUserListHold)
+ this._timedLoginUserListHold.release();
}
}
@@ -1020,16 +1023,31 @@ var LoginDialog = GObject.registerClass({
let loginItem = null;
let animationTime;
- let tasks = [() => this._waitForItemForUser(userName),
+ let tasks = [() => {
+ if (this._disableUserList)
+ return;
+
+ this._timedLoginUserListHold = this._waitForItemForUser(userName);
+
+ return this._timedLoginUserListHold;
+ },
() => {
- loginItem = this._userList.getItemFromUserName(userName);
+ this._timedLoginUserListHold = null;
+
+
+ loginItem = this._disableUserList
+ ? this._authPrompt
+ : this._userList.getItemFromUserName(userName);
// If there is an animation running on the item, reset it.
loginItem.hideTimedLoginIndicator();
},
() => {
+ if (this._disableUserList)
+ return;
+
// If we're just starting out, start on the right item.
if (!this._userManager.is_loaded) {
this._userList.jumpToItem(loginItem);
@@ -1051,6 +1069,9 @@ var LoginDialog = GObject.registerClass({
},
() => {
+ if (this._disableUserList)
+ return;
+
// If idle timeout is done, make sure the timed login indicator is shown
if (delay > _TIMED_LOGIN_IDLE_THRESHOLD &&
this._authPrompt.actor.visible)
--
2.26.2

@ -0,0 +1,488 @@
From 7b514e637837e00372e20fa52f841e993966b734 Mon Sep 17 00:00:00 2001
From: Umang Jain <mailumangjain@gmail.com>
Date: Fri, 13 Dec 2019 13:36:14 +0530
Subject: [PATCH 1/7] shellEntry: Add CapsLockWarning class
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
---
data/theme/gnome-shell-sass/_common.scss | 5 +++
js/ui/shellEntry.js | 39 +++++++++++++++++++++++-
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index b1eeb0ce97..19a736ab7d 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -391,6 +391,11 @@ StScrollBar {
padding-bottom: 8px;
}
+ .prompt-dialog-caps-lock-warning {
+ @extend .prompt-dialog-error-label;
+ padding-left: 6.2em;
+ }
+
.prompt-dialog-info-label {
font-size: 10pt;
padding-bottom: 8px;
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
index 79f1aad3e7..c1738c4064 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -1,6 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-const { Clutter, Shell, St } = imports.gi;
+const { Clutter, GObject, Pango, Shell, St } = imports.gi;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
@@ -170,3 +170,40 @@ function addContextMenu(entry, params) {
entry._menuManager = null;
});
}
+
+var CapsLockWarning = GObject.registerClass(
+class CapsLockWarning extends St.Label {
+ _init(params) {
+ let defaultParams = { style_class: 'prompt-dialog-error-label' };
+ super._init(Object.assign(defaultParams, params));
+
+ this.text = _('Caps lock is on.');
+
+ this._keymap = Clutter.get_default_backend().get_keymap();
+
+ this.connect('notify::mapped', () => {
+ if (this.is_mapped()) {
+ this.stateChangedId = this._keymap.connect('state-changed',
+ this._updateCapsLockWarningOpacity.bind(this));
+ } else {
+ this._keymap.disconnect(this.stateChangedId);
+ this.stateChangedId = 0;
+ }
+
+ this._updateCapsLockWarningOpacity();
+ });
+
+ this.connect('destroy', () => {
+ if (this.stateChangedId > 0)
+ this._keymap.disconnect(this.stateChangedId);
+ });
+
+ this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this.clutter_text.line_wrap = true;
+ }
+
+ _updateCapsLockWarningOpacity() {
+ let capsLockOn = this._keymap.get_caps_lock_state();
+ this.opacity = capsLockOn ? 255 : 0;
+ }
+});
--
2.21.1
From aa4938f261454f85c782e59e40d4e5a9e1a01dbc Mon Sep 17 00:00:00 2001
From: Umang Jain <mailumangjain@gmail.com>
Date: Wed, 18 Dec 2019 01:33:45 +0530
Subject: [PATCH 2/7] js: Add caps-lock Warning to the dialogs
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/619
---
js/gdm/authPrompt.js | 4 ++++
js/ui/components/keyring.js | 6 ++++++
js/ui/components/networkAgent.js | 8 ++++++++
js/ui/components/polkitAgent.js | 2 ++
js/ui/shellMountOperation.js | 3 +++
5 files changed, 23 insertions(+)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 71069e93b8..3ce9fd0d01 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -113,6 +113,9 @@ var AuthPrompt = class {
this._entry.grab_key_focus();
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
+ this.actor.add_child(this._capsLockWarningLabel);
+
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
@@ -432,6 +435,7 @@ var AuthPrompt = class {
setPasswordChar(passwordChar) {
this._entry.clutter_text.set_password_char(passwordChar);
this._entry.menu.isPassword = passwordChar != '';
+ this._capsLockWarningLabel.visible = passwordChar !== '';
}
setQuestion(question) {
diff --git a/js/ui/components/keyring.js b/js/ui/components/keyring.js
index 0d9f1e4663..3512fb63b1 100644
--- a/js/ui/components/keyring.js
+++ b/js/ui/components/keyring.js
@@ -128,6 +128,12 @@ var KeyringDialog = class extends ModalDialog.ModalDialog {
this.prompt.set_password_actor(this._passwordEntry ? this._passwordEntry.clutter_text : null);
this.prompt.set_confirm_actor(this._confirmEntry ? this._confirmEntry.clutter_text : null);
+ if (this._passwordEntry || this._confirmEntry) {
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
+ layout.attach(this._capsLockWarningLabel, 1, row, 1, 1);
+ row++;
+ }
+
if (this.prompt.choice_visible) {
let choice = new CheckBox.CheckBox();
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
diff --git a/js/ui/components/networkAgent.js b/js/ui/components/networkAgent.js
index f871c732d9..32d40fb2b9 100644
--- a/js/ui/components/networkAgent.js
+++ b/js/ui/components/networkAgent.js
@@ -95,6 +95,14 @@ var NetworkSecretDialog = class extends ModalDialog.ModalDialog {
secret.entry.clutter_text.set_password_char('\u25cf');
}
+ if (this._content.secrets.some(s => s.password)) {
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
+ if (rtl)
+ layout.attach(this._capsLockWarningLabel, 0, pos, 1, 1);
+ else
+ layout.attach(this._capsLockWarningLabel, 1, pos, 1, 1);
+ }
+
contentBox.messageBox.add(secretTable);
if (flags & NM.SecretAgentGetSecretsFlags.WPS_PBC_ACTIVE) {
diff --git a/js/ui/components/polkitAgent.js b/js/ui/components/polkitAgent.js
index 21feb40903..734a217335 100644
--- a/js/ui/components/polkitAgent.js
+++ b/js/ui/components/polkitAgent.js
@@ -108,6 +108,8 @@ var AuthenticationDialog = class extends ModalDialog.ModalDialog {
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide();
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning({ style_class: 'prompt-dialog-caps-lock-warning' });
+ content.messageBox.add(this._capsLockWarningLabel);
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js
index f976f400f4..3a2377ddaf 100644
--- a/js/ui/shellMountOperation.js
+++ b/js/ui/shellMountOperation.js
@@ -305,6 +305,9 @@ var ShellMountPasswordDialog = class extends ModalDialog.ModalDialog {
this._passwordBox.add(this._passwordEntry, {expand: true });
this.setInitialKeyFocus(this._passwordEntry);
+ this._capsLockWarningLabel = new ShellEntry.CapsLockWarning();
+ content.messageBox.add(this._capsLockWarningLabel);
+
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
text: _("Sorry, that didnt work. Please try again.") });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
--
2.21.1
From 016cbd971711665844d40ec678d2779c160f791b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 23 Jan 2020 22:37:06 +0100
Subject: [PATCH 3/7] shellEntry: Make signal id variable private
Signal connection IDs should be private variables, so make this one
private.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/952
---
js/ui/shellEntry.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
index c1738c4064..cd7c9a6c88 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -183,19 +183,19 @@ class CapsLockWarning extends St.Label {
this.connect('notify::mapped', () => {
if (this.is_mapped()) {
- this.stateChangedId = this._keymap.connect('state-changed',
+ this._stateChangedId = this._keymap.connect('state-changed',
this._updateCapsLockWarningOpacity.bind(this));
} else {
- this._keymap.disconnect(this.stateChangedId);
- this.stateChangedId = 0;
+ this._keymap.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
}
this._updateCapsLockWarningOpacity();
});
this.connect('destroy', () => {
- if (this.stateChangedId > 0)
- this._keymap.disconnect(this.stateChangedId);
+ if (this._stateChangedId > 0)
+ this._keymap.disconnect(this._stateChangedId);
});
this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
--
2.21.1
From ba65f9066d72731e345a5aced61f35d39c1c1376 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 23 Jan 2020 23:26:45 +0100
Subject: [PATCH 4/7] theme: Move caps-lock warning to entry widget stylesheet
The caps-lock warning is more related to entries than dialogs and is
also used in gdm, which is not realated to dialogs at all. Rename the
css class to caps-lock-warning-label and move it to the entry
stylesheet.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/952
---
data/theme/gnome-shell-sass/_common.scss | 12 +++++++-----
js/ui/shellEntry.js | 2 +-
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index 19a736ab7d..4661533de2 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -94,6 +94,13 @@ StEntry {
}
}
+.caps-lock-warning-label {
+ padding-left: 6.2em;
+ @include fontsize($font-size - 1);
+ color: $warning_color;
+}
+
+
/* Scrollbars */
@@ -391,11 +398,6 @@ StScrollBar {
padding-bottom: 8px;
}
- .prompt-dialog-caps-lock-warning {
- @extend .prompt-dialog-error-label;
- padding-left: 6.2em;
- }
-
.prompt-dialog-info-label {
font-size: 10pt;
padding-bottom: 8px;
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
index cd7c9a6c88..46eba88d54 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -174,7 +174,7 @@ function addContextMenu(entry, params) {
var CapsLockWarning = GObject.registerClass(
class CapsLockWarning extends St.Label {
_init(params) {
- let defaultParams = { style_class: 'prompt-dialog-error-label' };
+ let defaultParams = { style_class: 'caps-lock-warning-label' };
super._init(Object.assign(defaultParams, params));
this.text = _('Caps lock is on.');
--
2.21.1
From afd764c82febe21aec70bdfc19d256f3401530e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Thu, 23 Jan 2020 22:36:09 +0100
Subject: [PATCH 5/7] shellEntry: Hide caps lock warning and use animation to
show it
Since the caps-lock warning adds a lot of spacing to dialogs and the
lock screen, hide it by default and only show it when necessary. To make
the transition smooth instead of just showing the label, animate it in
using the height and opacity.
Also add some bottom padding to the label so we can show or hide that
padding, too.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/952
---
data/theme/gnome-shell-sass/_common.scss | 1 +
js/ui/shellEntry.js | 33 ++++++++++++++++++------
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index 4661533de2..9e0751c8c5 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -95,6 +95,7 @@ StEntry {
}
.caps-lock-warning-label {
+ padding-bottom: 8px;
padding-left: 6.2em;
@include fontsize($font-size - 1);
color: $warning_color;
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
index 46eba88d54..fc8ee37a9a 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -6,6 +6,7 @@ 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;
var EntryMenu = class extends PopupMenu.PopupMenu {
constructor(entry) {
@@ -179,31 +180,47 @@ class CapsLockWarning extends St.Label {
this.text = _('Caps lock is on.');
+ this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+ this.clutter_text.line_wrap = true;
+
this._keymap = Clutter.get_default_backend().get_keymap();
this.connect('notify::mapped', () => {
if (this.is_mapped()) {
this._stateChangedId = this._keymap.connect('state-changed',
- this._updateCapsLockWarningOpacity.bind(this));
+ () => this._sync(true));
} else {
this._keymap.disconnect(this._stateChangedId);
this._stateChangedId = 0;
}
- this._updateCapsLockWarningOpacity();
+ this._sync(false);
});
this.connect('destroy', () => {
- if (this._stateChangedId > 0)
+ if (this._stateChangedId)
this._keymap.disconnect(this._stateChangedId);
});
-
- this.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
- this.clutter_text.line_wrap = true;
}
- _updateCapsLockWarningOpacity() {
+ _sync(animate) {
let capsLockOn = this._keymap.get_caps_lock_state();
- this.opacity = capsLockOn ? 255 : 0;
+
+ Tweener.removeTweens(this);
+
+ this.natural_height_set = false;
+ let [, height] = this.get_preferred_height(-1);
+ this.natural_height_set = true;
+
+ Tweener.addTween(this, {
+ height: capsLockOn ? height : 0,
+ opacity: capsLockOn ? 255 : 0,
+ time: animate ? 0.2 : 0,
+ transition: 'easeOutQuad',
+ onComplete: () => {
+ if (capsLockOn)
+ this.height = -1;
+ },
+ });
}
});
--
2.21.1
From 1ef3dafb51da380c54635d0565dc098e40bbb3e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 29 Jan 2020 17:48:57 +0100
Subject: [PATCH 6/7] js: Initialize some properties
Otherwise those can result in the (harmless) "reference to undefined
property" warnings.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/970
---
js/ui/overview.js | 1 +
js/ui/shellEntry.js | 1 +
2 files changed, 2 insertions(+)
diff --git a/js/ui/overview.js b/js/ui/overview.js
index dc6ad1821b..5bad4cbd62 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -80,6 +80,7 @@ var Overview = class {
constructor() {
this._overviewCreated = false;
this._initCalled = false;
+ this._visible = false;
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
this._sessionUpdated();
diff --git a/js/ui/shellEntry.js b/js/ui/shellEntry.js
index fc8ee37a9a..55267e7c87 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -184,6 +184,7 @@ class CapsLockWarning extends St.Label {
this.clutter_text.line_wrap = true;
this._keymap = Clutter.get_default_backend().get_keymap();
+ this._stateChangedId = 0;
this.connect('notify::mapped', () => {
if (this.is_mapped()) {
--
2.21.1
From 273f7adb43cfee907342d017e1454ea90d42d262 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 21 Feb 2020 19:38:53 +0100
Subject: [PATCH 7/7] shellEntry: Restore natural-height-set instead of forcing
it
If we are transitioning the label from 0 to its natural height, we
must set natural-height-set again after querying the preferred height,
otherwise Clutter would skip the transition.
However when transitioning in the opposite direction, setting the
property to true can go horribly wrong:
If the actor hasn't been allocated before, it will store a fixed
natural height of 0. But as there is no fixed min-height, we can
end up with min-height > natural-height, which is a fatal error.
(This isn't an issue when *actually* setting a fixed height, as
that will set both natural and minimum height)
So instead of always setting natural-height-set to true, restore
its previous value to fix the issue.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/2255
---
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 55267e7c87..4a30b22f7a 100644
--- a/js/ui/shellEntry.js
+++ b/js/ui/shellEntry.js
@@ -209,9 +209,10 @@ class CapsLockWarning extends St.Label {
Tweener.removeTweens(this);
+ const naturalHeightSet = this.natural_height_set;
this.natural_height_set = false;
let [, height] = this.get_preferred_height(-1);
- this.natural_height_set = true;
+ this.natural_height_set = naturalHeightSet;
Tweener.addTween(this, {
height: capsLockOn ? height : 0,
--
2.21.1

@ -0,0 +1,145 @@
From 7bdd1962213a37f6218fe15ea1a4062dd318672a Mon Sep 17 00:00:00 2001
From: Will Thompson <wjt@endlessm.com>
Date: Wed, 28 Aug 2019 15:39:44 +0100
Subject: [PATCH 1/2] global: Don't trust persistent/runtime state data
An Endless OS system was found in the wild with a malformed
.local/share/gnome-shell/notifications. When deserialized in Python,
after passing trusted=True to g_variant_new_from_bytes(), the first
element of the first struct in the array looks like this:
In [41]: _38.get_child_value(0).get_child_value(0)
Out[41]: GLib.Variant('s', '\Uffffffff\Uffffffff\Uffffffff\Uffffffff\Uffffffff')
When deserialised in GJS, we get:
gjs> v.get_child_value(0).get_child_value(0)
[object variant of type "s"]
gjs> v.get_child_value(0).get_child_value(0).get_string()
typein:43:1 malformed UTF-8 character sequence at offset 0
@typein:43:1
@<stdin>:1:34
While g_variant_new_from_bytes() doesn't have much to say about its
'trusted' parameter, g_variant_new_from_data() does:
> If data is trusted to be serialised data in normal form then trusted
> should be TRUE. This applies to serialised data created within this
> process or read from a trusted location on the disk (such as a file
> installed in /usr/lib alongside your application). You should set
> trusted to FALSE if data is read from the network, a file in the
> user's home directory, etc.
Persistent state is read from the user's home directory, so it should
not be trusted. With trusted=False, the string value above comes out as
"".
I don't have an explanation for how this file ended up being malformed.
I also don't have an explanation for when this started crashing: my
guess is that recent GJS became stricter about validating UTF-8 but I
could be wrong!
https://gitlab.gnome.org/GNOME/gnome-shell/issues/1552
---
src/shell-global.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shell-global.c b/src/shell-global.c
index 4b33778e0..33046f614 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -1707,7 +1707,7 @@ load_variant (GFile *dir,
else
{
GBytes *bytes = g_mapped_file_get_bytes (mfile);
- res = g_variant_new_from_bytes (G_VARIANT_TYPE (property_type), bytes, TRUE);
+ res = g_variant_new_from_bytes (G_VARIANT_TYPE (property_type), bytes, FALSE);
g_bytes_unref (bytes);
g_mapped_file_unref (mfile);
}
--
2.35.1
From 13dcb3e4400b92a0d2f548e88b70b358240d462c Mon Sep 17 00:00:00 2001
From: Will Thompson <wjt@endlessm.com>
Date: Wed, 28 Aug 2019 15:38:03 +0100
Subject: [PATCH 2/2] notificationDaemon: Catch exceptions while loading
notifications
An Endless OS system was found in the wild with a malformed
.local/share/gnome-shell/notifications which causes _loadNotifications()
to raise an exception. This exception was not previously handled and
bubbles all the way out to gnome_shell_plugin_start(), whereupon the
shell exit(1)s. The user could no longer log into their computer.
Handle exceptions from _loadNotifications(), log them, and attempt to
continue. Ensure that this._isLoading is set to 'false' even on error,
so that future calls to _saveNotifications() can overwrite the (corrupt)
state file.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/1552
---
js/ui/notificationDaemon.js | 42 ++++++++++++++++++++-----------------
1 file changed, 23 insertions(+), 19 deletions(-)
diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js
index 4bdede841..dbe673b88 100644
--- a/js/ui/notificationDaemon.js
+++ b/js/ui/notificationDaemon.js
@@ -749,29 +749,33 @@ var GtkNotificationDaemon = class GtkNotificationDaemon {
_loadNotifications() {
this._isLoading = true;
- let value = global.get_persistent_state('a(sa(sv))', 'notifications');
- if (value) {
- let sources = value.deep_unpack();
- sources.forEach(([appId, notifications]) => {
- if (notifications.length == 0)
- return;
-
- let source;
- try {
- source = this._ensureAppSource(appId);
- } catch(e) {
- if (e instanceof InvalidAppError)
+ try {
+ let value = global.get_persistent_state('a(sa(sv))', 'notifications');
+ if (value) {
+ let sources = value.deep_unpack();
+ sources.forEach(([appId, notifications]) => {
+ if (notifications.length == 0)
return;
- throw e;
- }
- notifications.forEach(([notificationId, notification]) => {
- source.addNotification(notificationId, notification.deep_unpack(), false);
+ let source;
+ try {
+ source = this._ensureAppSource(appId);
+ } catch (e) {
+ if (e instanceof InvalidAppError)
+ return;
+ throw e;
+ }
+
+ notifications.forEach(([notificationId, notification]) => {
+ source.addNotification(notificationId, notification.deep_unpack(), false);
+ });
});
- });
+ }
+ } catch (e) {
+ logError(e, 'Failed to load saved notifications');
+ } finally {
+ this._isLoading = false;
}
-
- this._isLoading = false;
}
_saveNotifications() {
--
2.35.1

@ -0,0 +1,167 @@
From a57132816ac7bd93d6875fee0a6c5b273177ac8d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 d7f53a92e..d421a8856 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -169,7 +169,7 @@ var AuthPrompt = class {
this._updateNextButtonSensitivity(this._entry.text.length > 0);
this._entry.clutter_text.connect('text-changed', () => {
- if (!this._userVerifier.hasPendingMessages)
+ if (!this._userVerifier.hasPendingMessages && this._queryingService && !this._preemptiveAnswer)
this._fadeOutMessage();
this._updateNextButtonSensitivity(this._entry.text.length > 0 || this.verificationStatus == AuthPromptStatus.VERIFYING);
--
2.21.0
From 50af703ea95f2b73733c38e66c9c251663a51744 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 30 Sep 2015 14:36:33 -0400
Subject: [PATCH 2/3] authPrompt: don't spin unless answering question
---
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 d421a8856..62c5bd078 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -60,8 +60,8 @@ var AuthPrompt = class {
this.connect('next', () => {
this.updateSensitivity(false);
- this.startSpinning();
if (this._queryingService) {
+ this.startSpinning();
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
this._preemptiveAnswer = this._entry.text;
--
2.21.0
From b89be880936ad9dd145eb43890ac72d03c37785d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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, 37 insertions(+)
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 62c5bd078..27eb31a89 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -6,6 +6,7 @@ const Signals = imports.signals;
const Animation = imports.ui.animation;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
+const Meta = imports.gi.Meta;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
@@ -41,6 +42,8 @@ var AuthPrompt = class {
this._gdmClient = gdmClient;
this._mode = mode;
+ this._idleMonitor = Meta.IdleMonitor.get_core();
+
let reauthenticationOnly;
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
reauthenticationOnly = true;
@@ -65,6 +68,11 @@ var AuthPrompt = class {
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;
+ }
}
});
@@ -128,6 +136,11 @@ var AuthPrompt = class {
}
_onDestroy() {
+ if (this._preemptiveAnswerWatchId) {
+ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId);
+ this._preemptiveAnswerWatchId = 0;
+ }
+
this._userVerifier.destroy();
this._userVerifier = null;
}
@@ -342,6 +355,11 @@ var AuthPrompt = class {
}
setQuestion(question) {
+ if (this._preemptiveAnswerWatchId) {
+ this._idleMonitor.remove_watch(this._preemptiveAnswerWatchId);
+ this._preemptiveAnswerWatchId = 0;
+ }
+
this._label.set_text(question);
this._label.show();
@@ -427,6 +445,19 @@ var AuthPrompt = class {
}
}
+ _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;
@@ -434,6 +465,12 @@ var AuthPrompt = class {
this.nextButton.label = _("Next");
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.21.0

@ -0,0 +1,114 @@
From 8ce91c85fe052d1a9f4fed0743bceae7d9654aa0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 fda782d1e..bb43c96e7 100644
--- a/js/misc/smartcardManager.js
+++ b/js/misc/smartcardManager.js
@@ -112,5 +112,12 @@ var SmartcardManager = class {
return true;
}
+ loggedInWithToken() {
+ if (this._loginToken)
+ return true;
+
+ return false;
+ }
+
};
Signals.addSignalMethods(SmartcardManager.prototype);
--
2.21.0
From 6decf5560d309579760e10048533d3bd9bc56c3c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 2e9935250..2b80e1dd9 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -126,7 +126,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 = Fprint.FprintManager();
this._smartcardManager = SmartcardManager.getSmartcardManager();
@@ -138,6 +137,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',
@@ -407,7 +408,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.21.0
From dd844c98c3450dd1b21bcc580b51162c1b00ed2a Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
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 2b80e1dd9..6e940d2ab 100644
--- a/js/gdm/util.js
+++ b/js/gdm/util.js
@@ -327,6 +327,8 @@ var ShellUserVerifier = class {
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
this._preemptingService = null;
+ this._updateDefaultService();
+
this.emit('smartcard-status-changed');
}
}
--
2.21.0

File diff suppressed because it is too large Load Diff

@ -0,0 +1,421 @@
From a518c9f57e5fe9c6b5ece5c6cb0534a83f0b2f2d Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 15 Jul 2019 13:52:58 -0400
Subject: [PATCH 1/8] appDisplay: Don't leak duplicate items in AppView
If an icon already exists in an app view with the same id, the
duplicate is not added on a call to addItem. Unfortunately,
since it's not added, the icon actor gets orphaned and leaked.
This commit address the problem by introducing a new hasItem
method and disallowing callers to call addItem with a duplicate
in the first place.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index a07db6573..fa22f47e0 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -143,10 +143,14 @@ class BaseAppView {
return this._allItems;
}
+ hasItem(id) {
+ return this._items[id] !== undefined;
+ }
+
addItem(icon) {
let id = icon.id;
- if (this._items[id] !== undefined)
- return;
+ if (this.hasItem(id))
+ throw new Error(`icon with id ${id} already added to view`)
this._allItems.push(icon);
this._items[id] = icon;
@@ -386,6 +390,8 @@ var AllView = class AllView extends BaseAppView {
let folders = this._folderSettings.get_strv('folder-children');
folders.forEach(id => {
+ if (this.hasItem(id))
+ return;
let path = this._folderSettings.path + 'folders/' + id + '/';
let icon = new FolderIcon(id, path, this);
icon.connect('name-changed', this._itemNameChanged.bind(this));
@@ -1165,7 +1171,10 @@ var FolderIcon = class FolderIcon {
let excludedApps = this._folder.get_strv('excluded-apps');
let appSys = Shell.AppSystem.get_default();
let addAppId = appId => {
- if (excludedApps.indexOf(appId) >= 0)
+ if (this.view.hasItem(appId))
+ return;
+
+ if (excludedApps.includes(appId))
return;
let app = appSys.lookup_app(appId);
--
2.23.0
From 2b6aa9aed98c4854c2ad015879ddcb8d2bf91e9e Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 22 Jul 2019 11:06:30 -0400
Subject: [PATCH 2/8] iconGrid: Clear meta_later callback on destruction
The IconGrid code sometimes sets up a callback to be invoked
later right before being destroyed.
This commit adds a destroy handler to cancel the callback.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/iconGrid.js | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index d51a443e8..1f05e67f3 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -210,6 +210,8 @@ var IconGrid = GObject.registerClass({
this.rightPadding = 0;
this.leftPadding = 0;
+ this._updateIconSizesLaterId = 0;
+
this._items = [];
this._clonesAnimating = [];
// Pulled from CSS, but hardcode some defaults here
@@ -227,6 +229,14 @@ var IconGrid = GObject.registerClass({
this.connect('actor-added', this._childAdded.bind(this));
this.connect('actor-removed', this._childRemoved.bind(this));
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._updateIconSizesLaterId) {
+ Meta.later_remove (this._updateIconSizesLaterId);
+ this._updateIconSizesLaterId = 0;
+ }
}
_keyFocusIn(actor) {
@@ -757,12 +767,14 @@ var IconGrid = GObject.registerClass({
this._updateSpacingForSize(availWidth, availHeight);
}
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
- this._updateIconSizes.bind(this));
+ if (!this._updateIconSizesLaterId)
+ this._updateIconSizesLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
+ this._updateIconSizes.bind(this));
}
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
_updateIconSizes() {
+ this._updateIconSizesLaterId = 0;
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
let newIconSize = Math.floor(ICON_SIZE * scale);
for (let i in this._items) {
--
2.23.0
From 14a2650548a5104d6a3ec7a1174a23264d79030a Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 22 Jul 2019 11:02:10 -0400
Subject: [PATCH 3/8] appDisplay: Add AppFolderPopup destroy handler
At the moment AppFolderPopup calls popdown on destruction,
which leads to open-state-changed getting emitted after
the actor associated with the popup is destroyed.
This commit handles ungrabbing and closing from an
actor destroy handler to side-step the open-state-changed
signal.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index fa22f47e0..b75d095d5 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1329,6 +1329,15 @@ var AppFolderPopup = class AppFolderPopup {
});
this._grabHelper.addActor(Main.layoutManager.overviewGroup);
this.actor.connect('key-press-event', this._onKeyPress.bind(this));
+ this.actor.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._isOpen) {
+ this._isOpen = false;
+ this._grabHelper.ungrab({ actor: this.actor });
+ this._grabHelper = null;
+ }
}
_onKeyPress(actor, event) {
--
2.23.0
From c9fcb2d23141694ffa2182df20ba75687b01dacc Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 18 Jul 2019 10:06:38 -0400
Subject: [PATCH 4/8] appDisplay: Clear AllView reference to current popup when
destroyed
AllView contains a reference to the current popup that lingers after
the popup is destroyed.
This commit fixes that, by explicitly nullifying when appropriate.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index b75d095d5..dabf63bfd 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -300,6 +300,7 @@ var AllView = class AllView extends BaseAppView {
this._eventBlocker.add_action(this._clickAction);
this._displayingPopup = false;
+ this._currentPopupDestroyId = 0;
this._availWidth = 0;
this._availHeight = 0;
@@ -589,7 +590,22 @@ var AllView = class AllView extends BaseAppView {
this._stack.add_actor(popup.actor);
popup.connect('open-state-changed', (popup, isOpen) => {
this._eventBlocker.reactive = isOpen;
- this._currentPopup = isOpen ? popup : null;
+
+ if (this._currentPopup) {
+ this._currentPopup.actor.disconnect(this._currentPopupDestroyId);
+ this._currentPopupDestroyId = 0;
+ }
+
+ this._currentPopup = null;
+
+ if (isOpen) {
+ this._currentPopup = popup;
+ this._currentPopupDestroyId = popup.actor.connect('destroy', () => {
+ this._currentPopup = null;
+ this._currentPopupDestroyId = 0;
+ this._eventBlocker.reactive = false;
+ });
+ }
this._updateIconOpacities(isOpen);
if(!isOpen)
this._closeSpaceForPopup();
--
2.23.0
From b7a3fd7fa4527ba9411dcd18debe6ccf88c34dc0 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Mon, 22 Jul 2019 10:57:57 -0400
Subject: [PATCH 5/8] appDisplay: Add destroy handler for FolderIcon
It is important that the FolderView of a FolderIcon always
gets destroyed before the AppFolderPopup, since the view
may or may not be in the popup, and the view should
get cleaned up exactly once in either case.
This commit adds a destroy handler on FolderIcon to ensure
things get taken down in the right order, and to make sure
the view isn't leaked if it's not yet part of the popup.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index dabf63bfd..5a8f4f1bf 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1156,6 +1156,7 @@ var FolderIcon = class FolderIcon {
this.view.actor.vscroll.adjustment.value = 0;
this._openSpaceForPopup();
});
+ this.actor.connect('destroy', this.onDestroy.bind(this));
this.actor.connect('notify::mapped', () => {
if (!this.actor.mapped && this._popup)
this._popup.popdown();
@@ -1165,6 +1166,13 @@ var FolderIcon = class FolderIcon {
this._redisplay();
}
+ onDestroy() {
+ this.view.actor.destroy();
+
+ if (this._popup)
+ this._popup.actor.destroy();
+ }
+
getAppIds() {
return this.view.getAllItems().map(item => item.id);
}
--
2.23.0
From a90d7a97d21ffa596747cc8ecd0e3f500cb8a77c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 18 Jul 2019 14:49:30 -0400
Subject: [PATCH 6/8] appDisplay: Stop watching FolderIcon parent view when
destroyed
When a FolderIcon is opened, it asks the parent view to allocate
space for it, which takes time. Eventually, the space-ready
signal is emitted on the view and the icon can make use of the new
space with its popup. If the icon gets destroyed in the
interim, though, space-ready signal handler still fires.
This commit disconnects the signal handler so it doesn't get called
on a destroyed icon.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 5a8f4f1bf..062ff222c 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1169,6 +1169,11 @@ var FolderIcon = class FolderIcon {
onDestroy() {
this.view.actor.destroy();
+ if (this._spaceReadySignalId) {
+ this._parentView.disconnect(this._spaceReadySignalId);
+ this._spaceReadySignalId = 0;
+ }
+
if (this._popup)
this._popup.actor.destroy();
}
@@ -1240,8 +1245,9 @@ var FolderIcon = class FolderIcon {
}
_openSpaceForPopup() {
- let id = this._parentView.connect('space-ready', () => {
- this._parentView.disconnect(id);
+ this._spaceReadySignalId = this._parentView.connect('space-ready', () => {
+ this._parentView.disconnect(this._spaceReadySignalId);
+ this._spaceReadySignalId = 0;
this._popup.popup();
this._updatePopupPosition();
});
--
2.23.0
From b57ab33dadf0f31c5bf2c800806593e94784050c Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 18 Jul 2019 10:19:13 -0400
Subject: [PATCH 7/8] appDisplay: Add open method to FolderIcon
At the moment the only way to open a folder icon is to click on it;
there's no API to open the icon programmatically.
This commits adds an open method and makes the click handler use
it.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 062ff222c..c0c6e3663 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1151,11 +1151,7 @@ var FolderIcon = class FolderIcon {
this.view = new FolderView();
- this.actor.connect('clicked', () => {
- this._ensurePopup();
- this.view.actor.vscroll.adjustment.value = 0;
- this._openSpaceForPopup();
- });
+ this.actor.connect('clicked', this.open.bind(this));
this.actor.connect('destroy', this.onDestroy.bind(this));
this.actor.connect('notify::mapped', () => {
if (!this.actor.mapped && this._popup)
@@ -1178,6 +1174,12 @@ var FolderIcon = class FolderIcon {
this._popup.actor.destroy();
}
+ open() {
+ this._ensurePopup();
+ this.view.actor.vscroll.adjustment.value = 0;
+ this._openSpaceForPopup();
+ }
+
getAppIds() {
return this.view.getAllItems().map(item => item.id);
}
--
2.23.0
From baacab7922a56957d041aa59944c419b82e7a7e1 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 18 Jul 2019 11:13:27 -0400
Subject: [PATCH 8/8] appDisplay: Keep popup open on refresh
If the list of applications is refreshed we currently close
the open app folder.
This commit adds logic to reopen the app folder on reload.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
---
js/ui/appDisplay.js | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index c0c6e3663..7fad02cd0 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -345,6 +345,21 @@ var AllView = class AllView extends BaseAppView {
super.removeAll();
}
+ _redisplay() {
+ let openFolderId = null;
+ if (this._displayingPopup && this._currentPopup)
+ openFolderId = this._currentPopup._source.id;
+
+ super._redisplay();
+
+ if (openFolderId) {
+ let [folderToReopen] = this.folderIcons.filter(folder => folder.id == openFolderId);
+
+ if (folderToReopen)
+ folderToReopen.open();
+ }
+ }
+
_itemNameChanged(item) {
// If an item's name changed, we can pluck it out of where it's
// supposed to be and reinsert it where it's sorted.
--
2.23.0

@ -0,0 +1,101 @@
From 49d066234f9f528122bb40c5144b40d8b19a0071 Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Mon, 22 Aug 2022 12:52:19 +0200
Subject: [PATCH] Background: Avoid double dispose and actors recreations
Subject: [PATCH 1/2] background: Use Garbage Collector to dispose background:
The same Meta.Background could be used by multiple instances of background
actors, and so should not be disposed when the actor using it is destroyed.
Instead of calling `run_dispose` directly on it, just nullify the reference
on destroy method, leaving the job of doing the proper disposition to the
gabage collector that keeps the proper reference count on the Meta.Background.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/501
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/558
Subject: [PATCH 2/2] background: Group 'changed' signal emission
Background is monitoring the whole `org.gnome.desktop.background` gsettings keys
for changes connecting to the non-specialized 'changed' signal and re-emitting
this as-is.
This means that when the background is changed via control-center, we get
multiple 'changed' signal events from GSettings, and for each one of this we
recreate a Background and a BackgroundActor.
Avoid this by using an idle to delay the emission of the 'changed' signal
grouping the events.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/558
---
js/ui/background.js | 26 +++++++++++++++++++++-----
1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/js/ui/background.js b/js/ui/background.js
index 06e0388..2a404ae 100644
--- a/js/ui/background.js
+++ b/js/ui/background.js
@@ -257,14 +257,15 @@ var Background = class Background {
this._refreshAnimation();
});
- this._settingsChangedSignalId = this._settings.connect('changed', () => {
- this.emit('changed');
- });
+ this._settingsChangedSignalId =
+ this._settings.connect('changed', this._emitChangedSignal.bind(this));
this._load();
}
destroy() {
+ this.background = null;
+
this._cancellable.cancel();
this._removeAnimationTimeout();
@@ -288,6 +289,22 @@ var Background = class Background {
if (this._settingsChangedSignalId != 0)
this._settings.disconnect(this._settingsChangedSignalId);
this._settingsChangedSignalId = 0;
+
+ if (this._changedIdleId) {
+ GLib.source_remove(this._changedIdleId);
+ this._changedIdleId = 0;
+ }
+ }
+
+ _emitChangedSignal() {
+ if (this._changedIdleId)
+ return;
+
+ this._changedIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ this._changedIdleId = 0;
+ this.emit('changed');
+ return GLib.SOURCE_REMOVE;
+ });
}
updateResolution() {
@@ -343,7 +360,7 @@ var Background = class Background {
if (changedFile.equal(file)) {
let imageCache = Meta.BackgroundImageCache.get_default();
imageCache.purge(changedFile);
- this.emit('changed');
+ this._emitChangedSignal();
}
});
this._fileWatches[key] = signalId;
@@ -699,7 +716,6 @@ var BackgroundManager = class BackgroundManager {
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete() {
- oldBackgroundActor.background.run_dispose();
oldBackgroundActor.destroy();
}
});
--
2.35.3

@ -0,0 +1,224 @@
From 76eebb42ed4c76970a9debfc0cd41537923eccde Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
Date: Tue, 5 Dec 2017 02:41:50 +0100
Subject: [PATCH 1/2] tweener: Save handlers on target and remove them on
destroy
Saving handlers we had using the wrapper as a property of the object and delete
them when resetting the object state.
Without doing this an handler could be called on a destroyed target when this
happens on the onComplete callback.
https://bugzilla.gnome.org/show_bug.cgi?id=791233
---
js/ui/tweener.js | 63 ++++++++++++++++++++++++++++++++++++++----------
1 file changed, 50 insertions(+), 13 deletions(-)
diff --git a/js/ui/tweener.js b/js/ui/tweener.js
index bb9ea557c..c04cede25 100644
--- a/js/ui/tweener.js
+++ b/js/ui/tweener.js
@@ -63,30 +63,67 @@ function _getTweenState(target) {
return target.__ShellTweenerState;
}
+function _ensureHandlers(target) {
+ if (!target.__ShellTweenerHandlers)
+ target.__ShellTweenerHandlers = {};
+ return target.__ShellTweenerHandlers;
+}
+
function _resetTweenState(target) {
let state = target.__ShellTweenerState;
if (state) {
- if (state.destroyedId)
+ if (state.destroyedId) {
state.actor.disconnect(state.destroyedId);
+ delete state.destroyedId;
+ }
}
+ _removeHandler(target, 'onComplete', _tweenCompleted);
target.__ShellTweenerState = {};
}
function _addHandler(target, params, name, handler) {
- if (params[name]) {
- let oldHandler = params[name];
- let oldScope = params[name + 'Scope'];
- let oldParams = params[name + 'Params'];
- let eventScope = oldScope ? oldScope : target;
-
- params[name] = () => {
- oldHandler.apply(eventScope, oldParams);
- handler(target);
- };
- } else
- params[name] = () => { handler(target); };
+ let wrapperNeeded = false;
+ let tweenerHandlers = _ensureHandlers(target);
+
+ if (!(name in tweenerHandlers)) {
+ tweenerHandlers[name] = [];
+ wrapperNeeded = true;
+ }
+
+ let handlers = tweenerHandlers[name];
+ handlers.push(handler);
+
+ if (wrapperNeeded) {
+ if (params[name]) {
+ let oldHandler = params[name];
+ let oldScope = params[name + 'Scope'];
+ let oldParams = params[name + 'Params'];
+ let eventScope = oldScope ? oldScope : target;
+
+ params[name] = () => {
+ oldHandler.apply(eventScope, oldParams);
+ handlers.forEach((h) => h(target));
+ };
+ } else {
+ params[name] = () => { handlers.forEach((h) => h(target)); };
+ }
+ }
+}
+
+function _removeHandler(target, name, handler) {
+ let tweenerHandlers = _ensureHandlers(target);
+
+ if (name in tweenerHandlers) {
+ let handlers = tweenerHandlers[name];
+ let handlerIndex = handlers.indexOf(handler);
+
+ while (handlerIndex > -1) {
+ handlers.splice(handlerIndex, 1);
+ handlerIndex = handlers.indexOf(handler);
+ }
+ }
}
function _actorDestroyed(target) {
--
2.21.0
From 730f6f7d708a0cbcfcc75e4a1fba8512ac7c4c82 Mon Sep 17 00:00:00 2001
From: Cosimo Cecchi <cosimo@endlessm.com>
Date: Sun, 26 May 2019 08:31:07 -0700
Subject: [PATCH 2/2] windowAttentionHandler: disconnect signals before
destruction
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The 'destroy' signal is emitted at the end of the destroy() method.
However the implementation of destroy() can end up emitting one of the
signals we connect to on the window, causing us to re-enter destroy
from its callback.
That will in turn lead to some objects getting disposed twice, which
produces a stack trace like the following one.
This commit fixes the issue by overriding the destroy() method instead
of connecting to the signal, which allows us to disconnect the signal
handlers from the window at an earlier time and avoid re-entrancy.
--
gnome-shell[1082]: Object Gio.Settings (0x7f0af8143f00), has been already deallocated — impossible to access it. This might be caused by the object having been destroyed from C code using something such as destroy(), dispose(), or remove() vfuncs.
org.gnome.Shell.desktop[1082]: == Stack trace for context 0x5627f7d1e220 ==
org.gnome.Shell.desktop[1082]: #0 5627f9e801a8 i resource:///org/gnome/shell/ui/messageTray.js:238 (7f0aefa9eca0 @ 22)
org.gnome.Shell.desktop[1082]: #1 5627f9e80108 i resource:///org/gnome/shell/ui/messageTray.js:802 (7f0aefaa2ee0 @ 28)
org.gnome.Shell.desktop[1082]: #2 5627f9e80070 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:79 (7f0aef7b29d0 @ 62)
org.gnome.Shell.desktop[1082]: #3 7fffa69fbfc0 b self-hosted:979 (7f0aefa515e0 @ 440)
org.gnome.Shell.desktop[1082]: #4 5627f9e7ffe0 i resource:///org/gnome/shell/ui/messageTray.js:121 (7f0aefa9e1f0 @ 71)
org.gnome.Shell.desktop[1082]: #5 5627f9e7ff38 i resource:///org/gnome/shell/ui/messageTray.js:1408 (7f0aefaa58b0 @ 22)
org.gnome.Shell.desktop[1082]: #6 5627f9e7fe80 i resource:///org/gnome/shell/ui/messageTray.js:1237 (7f0aefaa51f0 @ 729)
org.gnome.Shell.desktop[1082]: #7 5627f9e7fde8 i resource:///org/gnome/shell/ui/messageTray.js:1055 (7f0aefaa3d30 @ 124)
org.gnome.Shell.desktop[1082]: #8 7fffa69ff8e0 b self-hosted:979 (7f0aefa515e0 @ 440)
org.gnome.Shell.desktop[1082]: #9 7fffa69ff9d0 b resource:///org/gnome/gjs/modules/signals.js:142 (7f0aefccb670 @ 386)
org.gnome.Shell.desktop[1082]: #10 5627f9e7fd58 i resource:///org/gnome/shell/ui/messageTray.js:479 (7f0aefaa0940 @ 50)
org.gnome.Shell.desktop[1082]: #11 5627f9e7fcb8 i resource:///org/gnome/shell/ui/messageTray.js:808 (7f0aefaa2ee0 @ 99)
org.gnome.Shell.desktop[1082]: #12 5627f9e7fc28 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:69 (7f0aef7b28b0 @ 13)
org.gnome.Shell.desktop[1082]: #13 5627f9e7fb80 i resource:///org/gnome/shell/ui/main.js:566 (7f0aefcd8820 @ 216)
org.gnome.Shell.desktop[1082]: #14 5627f9e7fad0 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:103 (7f0aef7b2c10 @ 27)
org.gnome.Shell.desktop[1082]: #15 5627f9e7fa58 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:43 (7f0aef7b2700 @ 17)
org.gnome.Shell.desktop[1082]: #16 7fffa6a03350 b resource:///org/gnome/gjs/modules/signals.js:142 (7f0aefccb670 @ 386)
org.gnome.Shell.desktop[1082]: #17 5627f9e7f9d0 i resource:///org/gnome/shell/ui/messageTray.js:471 (7f0aefaa08b0 @ 22)
org.gnome.Shell.desktop[1082]: #18 5627f9e7f950 i resource:///org/gnome/shell/ui/calendar.js:752 (7f0aefaabdc0 @ 22)
org.gnome.Shell.desktop[1082]: #19 7fffa6a048f0 b self-hosted:979 (7f0aefa515e0 @ 440)
org.gnome.Shell.desktop[1082]: == Stack trace for context 0x5627f7d1e220 ==
org.gnome.Shell.desktop[1082]: #0 5627f9e801a8 i resource:///org/gnome/shell/ui/messageTray.js:239 (7f0aefa9eca0 @ 42)
org.gnome.Shell.desktop[1082]: #1 5627f9e80108 i resource:///org/gnome/shell/ui/messageTray.js:802 (7f0aefaa2ee0 @ 28)
org.gnome.Shell.desktop[1082]: #2 5627f9e80070 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:79 (7f0aef7b29d0 @ 62)
org.gnome.Shell.desktop[1082]: #3 7fffa69fbfc0 b self-hosted:979 (7f0aefa515e0 @ 440)
org.gnome.Shell.desktop[1082]: #4 5627f9e7ffe0 i resource:///org/gnome/shell/ui/messageTray.js:121 (7f0aefa9e1f0 @ 71)
org.gnome.Shell.desktop[1082]: #5 5627f9e7ff38 i resource:///org/gnome/shell/ui/messageTray.js:1408 (7f0aefaa58b0 @ 22)
org.gnome.Shell.desktop[1082]: #6 5627f9e7fe80 i resource:///org/gnome/shell/ui/messageTray.js:1237 (7f0aefaa51f0 @ 729)
org.gnome.Shell.desktop[1082]: #7 5627f9e7fde8 i resource:///org/gnome/shell/ui/messageTray.js:1055 (7f0aefaa3d30 @ 124)
org.gnome.Shell.desktop[1082]: #8 7fffa69ff8e0 b self-hosted:979 (7f0aefa515e0 @ 440)
org.gnome.Shell.desktop[1082]: #9 7fffa69ff9d0 b resource:///org/gnome/gjs/modules/signals.js:142 (7f0aefccb670 @ 386)
org.gnome.Shell.desktop[1082]: #10 5627f9e7fd58 i resource:///org/gnome/shell/ui/messageTray.js:479 (7f0aefaa0940 @ 50)
org.gnome.Shell.desktop[1082]: #11 5627f9e7fcb8 i resource:///org/gnome/shell/ui/messageTray.js:808 (7f0aefaa2ee0 @ 99)
org.gnome.Shell.desktop[1082]: #12 5627f9e7fc28 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:69 (7f0aef7b28b0 @ 13)
org.gnome.Shell.desktop[1082]: #13 5627f9e7fb80 i resource:///org/gnome/shell/ui/main.js:566 (7f0aefcd8820 @ 216)
org.gnome.Shell.desktop[1082]: #14 5627f9e7fad0 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:103 (7f0aef7b2c10 @ 27)
org.gnome.Shell.desktop[1082]: #15 5627f9e7fa58 i resource:///org/gnome/shell/ui/windowAttentionHandler.js:43 (7f0aef7b2700 @ 17)
org.gnome.Shell.desktop[1082]: #16 7fffa6a03350 b resource:///org/gnome/gjs/modules/signals.js:142 (7f0aefccb670 @ 386)
org.gnome.Shell.desktop[1082]: #17 5627f9e7f9d0 i resource:///org/gnome/shell/ui/messageTray.js:471 (7f0aefaa08b0 @ 22)
org.gnome.Shell.desktop[1082]: #18 5627f9e7f950 i resource:///org/gnome/shell/ui/calendar.js:752 (7f0aefaabdc0 @ 22)
org.gnome.Shell.desktop[1082]: #19 7fffa6a048f0 b self-hosted:979 (7f0aefa515e0 @ 440)
gnome-shell[1082]: g_object_run_dispose: assertion 'G_IS_OBJECT (object)' failed
gnome-shell[1082]: Object Gio.Settings (0x7f0af8161750), has been already deallocated — impossible to access it. This might be caused by the object having been destroyed from C code using something such as destroy(), dispose(), or remove() vfuncs.
gnome-shell[1082]: g_object_run_dispose: assertion 'G_IS_OBJECT (object)' failed
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/555
---
js/ui/windowAttentionHandler.js | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/js/ui/windowAttentionHandler.js b/js/ui/windowAttentionHandler.js
index abdb8a444..a9a7111ba 100644
--- a/js/ui/windowAttentionHandler.js
+++ b/js/ui/windowAttentionHandler.js
@@ -69,8 +69,6 @@ var Source = class WindowAttentionSource extends MessageTray.Source {
() => { this.destroy(); }));
this.signalIDs.push(this._window.connect('unmanaged',
() => { this.destroy(); }));
-
- this.connect('destroy', this._onDestroy.bind(this));
}
_sync() {
@@ -79,13 +77,6 @@ var Source = class WindowAttentionSource extends MessageTray.Source {
this.destroy();
}
- _onDestroy() {
- for(let i = 0; i < this.signalIDs.length; i++) {
- this._window.disconnect(this.signalIDs[i]);
- }
- this.signalIDs = [];
- }
-
_createPolicy() {
if (this._app && this._app.get_app_info()) {
let id = this._app.get_id().replace(/\.desktop$/,'');
@@ -99,6 +90,14 @@ var Source = class WindowAttentionSource extends MessageTray.Source {
return this._app.create_icon_texture(size);
}
+ destroy(params) {
+ for (let i = 0; i < this.signalIDs.length; i++)
+ this._window.disconnect(this.signalIDs[i]);
+ this.signalIDs = [];
+
+ super.destroy(params);
+ }
+
open() {
Main.activateWindow(this._window);
}
--
2.21.0

@ -0,0 +1,161 @@
From 214c4f390faa40199c03a80594313760ffe9c5a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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

@ -0,0 +1,131 @@
From e2a1b737156804e2647e5de938c3d170c11b6ba4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 31 Jul 2020 20:40:36 +0200
Subject: [PATCH 1/2] status/network: Use D-Bus to launch Settings panels
For more obscure network configurations, we need to launch the
corresponding Settings panel with additional parameters, so we
cannot simply launch the .desktop file.
However we can do better than spawning a command line: Control center
exposes an application action we can use instead, so the process is
launched with the appropriate activation environment and startup
notification support.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1385
---
js/ui/status/network.js | 33 +++++++++++++++++++++++++++++----
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index f8991d02f..0e7e82ce0 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -15,6 +15,8 @@ const Util = imports.misc.util;
const { loadInterfaceXML } = imports.misc.fileUtils;
+Gio._promisify(Gio.DBusConnection.prototype, 'call', 'call_finish');
+
const NMConnectionCategory = {
INVALID: 'invalid',
WIRED: 'wired',
@@ -75,6 +77,30 @@ function ensureActiveConnectionProps(active, client) {
}
}
+function launchSettingsPanel(panel, ...args) {
+ const param = new GLib.Variant('(sav)',
+ [panel, args.map(s => new GLib.Variant('s', s))]);
+ const platformData = {
+ 'desktop-startup-id': new GLib.Variant('s',
+ '_TIME%s'.format(global.get_current_time())),
+ };
+ try {
+ Gio.DBus.session.call(
+ 'org.gnome.ControlCenter',
+ '/org/gnome/ControlCenter',
+ 'org.freedesktop.Application',
+ 'ActivateAction',
+ new GLib.Variant('(sava{sv})',
+ ['launch-panel', [param], platformData]),
+ null,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ null);
+ } catch (e) {
+ log('Failed to launch Settings panel: %s'.format(e.message));
+ }
+}
+
var NMConnectionItem = class {
constructor(section, connection) {
this._section = section;
@@ -534,8 +560,7 @@ var NMDeviceModem = class extends NMConnectionDevice {
}
_autoConnect() {
- Util.spawn(['gnome-control-center', 'network',
- 'connect-3g', this._device.get_path()]);
+ launchSettingsPanel('network', 'connect-3g', this._device.get_path());
}
_sessionUpdated() {
@@ -920,8 +945,8 @@ var NMWirelessDialog = class extends ModalDialog.ModalDialog {
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're
// handled in gnome-control-center
- Util.spawn(['gnome-control-center', 'wifi', 'connect-8021x-wifi',
- this._device.get_path(), accessPoints[0].get_path()]);
+ launchSettingsPanel('wifi', 'connect-8021x-wifi',
+ this._device.get_path(), accessPoints[0].get_path());
} else {
let connection = new NM.SimpleConnection();
this._client.add_and_activate_connection_async(connection, this._device, accessPoints[0].get_path(), null, null)
--
2.38.1
From 9ca1989fcc73157685742470c25f538d01d8df44 Mon Sep 17 00:00:00 2001
From: Xiaoguang Wang <xwang@suse.com>
Date: Mon, 21 Feb 2022 09:11:23 +0800
Subject: [PATCH 2/2] network: Get dbus path from NMDevice
In the NetworkManager new version the NMDevice.get_path returns pci
path, we need to use NM prototype to get device dbus path.
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4565
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2194>
---
js/ui/status/network.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index 0e7e82ce0..9d6a83b73 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -946,7 +946,7 @@ var NMWirelessDialog = class extends ModalDialog.ModalDialog {
// 802.1x-enabled APs require further configuration, so they're
// handled in gnome-control-center
launchSettingsPanel('wifi', 'connect-8021x-wifi',
- this._device.get_path(), accessPoints[0].get_path());
+ this._getDeviceDBusPath(), accessPoints[0].get_path());
} else {
let connection = new NM.SimpleConnection();
this._client.add_and_activate_connection_async(connection, this._device, accessPoints[0].get_path(), null, null)
@@ -956,6 +956,11 @@ var NMWirelessDialog = class extends ModalDialog.ModalDialog {
this.close();
}
+ _getDeviceDBusPath() {
+ // nm_object_get_path() is shadowed by nm_device_get_path()
+ return NM.Object.prototype.get_path.call(this._device);
+ }
+
_notifySsidCb(accessPoint) {
if (accessPoint.get_ssid() != null) {
accessPoint.disconnect(accessPoint._notifySsidId);
--
2.38.1

@ -0,0 +1,223 @@
From 530964cc6e5db02633434853debd96069dc2b8d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= <mail@3v1n0.net>
Date: Thu, 23 May 2019 06:12:56 +0200
Subject: [PATCH 1/6] realmd: Set login format to null on start and update if
invalid
We were checking an undefined property but that would lead to a a warning.
Instead we can consider the login format unset until is null, and in case
update it.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/700
---
js/gdm/realmd.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/js/gdm/realmd.js b/js/gdm/realmd.js
index 50f3c5899..04cd99787 100644
--- a/js/gdm/realmd.js
+++ b/js/gdm/realmd.js
@@ -21,6 +21,7 @@ var Manager = class {
'/org/freedesktop/realmd',
this._reloadRealms.bind(this))
this._realms = {};
+ this._loginFormat = null;
this._signalId = this._aggregateProvider.connect('g-properties-changed',
(proxy, properties) => {
@@ -86,7 +87,7 @@ var Manager = class {
}
get loginFormat() {
- if (this._loginFormat !== undefined)
+ if (this._loginFormat)
return this._loginFormat;
this._updateLoginFormat();
--
2.33.1
From 988e4b58d64fbf87f0c497315ff2506b269ff7c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 9 Jun 2020 19:42:21 +0200
Subject: [PATCH 2/6] 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 44818533a..b5115d7f7 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -696,7 +696,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.33.1
From 609a8e22e67b63da1e35167d8511400f22641368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 9 Jun 2020 19:48:06 +0200
Subject: [PATCH 3/6] 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 f3a22f034..7665de755 100644
--- a/src/st/st-shadow.c
+++ b/src/st/st-shadow.c
@@ -289,9 +289,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.33.1
From b57d6efccbeb139d6c7c1894f83caa7a26fd6bad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 5 Jan 2021 21:42:24 +0100
Subject: [PATCH 4/6] viewSelector: Don't set page parent during construction
gjs now aggressively garbage-collects objects that fall out of scope,
sometimes too aggressively:
- we pass a child as construct property to StBin
- as a result, the child's ::parent-set handler runs
- when calling clutter_actor_get_parent() from that
handler, the returned object is garbage-collected
*before* the constructor returns (and thus the
assignment that would keep it alive)
This is a bug on the gjs side that should be fixed, but we can easily
work around the issue by setting the child after constructing the
parent.
---
js/ui/viewSelector.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js
index 77146552d..6529ac9a5 100644
--- a/js/ui/viewSelector.js
+++ b/js/ui/viewSelector.js
@@ -301,11 +301,13 @@ var ViewSelector = class {
_addPage(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null });
- let page = new St.Bin({ child: actor,
- x_align: St.Align.START,
- y_align: St.Align.START,
- x_fill: true,
- y_fill: true });
+ let page = new St.Bin({
+ x_align: St.Align.START,
+ y_align: St.Align.START,
+ x_fill: true,
+ y_fill: true,
+ });
+ page.set_child(actor);
if (params.a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
else
--
2.33.1
From 0c76c91c3d16c8386a242daf367d66057364a5d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 23 Oct 2020 23:44:48 +0200
Subject: [PATCH 5/6] workspacesView: Don't set up MetaLater when unparented
We already do the check in the later handler, but if we got
unparented because the actor is destroyed, then the call to
get_parent() itself will trigger a (harmless but annoying)
warning.
---
js/ui/workspacesView.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index e302296a6..3270900b2 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -715,6 +715,9 @@ var WorkspacesDisplay = class {
oldParent.disconnect(this._notifyOpacityId);
this._notifyOpacityId = 0;
+ if (!this.actor.get_parent())
+ return;
+
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
let newParent = this.actor.get_parent();
if (!newParent)
--
2.33.1
From 4ba01f2fdada7e4b059a0f57a99dc3ff2ddfa8f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Fri, 26 Nov 2021 17:28:54 +0100
Subject: [PATCH 6/6] workspacesView: Remove later on destroy
We are careful not to schedule the later when the actor is destroyed,
however it is possible that one is still pending at that point (namely
if the actor was never shown).
---
js/ui/workspacesView.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 3270900b2..9dc05fca7 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -449,6 +449,11 @@ var WorkspacesDisplay = class {
this.actor._delegate = this;
this.actor.connect('notify::allocation', this._updateWorkspacesActualGeometry.bind(this));
this.actor.connect('parent-set', this._parentSet.bind(this));
+ this.actor.connect('destroy', () => {
+ if (this._laterId)
+ Meta.later_remove(this._laterId);
+ this._laterId = 0;
+ });
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', action => {
@@ -718,7 +723,7 @@ var WorkspacesDisplay = class {
if (!this.actor.get_parent())
return;
- Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+ this._laterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
let newParent = this.actor.get_parent();
if (!newParent)
return;
--
2.33.1

@ -0,0 +1,246 @@
From ed0699886f49e5dd8d6ca9ffb60ba17cd76a810f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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

@ -0,0 +1,38 @@
From 87104647f061892525236a71f304b63609960626 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 9 Mar 2017 14:43:30 +0100
Subject: [PATCH] appFavorites: Make firefox the default browser
---
data/org.gnome.shell.gschema.xml.in | 2 +-
js/ui/appFavorites.js | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in
index 24e2a75b0..2f50036d0 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -39,7 +39,7 @@
</description>
</key>
<key name="favorite-apps" type="as">
- <default>[ 'epiphany.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
+ <default>[ 'firefox.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers
diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js
index 657e15965..1e44a1655 100644
--- a/js/ui/appFavorites.js
+++ b/js/ui/appFavorites.js
@@ -49,6 +49,7 @@ const RENAMED_DESKTOP_IDS = {
'gnotski.desktop': 'org.gnome.Klotski.desktop',
'gtali.desktop': 'org.gnome.Tali.desktop',
'iagno.desktop': 'org.gnome.Reversi.desktop',
+ 'mozilla-firefox.desktop': 'firefox.desktop',
'nautilus.desktop': 'org.gnome.Nautilus.desktop',
'org.gnome.gnome-2048.desktop': 'org.gnome.TwentyFortyEight.desktop',
'org.gnome.taquin.desktop': 'org.gnome.Taquin.desktop',
--
2.21.0

@ -0,0 +1,25 @@
From d15a92aeaa075230f711921f4bcd929c49bfc97d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 9 Mar 2017 14:44:32 +0100
Subject: [PATCH] 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 40526187e..9d7e011fc 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -39,7 +39,7 @@
</description>
</key>
<key name="favorite-apps" type="as">
- <default>[ 'firefox.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop' ]</default>
+ <default>[ 'firefox.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop', 'gnome-terminal.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers
--
2.21.0

@ -0,0 +1,26 @@
From 53eba56c29c2c3f25bdfc4b73d1b9ce74ce2504b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 9 Mar 2017 14:44:03 +0100
Subject: [PATCH] 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 2f50036d0..40526187e 100644
--- a/data/org.gnome.shell.gschema.xml.in
+++ b/data/org.gnome.shell.gschema.xml.in
@@ -39,7 +39,7 @@
</description>
</key>
<key name="favorite-apps" type="as">
- <default>[ 'firefox.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop' ]</default>
+ <default>[ 'firefox.desktop', 'evolution.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Software.desktop', 'yelp.desktop' ]</default>
<summary>List of desktop file IDs for favorite applications</summary>
<description>
The applications corresponding to these identifiers
--
2.21.0

@ -0,0 +1,399 @@
From b42dd3f87ad5fb6c7ee139cb0de22e0fbb393ba2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 4 Jun 2019 19:22:26 +0000
Subject: [PATCH 1/2] workspaceSwitcherPopup: Support horizontal layout
While mutter supports a variety of different grid layouts (n columns/rows,
growing vertically or horizontally from any of the four corners), we
hardcode a fixed vertical layout of a single column.
Now that mutter exposes the actual layout to us, add support for a more
traditional horizontal layout as well.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/575
---
data/theme/gnome-shell-sass/_common.scss | 3 +-
js/ui/windowManager.js | 36 ++++++++--
js/ui/workspaceSwitcherPopup.js | 86 ++++++++++++++++++------
3 files changed, 98 insertions(+), 27 deletions(-)
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index 293ea2ab9..b1eeb0ce9 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -680,7 +680,8 @@ StScrollBar {
spacing: 8px;
}
- .ws-switcher-active-up, .ws-switcher-active-down {
+ .ws-switcher-active-up, .ws-switcher-active-down,
+ .ws-switcher-active-left, .ws-switcher-active-right {
height: 50px;
background-color: $selected_bg_color;
color: $selected_fg_color;
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index b9f5fef46..dfe1b4460 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -2145,6 +2145,8 @@ var WindowManager = class {
let [action,,,target] = binding.get_name().split('-');
let newWs;
let direction;
+ let vertical = workspaceManager.layout_rows == -1;
+ let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
if (action == 'move') {
// "Moving" a window to another workspace doesn't make sense when
@@ -2157,7 +2159,12 @@ var WindowManager = class {
}
if (target == 'last') {
- direction = Meta.MotionDirection.DOWN;
+ if (vertical)
+ direction = Meta.MotionDirection.DOWN;
+ else if (rtl)
+ direction = Meta.MotionDirection.LEFT;
+ else
+ direction = Meta.MotionDirection.RIGHT;
newWs = workspaceManager.get_workspace_by_index(workspaceManager.n_workspaces - 1);
} else if (isNaN(target)) {
// Prepend a new workspace dynamically
@@ -2173,16 +2180,33 @@ var WindowManager = class {
target--;
newWs = workspaceManager.get_workspace_by_index(target);
- if (workspaceManager.get_active_workspace().index() > target)
- direction = Meta.MotionDirection.UP;
- else
- direction = Meta.MotionDirection.DOWN;
+ if (workspaceManager.get_active_workspace().index() > target) {
+ if (vertical)
+ direction = Meta.MotionDirection.UP;
+ else if (rtl)
+ direction = Meta.MotionDirection.RIGHT;
+ else
+ direction = Meta.MotionDirection.LEFT;
+ } else {
+ if (vertical)
+ direction = Meta.MotionDirection.DOWN;
+ else if (rtl)
+ direction = Meta.MotionDirection.LEFT;
+ else
+ direction = Meta.MotionDirection.RIGHT;
+ }
}
- if (direction != Meta.MotionDirection.UP &&
+ if (workspaceManager.layout_rows == -1 &&
+ direction != Meta.MotionDirection.UP &&
direction != Meta.MotionDirection.DOWN)
return;
+ if (workspaceManager.layout_columns == -1 &&
+ direction != Meta.MotionDirection.LEFT &&
+ direction != Meta.MotionDirection.RIGHT)
+ return;
+
if (action == 'switch')
this.actionMoveWorkspace(newWs);
else
diff --git a/js/ui/workspaceSwitcherPopup.js b/js/ui/workspaceSwitcherPopup.js
index 26404eaab..d21c5de4d 100644
--- a/js/ui/workspaceSwitcherPopup.js
+++ b/js/ui/workspaceSwitcherPopup.js
@@ -17,41 +17,75 @@ class WorkspaceSwitcherPopupList extends St.Widget {
this._itemSpacing = 0;
this._childHeight = 0;
this._childWidth = 0;
+ this._orientation = global.workspace_manager.layout_rows == -1
+ ? Clutter.Orientation.VERTICAL
+ : Clutter.Orientation.HORIZONTAL;
this.connect('style-changed', () => {
this._itemSpacing = this.get_theme_node().get_length('spacing');
});
}
- vfunc_get_preferred_height(forWidth) {
+ _getPreferredSizeForOrientation(forSize) {
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let themeNode = this.get_theme_node();
- let availHeight = workArea.height;
- availHeight -= themeNode.get_vertical_padding();
+ let availSize;
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ availSize = workArea.width - themeNode.get_horizontal_padding();
+ else
+ availSize = workArea.height - themeNode.get_vertical_padding();
- let height = 0;
+ let size = 0;
for (let child of this.get_children()) {
let [childMinHeight, childNaturalHeight] = child.get_preferred_height(-1);
- let [childMinWidth, childNaturalWidth] = child.get_preferred_width(childNaturalHeight);
- height += childNaturalHeight * workArea.width / workArea.height;
+ let height = childNaturalHeight * workArea.width / workArea.height;
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ size += height * workArea.width / workArea.height;
+ } else {
+ size += height;
+ }
}
let workspaceManager = global.workspace_manager;
let spacing = this._itemSpacing * (workspaceManager.n_workspaces - 1);
- height += spacing;
- height = Math.min(height, availHeight);
+ size += spacing;
+ size = Math.min(size, availSize);
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ this._childWidth = (size - spacing) / workspaceManager.n_workspaces;
+ return themeNode.adjust_preferred_width(size, size);
+ } else {
+ this._childHeight = (size - spacing) / workspaceManager.n_workspaces;
+ return themeNode.adjust_preferred_height(size, size);
+ }
+ }
+
+ _getSizeForOppositeOrientation() {
+ let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
- this._childHeight = (height - spacing) / workspaceManager.n_workspaces;
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ this._childHeight = Math.round(this._childWidth * workArea.height / workArea.width);
+ return [this._childHeight, this._childHeight];
+ } else {
+ this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
+ return [this._childWidth, this._childWidth];
+ }
+ }
- return themeNode.adjust_preferred_height(height, height);
+ vfunc_get_preferred_height(forWidth) {
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ return this._getSizeForOppositeOrientation();
+ else
+ return this._getPreferredSizeForOrientation(forWidth);
}
vfunc_get_preferred_width(forHeight) {
- let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
- this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
-
- return [this._childWidth, this._childWidth];
+ if (this._orientation == Clutter.Orientation.HORIZONTAL)
+ return this._getPreferredSizeForOrientation(forHeight);
+ else
+ return this._getSizeForOppositeOrientation();
}
vfunc_allocate(box, flags) {
@@ -62,15 +96,23 @@ class WorkspaceSwitcherPopupList extends St.Widget {
let childBox = new Clutter.ActorBox();
+ let rtl = this.text_direction == Clutter.TextDirection.RTL;
+ let x = rtl ? box.x2 - this._childWidth : box.x1;
let y = box.y1;
- let prevChildBoxY2 = box.y1 - this._itemSpacing;
for (let child of this.get_children()) {
- childBox.x1 = box.x1;
- childBox.x2 = box.x1 + this._childWidth;
- childBox.y1 = prevChildBoxY2 + this._itemSpacing;
+ childBox.x1 = Math.round(x);
+ childBox.x2 = Math.round(x + this._childWidth);
+ childBox.y1 = Math.round(y);
childBox.y2 = Math.round(y + this._childHeight);
- y += this._childHeight + this._itemSpacing;
- prevChildBoxY2 = childBox.y2;
+
+ if (this._orientation == Clutter.Orientation.HORIZONTAL) {
+ if (rtl)
+ x -= this._childWidth + this._itemSpacing;
+ else
+ x += this._childWidth + this._itemSpacing;
+ } else {
+ y += this._childHeight + this._itemSpacing;
+ }
child.allocate(childBox, flags);
}
}
@@ -123,6 +165,10 @@ class WorkspaceSwitcherPopup extends St.Widget {
indicator = new St.Bin({ style_class: 'ws-switcher-active-up' });
else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.DOWN)
indicator = new St.Bin({ style_class: 'ws-switcher-active-down' });
+ else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.LEFT)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-left' });
+ else if(i == this._activeWorkspaceIndex && this._direction == Meta.MotionDirection.RIGHT)
+ indicator = new St.Bin({ style_class: 'ws-switcher-active-right' });
else
indicator = new St.Bin({ style_class: 'ws-switcher-box' });
--
2.21.0
From 813976ff69b15ab884d44f5f6a56ae66f407acfd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 4 Jun 2019 19:49:23 +0000
Subject: [PATCH 2/2] workspacesView: Support horizontal layout
Just as we did for the workspace switcher popup, support workspaces
being laid out in a single row in the window picker.
Note that this takes care of the various workspace switch actions in
the overview (scrolling, panning, touch(pad) gestures) as well as the
switch animation, but not of the overview's workspace switcher component.
There are currently no plans to support other layouts there, as the
component is inherently vertical (in fact, it was the whole reason for
switching the layout in the first place).
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/575
---
js/ui/workspacesView.js | 81 ++++++++++++++++++++++++++++++-----------
1 file changed, 60 insertions(+), 21 deletions(-)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index fe06d9dae..069937d5a 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -181,26 +181,32 @@ var WorkspacesView = class extends WorkspacesViewBase {
Tweener.removeTweens(workspace.actor);
- let y = (w - active) * this._fullGeometry.height;
+ let params = {};
+ if (workspaceManager.layout_rows == -1)
+ params.y = (w - active) * this._fullGeometry.height;
+ else if (this.actor.text_direction == Clutter.TextDirection.RTL)
+ params.x = (active - w) * this._fullGeometry.width;
+ else
+ params.x = (w - active) * this._fullGeometry.width;
if (showAnimation) {
- let params = { y: y,
- time: WORKSPACE_SWITCH_TIME,
- transition: 'easeOutQuad'
- };
+ let tweenParams = Object.assign(params, {
+ time: WORKSPACE_SWITCH_TIME,
+ transition: 'easeOutQuad'
+ });
// we have to call _updateVisibility() once before the
// animation and once afterwards - it does not really
// matter which tween we use, so we pick the first one ...
if (w == 0) {
this._updateVisibility();
- params.onComplete = () => {
+ tweenParams.onComplete = () => {
this._animating = false;
this._updateVisibility();
};
}
- Tweener.addTween(workspace.actor, params);
+ Tweener.addTween(workspace.actor, tweenParams);
} else {
- workspace.actor.set_position(0, y);
+ workspace.actor.set(params);
if (w == 0)
this._updateVisibility();
}
@@ -338,22 +344,39 @@ var WorkspacesView = class extends WorkspacesViewBase {
metaWorkspace.activate(global.get_current_time());
}
- let last = this._workspaces.length - 1;
- let firstWorkspaceY = this._workspaces[0].actor.y;
- let lastWorkspaceY = this._workspaces[last].actor.y;
- let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
-
if (adj.upper == 1)
return;
- let currentY = firstWorkspaceY;
- let newY = - adj.value / (adj.upper - 1) * workspacesHeight;
+ let last = this._workspaces.length - 1;
+
+ if (workspaceManager.layout_rows == -1) {
+ let firstWorkspaceY = this._workspaces[0].actor.y;
+ let lastWorkspaceY = this._workspaces[last].actor.y;
+ let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
+
+ let currentY = firstWorkspaceY;
+ let newY = -adj.value / (adj.upper - 1) * workspacesHeight;
- let dy = newY - currentY;
+ let dy = newY - currentY;
+
+ for (let i = 0; i < this._workspaces.length; i++) {
+ this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
+ this._workspaces[i].actor.y += dy;
+ }
+ } else {
+ let firstWorkspaceX = this._workspaces[0].actor.x;
+ let lastWorkspaceX = this._workspaces[last].actor.x;
+ let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
- for (let i = 0; i < this._workspaces.length; i++) {
- this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
- this._workspaces[i].actor.y += dy;
+ let currentX = firstWorkspaceX;
+ let newX = -adj.value / (adj.upper - 1) * workspacesWidth;
+
+ let dx = newX - currentX;
+
+ for (let i = 0; i < this._workspaces.length; i++) {
+ this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
+ this._workspaces[i].actor.x += dx;
+ }
}
}
};
@@ -504,7 +527,12 @@ var WorkspacesDisplay = class {
_onPan(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollAdjustment;
- adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
+ if (global.workspace_manager.layout_rows == -1)
+ adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
+ else if (this.actor.text_direction == Clutter.TextDirection.RTL)
+ adjustment.value += (dx / this.actor.width) * adjustment.page_size;
+ else
+ adjustment.value -= (dx / this.actor.width) * adjustment.page_size;
return false;
}
@@ -536,7 +564,12 @@ var WorkspacesDisplay = class {
let workspaceManager = global.workspace_manager;
let active = workspaceManager.get_active_workspace_index();
let adjustment = this._scrollAdjustment;
- adjustment.value = (active - yRel / this.actor.height) * adjustment.page_size;
+ if (workspaceManager.layout_rows == -1)
+ adjustment.value = (active - yRel / this.actor.height) * adjustment.page_size;
+ else if (this.actor.text_direction == Clutter.TextDirection.RTL)
+ adjustment.value = (active + xRel / this.actor.width) * adjustment.page_size;
+ else
+ adjustment.value = (active - xRel / this.actor.width) * adjustment.page_size;
}
_onSwitchWorkspaceActivated(action, direction) {
@@ -755,6 +788,12 @@ var WorkspacesDisplay = class {
case Clutter.ScrollDirection.DOWN:
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
break;
+ case Clutter.ScrollDirection.LEFT:
+ ws = activeWs.get_neighbor(Meta.MotionDirection.LEFT);
+ break;
+ case Clutter.ScrollDirection.RIGHT:
+ ws = activeWs.get_neighbor(Meta.MotionDirection.RIGHT);
+ break;
default:
return Clutter.EVENT_PROPAGATE;
}
--
2.21.0

@ -0,0 +1,643 @@
From 781dfcf6ce7168c6b116d58df5f1c67291a7b513 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 16 May 2019 00:57:27 +0200
Subject: [PATCH 01/11] introspect: Include `sandboxed-app-id` as well
App IDs in gnome-shell don't match AppStream, Flatpak or Snap IDs. For the
desktop portal, the latter two are more relevant, so include it in the
returned information.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/1289
---
js/misc/introspect.js | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index f7a7f2fe6..1e8300d0a 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -55,6 +55,11 @@ var IntrospectService = class {
return APP_WHITELIST.includes(sender);
}
+ _getSandboxedAppId(app) {
+ let ids = app.get_windows().map(w => w.get_sandboxed_app_id());
+ return ids.find(id => id != null);
+ }
+
_syncRunningApplications() {
let tracker = Shell.WindowTracker.get_default();
let apps = this._appSystem.get_running();
@@ -76,6 +81,10 @@ var IntrospectService = class {
newActiveApplication = app.get_id();
}
+ let sandboxedAppId = this._getSandboxedAppId(app);
+ if (sandboxedAppId)
+ appInfo['sandboxed-app-id'] = new GLib.Variant('s', sandboxedAppId);
+
newRunningApplications[app.get_id()] = appInfo;
}
@@ -137,6 +146,7 @@ var IntrospectService = class {
let frameRect = window.get_frame_rect();
let title = window.get_title();
let wmClass = window.get_wm_class();
+ let sandboxedAppId = window.get_sandboxed_app_id();
windowsList[windowId] = {
'app-id': GLib.Variant.new('s', app.get_id()),
@@ -153,6 +163,10 @@ var IntrospectService = class {
if (wmClass != null)
windowsList[windowId]['wm-class'] = GLib.Variant.new('s', wmClass);
+
+ if (sandboxedAppId != null)
+ windowsList[windowId]['sandboxed-app-id'] =
+ GLib.Variant.new('s', sandboxedAppId);
}
}
invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList]));
--
2.26.2
From b0b4fb82c058722e2171d24902ba3855ffe243f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 18 Sep 2019 14:57:48 +0200
Subject: [PATCH 02/11] introspect: Check whitelist also for
GetRunningWindows()
Otherwise the xdg-desktop-portal-gtk screen cast widget won't work.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/732
---
js/misc/introspect.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index 1e8300d0a..cee6409a8 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -128,7 +128,8 @@ var IntrospectService = class {
let apps = this._appSystem.get_running();
let windowsList = {};
- if (!this._isIntrospectEnabled()) {
+ if (!this._isIntrospectEnabled() &&
+ !this._isSenderWhitelisted(invocation.get_sender())) {
invocation.return_error_literal(Gio.DBusError,
Gio.DBusError.ACCESS_DENIED,
'App introspection not allowed');
--
2.26.2
From 23556e03db3743ddf478a3c1bbb64946c687afdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Mon, 25 Nov 2019 19:44:10 +0100
Subject: [PATCH 03/11] introspect: Fix whitelist check
The whitelist is a list of well-known D-Bus names, which we then search
for the unique name we get from the method invocation - unsuccesfully.
Fix this by watching the bus for any name in the whitelist in order
to maintain a map from wel-known to unique name that we can use for
matching.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/1916
---
js/misc/introspect.js | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index cee6409a8..f14eabfad 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -39,6 +39,15 @@ var IntrospectService = class {
});
this._syncRunningApplications();
+
+ this._whitelistMap = new Map();
+ APP_WHITELIST.forEach(appName => {
+ Gio.DBus.watch_name(Gio.BusType.SESSION,
+ appName,
+ Gio.BusNameWatcherFlags.NONE,
+ (conn, name, owner) => this._whitelistMap.set(name, owner),
+ (conn, name) => this._whitelistMap.delete(name));
+ });
}
_isStandaloneApp(app) {
@@ -52,7 +61,7 @@ var IntrospectService = class {
}
_isSenderWhitelisted(sender) {
- return APP_WHITELIST.includes(sender);
+ return [...this._whitelistMap.values()].includes(sender);
}
_getSandboxedAppId(app) {
--
2.26.2
From 1a6275add6d214df958ed8a06c097445bef021bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Wed, 25 Sep 2019 20:36:28 +0200
Subject: [PATCH 04/11] introspect: Add helper to check method call permission
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
js/misc/introspect.js | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index f14eabfad..6186754cd 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -120,9 +120,18 @@ var IntrospectService = class {
type == Meta.WindowType.UTILITY);
}
+ _isInvocationAllowed(invocation) {
+ if (this._isIntrospectEnabled())
+ return true;
+
+ if (this._isSenderWhitelisted(invocation.get_sender()))
+ return true;
+
+ return false;
+ }
+
GetRunningApplicationsAsync(params, invocation) {
- if (!this._isIntrospectEnabled() &&
- !this._isSenderWhitelisted(invocation.get_sender())) {
+ if (!this._isInvocationAllowed(invocation)) {
invocation.return_error_literal(Gio.DBusError,
Gio.DBusError.ACCESS_DENIED,
'App introspection not allowed');
@@ -137,8 +146,7 @@ var IntrospectService = class {
let apps = this._appSystem.get_running();
let windowsList = {};
- if (!this._isIntrospectEnabled() &&
- !this._isSenderWhitelisted(invocation.get_sender())) {
+ if (!this._isInvocationAllowed(invocation)) {
invocation.return_error_literal(Gio.DBusError,
Gio.DBusError.ACCESS_DENIED,
'App introspection not allowed');
--
2.26.2
From f578dc01cf774faa4504a4d258cc0e82060d988b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 11:55:33 +0200
Subject: [PATCH 05/11] shell-util: Add API to check for X11 extensions
Will be used to disable animations when running inside Xvnc. This was
done in gsd-xsettings before.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
src/shell-util.c | 26 ++++++++++++++++++++++++++
src/shell-util.h | 3 +++
2 files changed, 29 insertions(+)
diff --git a/src/shell-util.c b/src/shell-util.c
index 31bb18e70..fa3fc08c8 100644
--- a/src/shell-util.c
+++ b/src/shell-util.c
@@ -21,6 +21,8 @@
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <meta/meta-shaped-texture.h>
+#include <meta/display.h>
+#include <meta/meta-x11-display.h>
#include <locale.h>
#ifdef HAVE__NL_TIME_FIRST_WEEKDAY
@@ -613,3 +615,27 @@ shell_util_check_cloexec_fds (void)
fdwalk (check_cloexec, NULL);
g_info ("Open fd CLOEXEC check complete");
}
+
+/**
+ * shell_util_has_x11_display_extension:
+ * @display: A #MetaDisplay
+ * @extension: An X11 extension
+ *
+ * If the corresponding X11 display provides the passed extension, return %TRUE,
+ * otherwise %FALSE. If there is no X11 display, %FALSE is passed.
+ */
+gboolean
+shell_util_has_x11_display_extension (MetaDisplay *display,
+ const char *extension)
+{
+ MetaX11Display *x11_display;
+ Display *xdisplay;
+ int op, event, error;
+
+ x11_display = meta_display_get_x11_display (display);
+ if (!x11_display)
+ return FALSE;
+
+ xdisplay = meta_x11_display_get_xdisplay (x11_display);
+ return XQueryExtension (xdisplay, extension, &op, &event, &error);
+}
diff --git a/src/shell-util.h b/src/shell-util.h
index 6904f43bc..02b8404e9 100644
--- a/src/shell-util.h
+++ b/src/shell-util.h
@@ -59,6 +59,9 @@ cairo_surface_t * shell_util_composite_capture_images (ClutterCapture *captures
void shell_util_check_cloexec_fds (void);
+gboolean shell_util_has_x11_display_extension (MetaDisplay *display,
+ const char *extension);
+
G_END_DECLS
#endif /* __SHELL_UTIL_H__ */
--
2.26.2
From 48ee79bb7b48c7e93e77e35629f21bbdbabc253f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 11:56:34 +0200
Subject: [PATCH 06/11] st/settings: Add API to inhibit animations
There may be situations where we shouldn't enable animations. Make it
possible for the Shell to decide when there are such situations and in
when needed inhibit animations.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
src/st/st-settings.c | 38 +++++++++++++++++++++++++++++++++++++-
src/st/st-settings.h | 4 ++++
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/st/st-settings.c b/src/st/st-settings.c
index 17f2c466e..ebfd28480 100644
--- a/src/st/st-settings.c
+++ b/src/st/st-settings.c
@@ -54,6 +54,7 @@ struct _StSettings
gchar *gtk_theme;
gchar *gtk_icon_theme;
+ int inhibit_animations_count;
gboolean enable_animations;
gboolean primary_paste;
gboolean magnifier_active;
@@ -62,6 +63,41 @@ struct _StSettings
G_DEFINE_TYPE (StSettings, st_settings, G_TYPE_OBJECT)
+static gboolean
+get_enable_animations (StSettings *settings)
+{
+ if (settings->inhibit_animations_count > 0)
+ return FALSE;
+ else
+ return settings->enable_animations;
+}
+
+void
+st_settings_inhibit_animations (StSettings *settings)
+{
+ gboolean enable_animations;
+
+ enable_animations = get_enable_animations (settings);
+ settings->inhibit_animations_count++;
+
+ if (enable_animations != get_enable_animations (settings))
+ g_object_notify_by_pspec (G_OBJECT (settings),
+ props[PROP_ENABLE_ANIMATIONS]);
+}
+
+void
+st_settings_uninhibit_animations (StSettings *settings)
+{
+ gboolean enable_animations;
+
+ enable_animations = get_enable_animations (settings);
+ settings->inhibit_animations_count--;
+
+ if (enable_animations != get_enable_animations (settings))
+ g_object_notify_by_pspec (G_OBJECT (settings),
+ props[PROP_ENABLE_ANIMATIONS]);
+}
+
static void
st_settings_finalize (GObject *object)
{
@@ -95,7 +131,7 @@ st_settings_get_property (GObject *object,
switch (prop_id)
{
case PROP_ENABLE_ANIMATIONS:
- g_value_set_boolean (value, settings->enable_animations);
+ g_value_set_boolean (value, get_enable_animations (settings));
break;
case PROP_PRIMARY_PASTE:
g_value_set_boolean (value, settings->primary_paste);
diff --git a/src/st/st-settings.h b/src/st/st-settings.h
index c2c4fa23e..8b2549469 100644
--- a/src/st/st-settings.h
+++ b/src/st/st-settings.h
@@ -33,6 +33,10 @@ G_DECLARE_FINAL_TYPE (StSettings, st_settings, ST, SETTINGS, GObject)
StSettings * st_settings_get (void);
+void st_settings_inhibit_animations (StSettings *settings);
+
+void st_settings_uninhibit_animations (StSettings *settings);
+
G_END_DECLS
#endif /* __ST_SETTINGS_H__ */
--
2.26.2
From 80025388c44296b629c8f24ea673d77ffc4efc67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 12:02:31 +0200
Subject: [PATCH 07/11] main: Inhibit animations when software rendered
This was previously decided by gsd-xsettings.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
js/ui/main.js | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/js/ui/main.js b/js/ui/main.js
index 978f83c3f..c3230ff03 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -147,6 +147,8 @@ function _initializeUI() {
_loadOskLayouts();
_loadDefaultStylesheet();
+ new AnimationsSettings();
+
// Setup the stage hierarchy early
layoutManager = new Layout.LayoutManager();
@@ -723,3 +725,13 @@ function showRestartMessage(message) {
let restartMessage = new RestartMessage(message);
restartMessage.open();
}
+
+var AnimationsSettings = class {
+ constructor() {
+ let backend = Meta.get_backend();
+ if (!backend.is_rendering_hardware_accelerated()) {
+ St.Settings.get().inhibit_animations();
+ return;
+ }
+ }
+};
--
2.26.2
From 788ecb60e35d8a369f0747813f37e8b1ca27cb87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 12:03:52 +0200
Subject: [PATCH 08/11] main: Inhibit animations if X server advertises
VNC-EXTENSION
This was previously done by gsd-xsettings to disable animations when
running in Xvnc.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
js/ui/main.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/js/ui/main.js b/js/ui/main.js
index c3230ff03..ae7c3ffd0 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -733,5 +733,12 @@ var AnimationsSettings = class {
St.Settings.get().inhibit_animations();
return;
}
+
+ let isXvnc = Shell.util_has_x11_display_extension(
+ global.display, 'VNC-EXTENSION');
+ if (isXvnc) {
+ St.Settings.get().inhibit_animations();
+ return;
+ }
}
};
--
2.26.2
From 1da5a7ce4cf0b95b96dd50b62ac6c1380fd88cb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 12:04:52 +0200
Subject: [PATCH 09/11] main: Inhibit animations when there is a remote desktop
session
If a remote desktop session asks for animations to be disabled, inhibit
animations while the session is active.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
js/ui/main.js | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/js/ui/main.js b/js/ui/main.js
index ae7c3ffd0..1203b3c39 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -740,5 +740,31 @@ var AnimationsSettings = class {
St.Settings.get().inhibit_animations();
return;
}
+
+ let remoteAccessController = backend.get_remote_access_controller();
+ if (!remoteAccessController)
+ return;
+
+ this._handles = new Set();
+ remoteAccessController.connect('new-handle',
+ (_, handle) => this._onNewRemoteAccessHandle(handle));
+ }
+
+ _onRemoteAccessHandleStopped(handle) {
+ let settings = St.Settings.get();
+
+ settings.uninhibit_animations();
+ this._handles.delete(handle);
+ }
+
+ _onNewRemoteAccessHandle(handle) {
+ if (!handle.get_disable_animations())
+ return;
+
+ let settings = St.Settings.get();
+
+ settings.inhibit_animations();
+ this._handles.add(handle);
+ handle.connect('stopped', this._onRemoteAccessHandleStopped.bind(this));
}
};
--
2.26.2
From ebfd46341a2d7a6338386e4be4a2807a6bc6e63c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 12:06:13 +0200
Subject: [PATCH 10/11] introspect: Rename variable
It was too generic, and would conflict with a StSettings variable.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
js/misc/introspect.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index 6186754cd..8e68a7e4f 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -29,7 +29,9 @@ var IntrospectService = class {
this._syncRunningApplications();
});
- this._settings = new Gio.Settings({ schema_id: INTROSPECT_SCHEMA });
+ this._introspectSettings = new Gio.Settings({
+ schema_id: INTROSPECT_SCHEMA,
+ });
let tracker = Shell.WindowTracker.get_default();
tracker.connect('notify::focus-app',
@@ -57,7 +59,7 @@ var IntrospectService = class {
}
_isIntrospectEnabled() {
- return this._settings.get_boolean(INTROSPECT_KEY);
+ return this._introspectSettings.get_boolean(INTROSPECT_KEY);
}
_isSenderWhitelisted(sender) {
--
2.26.2
From 343e7792fc84c296b331c3fcb142ed79d2ce9bd5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
Date: Tue, 1 Oct 2019 12:07:03 +0200
Subject: [PATCH 11/11] introspect: Add AnimationsEnabled property
While the gsetting is available for all who needs it, the Shell might
override it given various hueristics. Expose the decision made by the
Shell via a new property.
Intended to be used by gsd-xsettings as well as xdg-desktop-portal-gtk.
This also add a version property to the API, so that semi external
services (xdg-desktop-portal-gtk) can detect what API is expected to be
present.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/757
---
.../org.gnome.Shell.Introspect.xml | 14 ++++++++++
js/misc/introspect.js | 27 ++++++++++++++++++-
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
index 9508681af..d71f2414b 100644
--- a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
+++ b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
@@ -57,5 +57,19 @@
<method name="GetWindows">
<arg name="windows" direction="out" type="a{ta{sv}}" />
</method>
+
+ <!--
+ AnimationsEnabled:
+ @short_description: Whether the shell animations are enabled
+
+ By default determined by the org.gnome.desktop.interface enable-animations
+ gsetting, but may be overridden, e.g. if there is an active screen cast or
+ remote desktop session that asked for animations to be disabled.
+
+ Since: 2
+ -->
+ <property name="AnimationsEnabled" type="b" access="read"/>
+
+ <property name="version" type="u" access="read"/>
</interface>
</node>
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
index 8e68a7e4f..7c62113e5 100644
--- a/js/misc/introspect.js
+++ b/js/misc/introspect.js
@@ -1,9 +1,11 @@
-const { Gio, GLib, Meta, Shell } = imports.gi;
+const { Gio, GLib, Meta, Shell, St } = imports.gi;
const INTROSPECT_SCHEMA = 'org.gnome.shell';
const INTROSPECT_KEY = 'introspect';
const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk'];
+const INTROSPECT_DBUS_API_VERSION = 2;
+
const { loadInterfaceXML } = imports.misc.fileUtils;
const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect');
@@ -21,6 +23,7 @@ var IntrospectService = class {
this._runningApplicationsDirty = true;
this._activeApplication = null;
this._activeApplicationDirty = true;
+ this._animationsEnabled = true;
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('app-state-changed',
@@ -50,6 +53,11 @@ var IntrospectService = class {
(conn, name, owner) => this._whitelistMap.set(name, owner),
(conn, name) => this._whitelistMap.delete(name));
});
+
+ this._settings = St.Settings.get();
+ this._settings.connect('notify::enable-animations',
+ this._syncAnimationsEnabled.bind(this));
+ this._syncAnimationsEnabled();
}
_isStandaloneApp(app) {
@@ -191,4 +199,21 @@ var IntrospectService = class {
}
invocation.return_value(new GLib.Variant('(a{ta{sv}})', [windowsList]));
}
+
+ _syncAnimationsEnabled() {
+ let wasAnimationsEnabled = this._animationsEnabled;
+ this._animationsEnabled = this._settings.enable_animations;
+ if (wasAnimationsEnabled !== this._animationsEnabled) {
+ let variant = new GLib.Variant('b', this._animationsEnabled);
+ this._dbusImpl.emit_property_changed('AnimationsEnabled', variant);
+ }
+ }
+
+ get AnimationsEnabled() {
+ return this._animationsEnabled;
+ }
+
+ get version() {
+ return INTROSPECT_DBUS_API_VERSION;
+ }
};
--
2.26.2

@ -0,0 +1,176 @@
From 4926a9b8f958617d67d603622b1382c17fe4037c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Wed, 20 May 2020 12:05:04 +0200
Subject: [PATCH 1/2] workspacesView: Avoid setting invalid geometries on views
The fullGeometry and the actualGeometry of the WorkspacesDisplay are set
from the allocation of the overviews ControlsManager and the
WorkspacesDisplay, that means they're only valid after those actors got
their allocations during Clutters allocation cycle.
Since WorkspacesDisplay._updateWorkspacesViews() is already called while
showing/mapping the WorkspacesDisplay, that allocation cycle didn't
happen yet and we end up either setting the geometries of the views to
null (in case of the fullGeometry) or to something wrong (a 0-sized
allocation in case of the actualGeometry).
So avoid setting invalid geometries on the views by initializing both
the fullGeometry and the actualGeometry to null, and then only updating
the geometries of the views after they're set to a correct value.
Note that this means we won't correctly animate the overview the first
time we open it since the animation depends on the geometries being set,
but is being started from show(), which means no allocations have
happened yet. In practice this introduces no regression though since
before this change we simply used incorrect geometries (see the 0-sized
allocation mentioned above) on the initial opening and the animation
didn't work either.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119
---
js/ui/workspacesView.js | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index e302296a6..02baddc6e 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -521,6 +521,7 @@ var WorkspacesDisplay = class {
this._scrollEventId = 0;
this._keyPressEventId = 0;
+ this._actualGeometry = null;
this._fullGeometry = null;
}
@@ -675,8 +676,10 @@ var WorkspacesDisplay = class {
this._workspacesViews.forEach(v => v.actor.show());
- this._updateWorkspacesFullGeometry();
- this._updateWorkspacesActualGeometry();
+ if (this._fullGeometry)
+ this._syncWorkspacesFullGeometry();
+ if (this._actualGeometry)
+ this._syncWorkspacesActualGeometry();
}
_scrollValueChanged() {
@@ -739,10 +742,10 @@ var WorkspacesDisplay = class {
// the sliding controls were never slid in at all.
setWorkspacesFullGeometry(geom) {
this._fullGeometry = geom;
- this._updateWorkspacesFullGeometry();
+ this._syncWorkspacesFullGeometry();
}
- _updateWorkspacesFullGeometry() {
+ _syncWorkspacesFullGeometry() {
if (!this._workspacesViews.length)
return;
@@ -754,18 +757,21 @@ var WorkspacesDisplay = class {
}
_updateWorkspacesActualGeometry() {
+ const [x, y] = this.actor.get_transformed_position();
+ const width = this.actor.allocation.get_width();
+ const height = this.actor.allocation.get_height();
+
+ this._actualGeometry = { x, y, width, height };
+ this._syncWorkspacesActualGeometry();
+ }
+
+ _syncWorkspacesActualGeometry() {
if (!this._workspacesViews.length)
return;
- let [x, y] = this.actor.get_transformed_position();
- let allocation = this.actor.allocation;
- let width = allocation.x2 - allocation.x1;
- let height = allocation.y2 - allocation.y1;
- let primaryGeometry = { x: x, y: y, width: width, height: height };
-
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
- let geometry = (i == this._primaryIndex) ? primaryGeometry : monitors[i];
+ let geometry = i === this._primaryIndex ? this._actualGeometry : monitors[i];
this._workspacesViews[i].setActualGeometry(geometry);
}
}
--
2.26.2
From 4671eebccf4e6afce8c0a869d63095b39aa7e163 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Wed, 20 May 2020 13:39:11 +0200
Subject: [PATCH 2/2] workspacesView: Only animate on show() when geometries
are already set
Animating the window clones of the overview requires the fullGeometry
and the actualGeometry to be set, which they won't be when showing the
overview for the first time. So don't even try to animate the window
clones in that case because the geometries will still be null and
accessing them in workspace.js will throw errors.
The workspace views will still get the correct layout as soon as the
allocations happen because syncing the geometries will trigger updating
the window positions. Since animations are disabled for position changes
when syncing the geometry though, we won't get an animation and the
clones will jump into place. That's not a regression though since before
this change we also didn't animate in that case because the geometries
used were simply wrong (the actualGeometry was 0-sized as explained in
the last commit).
If we wanted to fix the initial animation of the overview, we'd have to
always enable animations of the window clones when syncing geometries,
but that would break the animation of the workspace when hovering the
workspaceThumbnail slider, because right now those animations are "glued
together" using the actualGeometry, so they would get out of sync.
The reason there are no errors happening in workspace.js with the
existing code is that due to a bug in Clutter the fullGeometry of
WorkspacesDisplay gets set very early while mapping the WorkspacesViews
(because the overviews ControlsManager gets an allocation during the
resource scale calculation of a ClutterClone, see
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1181), so it
won't be set to null anymore when calling
WorkspacesView.animateToOverview().
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1119
---
js/ui/workspacesView.js | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 02baddc6e..3e9d77655 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -589,13 +589,16 @@ var WorkspacesDisplay = class {
show(fadeOnPrimary) {
this._updateWorkspacesViews();
- for (let i = 0; i < this._workspacesViews.length; i++) {
- let animationType;
- if (fadeOnPrimary && i == this._primaryIndex)
- animationType = AnimationType.FADE;
- else
- animationType = AnimationType.ZOOM;
- this._workspacesViews[i].animateToOverview(animationType);
+
+ if (this._actualGeometry && this._fullGeometry) {
+ for (let i = 0; i < this._workspacesViews.length; i++) {
+ let animationType;
+ if (fadeOnPrimary && i == this._primaryIndex)
+ animationType = AnimationType.FADE;
+ else
+ animationType = AnimationType.ZOOM;
+ this._workspacesViews[i].animateToOverview(animationType);
+ }
}
this._restackedNotifyId =
--
2.26.2

@ -0,0 +1,47 @@
diff --git a/js/portalHelper/main.js b/js/portalHelper/main.js
index e163d6574..c61f3b381 100644
--- a/js/portalHelper/main.js
+++ b/js/portalHelper/main.js
@@ -1,6 +1,13 @@
const Format = imports.format;
const Gettext = imports.gettext;
-const { Gio, GLib, GObject, Gtk, Pango, Soup, WebKit2: WebKit } = imports.gi;
+const { Gio, GLib, GObject, Gtk, Pango, Soup } = imports.gi;
+
+let WebKit;
+try {
+ WebKit = imports.gi.WebKit2;
+} catch {
+ WebKit = null;
+}
const _ = Gettext.gettext;
@@ -340,6 +346,11 @@ function initEnvironment() {
function main(argv) {
initEnvironment();
+ if (!WebKit) {
+ log('WebKit2 typelib is not installed, captive portal helper will be disabled');
+ return 1;
+ }
+
if (!WebKit.WebContext.new_ephemeral) {
log('WebKitGTK 2.16 is required for the portal-helper, see https://bugzilla.gnome.org/show_bug.cgi?id=780453');
return 1;
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index 421d2e7d2..13b6501e7 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -2010,7 +2010,9 @@ var NMApplet = class extends PanelMenu.SystemIndicator {
new PortalHelperProxy(Gio.DBus.session, 'org.gnome.Shell.PortalHelper',
'/org/gnome/Shell/PortalHelper', (proxy, error) => {
if (error) {
- log('Error launching the portal helper: ' + error);
+ // Timeout is expected if WebKit is unavailable
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.TIMED_OUT))
+ log('Error launching the portal helper: ' + error);
return;
}

@ -0,0 +1,117 @@
From 96ccb155bbe6ce570832a9f3d27a0a08698127ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Sat, 28 Mar 2020 14:15:09 +0100
Subject: [PATCH 1/3] keyboard: Don't include keyboard devices when updating
lastDevice
We're dealing with attached keyboards now using the touch_mode property
of ClutterSeat: If a device has a keyboard attached, the touch-mode is
FALSE and we won't automatically show the OSK on touches, also the
touch-mode gets set to FALSE when an external keyboard is being plugged
in, so that also hides the OSK automatically.
With that, we can now ignore keyboard devices when updating the last
used device and no longer have to special-case our own virtual devices.
Because there was no special-case for the virtual device we use on
Wayland now, this fixes a bug where the keyboard disappeared after
touching keys like Enter or Backspace.
Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2287
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1142
---
js/ui/keyboard.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js
index c4ac72d..94b5325 100644
--- a/js/ui/keyboard.js
+++ b/js/ui/keyboard.js
@@ -1075,6 +1075,9 @@ var Keyboard = class Keyboard {
let device = manager.get_device(deviceId);
if (device.get_device_name().indexOf('XTEST') < 0) {
+ if (device.device_type == Clutter.InputDeviceType.KEYBOARD_DEVICE)
+ return;
+
this._lastDeviceId = deviceId;
this._syncEnabled();
}
--
2.26.2
From 3106746ae424287d8644643a2ef46d565e4cd7ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= <verdre@v0yd.nl>
Date: Sat, 28 Mar 2020 14:34:24 +0100
Subject: [PATCH 2/3] layout: Use translation_y of 0 to hide keyboard
Since we show the keyboard using a translation_y of -keyboardHeight, the
keyboard will be moved down far enough to be out of sight by setting
translation_y to 0.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1142
---
js/ui/layout.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/js/ui/layout.js b/js/ui/layout.js
index beb4c0a..4382f6e 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -719,7 +719,7 @@ var LayoutManager = GObject.registerClass({
showKeyboard() {
this.keyboardBox.show();
Tweener.addTween(this.keyboardBox,
- { anchor_y: this.keyboardBox.height,
+ { translation_y: -this.keyboardBox.height,
opacity: 255,
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
@@ -735,7 +735,7 @@ var LayoutManager = GObject.registerClass({
this._updateRegions();
this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', () => {
- this.keyboardBox.anchor_y = this.keyboardBox.height;
+ this.keyboardBox.translation_y = -this.keyboardBox.height;
});
}
@@ -745,7 +745,7 @@ var LayoutManager = GObject.registerClass({
this._keyboardHeightNotifyId = 0;
}
Tweener.addTween(this.keyboardBox,
- { anchor_y: 0,
+ { translation_y: 0,
opacity: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeInQuad',
--
2.26.2
From 642822308a72be6a47f4eb285f32539499f0d3e4 Mon Sep 17 00:00:00 2001
From: rpm-build <rpm-build>
Date: Wed, 21 Oct 2020 20:29:34 +0200
Subject: [PATCH 3/3] layout: queue redraw after hiding keyboard
---
js/ui/layout.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 4382f6e..1824313 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -759,6 +759,7 @@ var LayoutManager = GObject.registerClass({
_hideKeyboardComplete() {
this.keyboardBox.hide();
this._updateRegions();
+ global.stage.queue_redraw();
}
// setDummyCursorGeometry:
--
2.26.2

@ -0,0 +1,107 @@
From 9a5bf8df37490de4472699c235236dc73a637ac7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Mon, 11 Sep 2023 19:20:14 +0200
Subject: [PATCH 1/2] status/network: Fix fallback SSID label
We currently only return the fallback label if the string returned
from the ssid was invalid or couldn't be transformed to UTF-8.
If the ssid parameter itself is empty, we throw an error.
Handle this case as well, as callers otherwise would need to duplicate
the existing error path themselves.
---
js/ui/status/network.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index 9d6a83b733..bd823384da 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -63,7 +63,9 @@ function signalToIcon(value) {
}
function ssidToLabel(ssid) {
- let label = NM.utils_ssid_to_utf8(ssid.get_data());
+ let label;
+ if (ssid)
+ label = NM.utils_ssid_to_utf8(ssid.get_data());
if (!label)
label = _("<unknown>");
return label;
--
2.41.0
From ffc6c744810a902a0aac2a562c9e410336dfdb57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 30 Aug 2023 01:47:00 +0200
Subject: [PATCH 2/2] status/network: Use connection name with hidden AP
When connected to an OWE transition network, NetworkManager
reports the connected API with a hidden SSID.
Handle this by using the active connection's name before
ultimately falling back to the device name.
https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6918
Part-of:
<https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2927>
---
js/ui/status/network.js | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index bd823384da..ef04f7f3dd 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -1346,26 +1346,36 @@ var NMDeviceWireless = class {
_getStatus() {
let ap = this._device.active_access_point;
- if (this._isHotSpotMaster())
+ if (this._isHotSpotMaster()) {
/* Translators: %s is a network identifier */
return _("%s Hotspot Active").format(this._description);
- else if (this._device.state >= NM.DeviceState.PREPARE &&
- this._device.state < NM.DeviceState.ACTIVATED)
+ } else if (this._device.state >= NM.DeviceState.PREPARE &&
+ this._device.state < NM.DeviceState.ACTIVATED) {
/* Translators: %s is a network identifier */
return _("%s Connecting").format(this._description);
- else if (ap)
- return ssidToLabel(ap.get_ssid());
- else if (!this._client.wireless_hardware_enabled)
+ } else if (ap) {
+ const ssid = ap.get_ssid();
+ if (ssid)
+ return ssidToLabel(ssid);
+
+ // Use connection name when connected to hidden AP
+ const activeConnection = this._device.get_active_connection();
+ if (activeConnection)
+ return activeConnection.connection.get_id();
+
+ return ssidToLabel(null);
+ } else if (!this._client.wireless_hardware_enabled) {
/* Translators: %s is a network identifier */
return _("%s Hardware Disabled").format(this._description);
- else if (!this._client.wireless_enabled)
+ } else if (!this._client.wireless_enabled) {
/* Translators: %s is a network identifier */
return _("%s Off").format(this._description);
- else if (this._device.state == NM.DeviceState.DISCONNECTED)
+ } else if (this._device.state == NM.DeviceState.DISCONNECTED) {
/* Translators: %s is a network identifier */
return _("%s Not Connected").format(this._description);
- else
+ } else {
return '';
+ }
}
_getMenuIcon() {
--
2.41.0

@ -0,0 +1,399 @@
From 119ec213b8f9a9e55ca340dbde10b0d19becab41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 5 Dec 2019 14:12:47 +0100
Subject: [PATCH 1/4] perf-helper: Add content for custom drawing
Drawing windows got a lot more involved with the advent of client-side
decorations. Instead of accounting for visible and invisible borders,
titlebar and shadows when necessary, just add an empty child for the
custom drawing.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887
---
src/shell-perf-helper.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/shell-perf-helper.c b/src/shell-perf-helper.c
index e5eab208b..55bdbef02 100644
--- a/src/shell-perf-helper.c
+++ b/src/shell-perf-helper.c
@@ -120,9 +120,9 @@ on_window_map_event (GtkWidget *window,
}
static gboolean
-on_window_draw (GtkWidget *window,
- cairo_t *cr,
- WindowInfo *info)
+on_child_draw (GtkWidget *window,
+ cairo_t *cr,
+ WindowInfo *info)
{
cairo_rectangle_int_t allocation;
double x_offset, y_offset;
@@ -204,6 +204,7 @@ create_window (int width,
gboolean redraws)
{
WindowInfo *info;
+ GtkWidget *child;
info = g_new0 (WindowInfo, 1);
info->width = width;
@@ -219,10 +220,13 @@ create_window (int width,
info->pending = TRUE;
info->start_time = -1;
+ child = g_object_new (GTK_TYPE_BOX, "visible", TRUE, "app-paintable", TRUE, NULL);
+ gtk_container_add (GTK_CONTAINER (info->window), child);
+
gtk_widget_set_size_request (info->window, width, height);
gtk_widget_set_app_paintable (info->window, TRUE);
g_signal_connect (info->window, "map-event", G_CALLBACK (on_window_map_event), info);
- g_signal_connect (info->window, "draw", G_CALLBACK (on_window_draw), info);
+ g_signal_connect (child, "draw", G_CALLBACK (on_child_draw), info);
gtk_widget_show (info->window);
if (info->redraws)
--
2.26.2
From bb4c2acaef4d8fdea50915030c221e1190f704a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Thu, 5 Dec 2019 13:29:38 +0100
Subject: [PATCH 2/4] perf-helper: Remove unused atoms
Those aren't used for anything, but make the helper dependent on X11.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/887
---
src/shell-perf-helper.c | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/src/shell-perf-helper.c b/src/shell-perf-helper.c
index 55bdbef02..d3280de96 100644
--- a/src/shell-perf-helper.c
+++ b/src/shell-perf-helper.c
@@ -12,7 +12,6 @@
#include <math.h>
#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
#define BUS_NAME "org.gnome.Shell.PerfHelper"
@@ -60,12 +59,6 @@ static GOptionEntry opt_entries[] =
{ NULL }
};
-static Display *xdisplay;
-static Window xroot;
-static Atom atom_wm_state;
-static Atom atom__net_wm_name;
-static Atom atom_utf8_string;
-
static guint timeout_id;
static GList *our_windows;
static GList *wait_windows_invocations;
@@ -351,8 +344,6 @@ on_name_lost (GDBusConnection *connection,
int
main (int argc, char **argv)
{
- GdkDisplay *display;
- GdkScreen *screen;
GOptionContext *context;
GError *error = NULL;
@@ -368,15 +359,6 @@ main (int argc, char **argv)
return 1;
}
- display = gdk_display_get_default ();
- screen = gdk_screen_get_default ();
-
- xdisplay = gdk_x11_display_get_xdisplay (display);
- xroot = gdk_x11_window_get_xid (gdk_screen_get_root_window (screen));
- atom_wm_state = gdk_x11_get_xatom_by_name_for_display (display, "WM_STATE");
- atom__net_wm_name = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME");
- atom_utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
-
g_bus_own_name (G_BUS_TYPE_SESSION,
BUS_NAME,
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
--
2.26.2
From d8b4d72b89340dab46bdcb92ee54bde18dbb9ba9 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Fri, 24 Jan 2020 10:59:31 +0100
Subject: [PATCH 3/4] perf-tool: Spawn perf-tool-helper from gnome-shell
On Wayland, the display server is the Wayland compositor, i.e.
`gnome-shell` itself.
As a result, we cannot spawn `gnome-shell-perf-helper` before
`gnome-shell` is started, as `gnome-shell-perf-helper` needs to connect
to the display server.
So, instead of spawning `gnome-shell-perf-helper` from the perf tool,
start it from `gnome-shell` itself.
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/941
---
js/ui/scripting.js | 51 ++++++++++++++++++++++------------
src/gnome-shell-perf-tool.in | 53 ------------------------------------
2 files changed, 34 insertions(+), 70 deletions(-)
diff --git a/js/ui/scripting.js b/js/ui/scripting.js
index d227b9ef4..eef8f3887 100644
--- a/js/ui/scripting.js
+++ b/js/ui/scripting.js
@@ -3,8 +3,10 @@
const { Gio, GLib, Meta, Shell } = imports.gi;
const Mainloop = imports.mainloop;
+const Config = imports.misc.config;
const Main = imports.ui.main;
const Params = imports.misc.params;
+const Util = imports.misc.util;
const { loadInterfaceXML } = imports.misc.fileUtils;
@@ -73,6 +75,12 @@ function _getPerfHelper() {
return _perfHelper;
}
+function _spawnPerfHelper() {
+ let path = Config.LIBEXECDIR;
+ let command = `${path}/gnome-shell-perf-helper`;
+ Util.trySpawnCommandLine(command);
+}
+
function _callRemote(obj, method, ...args) {
return new Promise((resolve, reject) => {
args.push((result, excp) => {
@@ -270,6 +278,25 @@ function _collect(scriptModule, outputFile) {
}
}
+async function _runPerfScript(scriptModule, outputFile) {
+ for (let step of scriptModule.run()) {
+ try {
+ await step; // eslint-disable-line no-await-in-loop
+ } catch (err) {
+ log(`Script failed: ${err}\n${err.stack}`);
+ Meta.exit(Meta.ExitCode.ERROR);
+ }
+ }
+
+ try {
+ _collect(scriptModule, outputFile);
+ } catch (err) {
+ log(`Script failed: ${err}\n${err.stack}`);
+ Meta.exit(Meta.ExitCode.ERROR);
+ }
+ Meta.exit(Meta.ExitCode.SUCCESS);
+}
+
/**
* runPerfScript
* @scriptModule: module object with run and finish functions
@@ -310,23 +337,13 @@ function _collect(scriptModule, outputFile) {
* After running the script and collecting statistics from the
* event log, GNOME Shell will exit.
**/
-async function runPerfScript(scriptModule, outputFile) {
+function runPerfScript(scriptModule, outputFile) {
Shell.PerfLog.get_default().set_enabled(true);
+ _spawnPerfHelper();
- for (let step of scriptModule.run()) {
- try {
- await step;
- } catch (err) {
- log(`Script failed: ${err}\n${err.stack}`);
- Meta.exit(Meta.ExitCode.ERROR);
- }
- }
-
- try {
- _collect(scriptModule, outputFile);
- } catch (err) {
- log(`Script failed: ${err}\n${err.stack}`);
- Meta.exit(Meta.ExitCode.ERROR);
- }
- Meta.exit(Meta.ExitCode.SUCCESS);
+ Gio.bus_watch_name(Gio.BusType.SESSION,
+ 'org.gnome.Shell.PerfHelper',
+ Gio.BusNameWatcherFlags.NONE,
+ () => _runPerfScript(scriptModule, outputFile),
+ null);
}
diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in
index f4b48f730..050c66b30 100755
--- a/src/gnome-shell-perf-tool.in
+++ b/src/gnome-shell-perf-tool.in
@@ -24,52 +24,6 @@ def show_version(option, opt_str, value, parser):
print("GNOME Shell Performance Test @VERSION@")
sys.exit()
-def wait_for_dbus_name(wait_name):
- loop = GLib.MainLoop()
-
- def on_name_appeared(connection, name, new_owner, *args):
- if not (name == wait_name and new_owner != ''):
- return
- loop.quit()
- return
-
- watch_id = Gio.bus_watch_name(Gio.BusType.SESSION,
- wait_name,
- Gio.BusNameWatcherFlags.NONE,
- on_name_appeared,
- None)
-
- def on_timeout():
- print("\nFailed to start %s: timed out" % (wait_name,))
- sys.exit(1)
- GLib.timeout_add_seconds(7, on_timeout)
-
- loop.run()
- Gio.bus_unwatch_name(watch_id)
-
-PERF_HELPER_NAME = "org.gnome.Shell.PerfHelper"
-PERF_HELPER_IFACE = "org.gnome.Shell.PerfHelper"
-PERF_HELPER_PATH = "/org/gnome/Shell/PerfHelper"
-
-def start_perf_helper():
- self_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
- perf_helper_path = "@libexecdir@/gnome-shell-perf-helper"
-
- subprocess.Popen([perf_helper_path])
- wait_for_dbus_name (PERF_HELPER_NAME)
-
-def stop_perf_helper():
- bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
-
- proxy = Gio.DBusProxy.new_sync(bus,
- Gio.DBusProxyFlags.NONE,
- None,
- PERF_HELPER_NAME,
- PERF_HELPER_PATH,
- PERF_HELPER_IFACE,
- None)
- proxy.Exit()
-
def start_shell(perf_output=None):
# Set up environment
env = dict(os.environ)
@@ -204,8 +158,6 @@ def run_performance_test():
logs = []
metric_summaries = {}
- start_perf_helper()
-
for i in range(0, iters):
# We create an empty temporary file that the shell will overwrite
# with the contents.
@@ -217,14 +169,12 @@ def run_performance_test():
try:
normal_exit = run_shell(perf_output=output_file)
except:
- stop_perf_helper()
raise
finally:
if not normal_exit:
os.remove(output_file)
if not normal_exit:
- stop_perf_helper()
return False
try:
@@ -232,7 +182,6 @@ def run_performance_test():
output = json.load(f)
f.close()
except:
- stop_perf_helper()
raise
finally:
os.remove(output_file)
@@ -260,8 +209,6 @@ def run_performance_test():
logs.append(output['log'])
- stop_perf_helper()
-
if options.perf_output or options.perf_upload:
# Write a complete report, formatted as JSON. The Javascript/C code that
# generates the individual reports we are summarizing here is very careful
--
2.26.2
From 8090db0f29dc72e602be341d43b3113373404b21 Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Tue, 21 Jan 2020 11:05:58 +0100
Subject: [PATCH 4/4] perf-tool: Allow to run as a Wayland compositor
`gnome-shell-perf-tool` is initially designed to run on X11, using the
`--replace` option which does not work when gnome-shell is a Wayland
compositor.
A solution would be to run `gnome-shell-perf-tool` in place of just
`gnome-shell` to run the entire perf session under Wayland, but the
script `gnome-shell-perf-tool` does not spawn `gnome-shell` as a Wayladn
compositor, so that fails as well.
Add a `--wayland` option to `gnome-shell-perf-tool` so that it can
optionally spawn gnome-shell as a Wayland compositor so the whole perf
tool can be starred from a console with:
```
$ dbus-run-session -- gnome-shell-perf-tool --wayland
```
Alternatively, for testing purposes, it can also be started nested with:
```
$ dbus-run-session -- gnome-shell-perf-tool --nested
```
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/2139
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/941
---
src/gnome-shell-perf-tool.in | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/src/gnome-shell-perf-tool.in b/src/gnome-shell-perf-tool.in
index 050c66b30..04072c4cd 100755
--- a/src/gnome-shell-perf-tool.in
+++ b/src/gnome-shell-perf-tool.in
@@ -45,6 +45,13 @@ def start_shell(perf_output=None):
if options.replace:
args.append('--replace')
+ if options.wayland or options.nested:
+ args.append('--wayland')
+ if options.nested:
+ args.append('--nested')
+ else:
+ args.append('--display-server')
+
return subprocess.Popen(args, env=env)
def run_shell(perf_output=None):
@@ -284,6 +291,10 @@ parser.add_option("", "--version", action="callback", callback=show_version,
parser.add_option("-r", "--replace", action="store_true",
help="Replace the running window manager")
+parser.add_option("-w", "--wayland", action="store_true",
+ help="Run as a Wayland compositor")
+parser.add_option("-n", "--nested", action="store_true",
+ help="Run as a Wayland nested compositor")
options, args = parser.parse_args()
--
2.26.2

@ -0,0 +1,71 @@
From 45ddeeaa317fb0ffd045600d9e4b95143c9ca8b8 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Sat, 8 Jun 2013 13:32:35 -0400
Subject: [PATCH 1/2] main: Show a warning when running as root
gnome-session used to show a dialog in this case, but a
notification is more natural nowadays. Doing it in gnome-shell
avoids complicated synchronization between gnome-session and
gnome-shell.
https://bugzilla.gnome.org/show_bug.cgi?id=701212
---
js/ui/main.js | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/js/ui/main.js b/js/ui/main.js
index 8d1755cf1..abf8a8765 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -237,6 +237,12 @@ function _initializeUI() {
['MESSAGE_ID=' + GNOMESHELL_STARTED_MESSAGE_ID]);
}
+ let credentials = new Gio.Credentials();
+ if (credentials.get_unix_user() === 0) {
+ notify(_('Logged in as a privileged user'),
+ _('Running a session as a privileged user should be avoided for security reasons. If possible, you should log in as a normal user.'));
+ }
+
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
if (perfModuleName) {
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
--
2.23.0
From 8e82907909b6a2e5af5da3f93b087df4b7eb48b5 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen@redhat.com>
Date: Sat, 8 Jun 2013 13:33:58 -0400
Subject: [PATCH 2/2] main: Show a warning when gdm is missing
If we are not running under gdm, some functionaliy (such as
the lock screen) does not work, and we should inform the
user about this.
https://bugzilla.gnome.org/show_bug.cgi?id=701212
---
js/ui/main.js | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/js/ui/main.js b/js/ui/main.js
index abf8a8765..be49c750e 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -243,6 +243,13 @@ function _initializeUI() {
_('Running a session as a privileged user should be avoided for security reasons. If possible, you should log in as a normal user.'));
}
+ if (sessionMode.currentMode !== 'gdm' &&
+ sessionMode.currentMode !== 'initial-setup' &&
+ screenShield === null) {
+ notify(_('Screen Lock disabled'),
+ _('Screen Locking requires the GNOME display manager.'));
+ }
+
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
if (perfModuleName) {
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
--
2.23.0

@ -0,0 +1,79 @@
From d6ead50fe230df58ddab822966d69760b00ec920 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 1 Apr 2020 14:48:10 +0200
Subject: [PATCH 1/2] screenShield: Switch lightboxes off before unlock
transition
There is no point in animating a transition with fullscreen black
rectangles stacked on top, so switch them off before rather than
after the transition.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1158
---
js/ui/screenShield.js | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index cd38f11fc8..282f29fa30 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -1221,6 +1221,9 @@ var ScreenShield = class {
this._isModal = false;
}
+ this._longLightbox.hide();
+ this._shortLightbox.hide();
+
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
@@ -1237,8 +1240,6 @@ var ScreenShield = class {
this._dialog = null;
}
- this._longLightbox.hide();
- this._shortLightbox.hide();
this.actor.hide();
if (this._becameActiveId != 0) {
--
2.28.0
From 39ac7cad68d8c00d98c900b35add637b01eddbbf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Tue, 31 Mar 2020 21:07:59 +0200
Subject: [PATCH 2/2] screenShield: Wake up on deactivate()
Usually the screen is woken up before the shield is deactivated, but
it is also possible to unlock the session programmatically via the
org.gnome.ScreenSaver D-Bus API.
The intention is very likely not to unlock a turned off screen in
that case. Nor does it seem like a good idea to change the lock
state without any indication.
Waking up the screen is more likely to meet expectations and is
more reasonable too, so do that.
https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1158
---
js/ui/screenShield.js | 2 ++
1 file changed, 2 insertions(+)
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index 282f29fa30..2d0a429bee 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -1200,6 +1200,8 @@ var ScreenShield = class {
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
+ this.emit('wake-up-screen');
+
if (this._isGreeter) {
// We don't want to "deactivate" any more than
// this. In particular, we don't want to drop
--
2.28.0

@ -0,0 +1,279 @@
From 37bbb9175bbd061d4ae14e86c35e4211602dbeaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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?= <fmuellner@gnome.org>
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: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1511>
---
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

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save