auto-move-windows: Optionally restrict to startup

Resolves: https://redhat.atlassian.net/browse/RHEL-80282
This commit is contained in:
Florian Müllner 2026-06-03 14:54:16 +02:00
parent f804b081bc
commit ba5d99f3a9
No known key found for this signature in database
2 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,194 @@
From 9272f1ecf3750d1385ea627ebda41edfce3b37eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20M=C3=BCllner?= <fmuellner@gnome.org>
Date: Wed, 27 May 2026 19:35:04 +0200
Subject: [PATCH] auto-move-windows: Optionally restrict to startup
It is not uncommon to use a combination of autostart and auto-move-windows
to work around missing session saving/restore support. In that case, only
autostarted windows should be moved to their configured workspace, while
windows that are opened later should behave normally.
While we cannot detect that a window is autostarted, we can approximate
the behavior by only enabling moves for 20 seconds during startup.
In theory we can improve accuracy further by only moving windows without
a startup ID (as gnome-session uses a plain GDesktopAppLaunchContext),
but in practice its usefulness is limited given that the activation token
of wayland windows is applied too late and not exposed to introspection.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/work_items/322
---
extensions/auto-move-windows/extension.js | 54 +++++++++++++++++--
...l.extensions.auto-move-windows.gschema.xml | 4 ++
extensions/auto-move-windows/prefs.js | 43 ++++++++++++++-
3 files changed, 97 insertions(+), 4 deletions(-)
diff --git a/extensions/auto-move-windows/extension.js b/extensions/auto-move-windows/extension.js
index 34decce7..67381e48 100644
--- a/extensions/auto-move-windows/extension.js
+++ b/extensions/auto-move-windows/extension.js
@@ -4,11 +4,15 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
+import GLib from 'gi://GLib';
import Shell from 'gi://Shell';
import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+// time during which windows are moved on startup if startup-only is true
+const SESSION_AUTOSTART_TIMEOUT_MS = 20 * 1000;
+
class WindowMover {
constructor(settings) {
this._settings = settings;
@@ -19,16 +23,50 @@ class WindowMover {
this._appSystem.connectObject('installed-changed',
() => this._updateAppData(), this);
- this._settings.connectObject('changed',
+ this._settings.connectObject('changed::application-list',
this._updateAppConfigs.bind(this), this);
+ this._settings.connectObject('changed::startup-only',
+ () => this._updateStartupOnly());
+ this._updateStartupOnly();
+ }
+
+ _updateStartupOnly() {
+ const startupOnly = this._settings.get_boolean('startup-only');
+ if (this._startupOnly === startupOnly)
+ return;
+
+ this._startupOnly = startupOnly;
+
+ if (startupOnly) {
+ this._movesEnabled = Main.layoutManager._startingUp;
+
+ if (this._movesEnabled) {
+ this._startupTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ SESSION_AUTOSTART_TIMEOUT_MS,
+ () => {
+ this._movesEnabled = false;
+ this._updateAppConfigs();
+
+ delete this._startupTimeoutId;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ } else {
+ this._movesEnabled = true;
+ }
+
this._updateAppConfigs();
}
_updateAppConfigs() {
this._appConfigs.clear();
- this._settings.get_strv('application-list').forEach(v => {
- let [appId, num] = v.split(':');
+ const appList = this._movesEnabled
+ ? this._settings.get_strv('application-list')
+ : [];
+ appList.forEach(v => {
+ const [appId, num] = v.split(':');
this._appConfigs.set(appId, parseInt(num) - 1);
});
@@ -61,6 +99,10 @@ class WindowMover {
this._appConfigs.clear();
this._updateAppData();
+
+ if (this._startupTimeoutId)
+ GLib.source_remove(this._startupTimeoutId);
+ delete this._startupTimeoutId;
}
_moveWindow(window, workspaceNum) {
@@ -89,6 +131,12 @@ class WindowMover {
return !windows.includes(w) && w.get_compositor_private() !== null;
}));
+ // In startup-only mode, we only want to move auto-started apps;
+ // we can't filter for that, but at least we know that windows with
+ // a startup ID were launched by the user
+ if (this._startupOnly)
+ windows = windows.filter(w => w.get_startup_id() === null);
+
let workspaceNum = this._appConfigs.get(app.id);
windows.filter(w => !data.windows.includes(w)).forEach(window => {
this._moveWindow(window, workspaceNum);
diff --git a/extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml b/extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml
index 563f7ee6..a7d5ebb5 100644
--- a/extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml
+++ b/extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml
@@ -12,5 +12,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
<summary>Application and workspace list</summary>
<description>A list of strings, each containing an application id (desktop file name), followed by a colon and the workspace number</description>
</key>
+ <key name="startup-only" type="b">
+ <default>false</default>
+ <summary>Only move windows during startup</summary>
+ </key>
</schema>
</schemalist>
diff --git a/extensions/auto-move-windows/prefs.js b/extensions/auto-move-windows/prefs.js
index 98e424e0..4e1fa6c5 100644
--- a/extensions/auto-move-windows/prefs.js
+++ b/extensions/auto-move-windows/prefs.js
@@ -140,7 +140,48 @@ class RulesList extends GObject.Object {
}
}
-class AutoMoveSettingsWidget extends Adw.PreferencesGroup {
+class AutoMoveSettingsWidget extends Adw.PreferencesPage {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(settings) {
+ super();
+
+ this._settings = settings;
+
+ const rulesGroup = new WorkspaceRulesGroup(settings);
+ this.add(rulesGroup);
+
+ const optionsGroup = new OptionsGroup(settings);
+ this.add(optionsGroup);
+ }
+}
+
+class OptionsGroup extends Adw.PreferencesGroup {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(settings) {
+ super();
+
+ this._settings = settings;
+
+ const actionGroup = new Gio.SimpleActionGroup();
+ actionGroup.add_action(settings.create_action('startup-only'));
+ this.insert_action_group('options', actionGroup);
+
+ const row = new Adw.SwitchRow({
+ title: _('Startup Only'),
+ subtitle: _('Only apply rules while the session is starting'),
+ action_name: 'options.startup-only',
+ });
+ this.add(row);
+ }
+}
+
+class WorkspaceRulesGroup extends Adw.PreferencesGroup {
static {
GObject.registerClass(this);
--
2.54.0

View File

@ -40,6 +40,8 @@ Patch: heads-up-display-from-file.patch
Patch: scrollable-workspace-menu.patch
Patch: 0001-auto-move-windows-Optionally-restrict-to-startup.patch
%description
GNOME Shell Extensions is a collection of extensions providing additional and
optional functionality to GNOME Shell.