From 0d7b4187daadc6c0bd4d40d5517853ef1b002af7 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 6 Aug 2026 09:17:26 +0000 Subject: [PATCH] Fix login freeze: make the UPower proxy lookup async, not blocking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _connectPowerWatches() used Gio.DBusProxy.new_for_bus_sync(), which blocks gnome-shell's single main thread until the system bus replies. Since gnome-shell is the Wayland compositor, that stalls the entire screen for however long it takes — and this runs every time live wallpaper starts, including automatically at login whenever it was left enabled. Right after login, with upowerd/D-Bus still warming up alongside everything else, that block can run for many seconds up to the ~25s GDBus timeout, which matches reports of the whole system freezing right after the greeter/login. Switched to the async new_for_bus(), guarded by a generation counter so a stale in-flight lookup can't clobber state from a later _stop()/_connectPowerWatches(). --- lib/liveWallpaper.js | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/liveWallpaper.js b/lib/liveWallpaper.js index 342cf4d..36cf6e5 100644 --- a/lib/liveWallpaper.js +++ b/lib/liveWallpaper.js @@ -7,6 +7,8 @@ import * as Main from 'resource:///org/gnome/shell/ui/main.js'; import {SettingsKey} from './settingsKeys.js'; import {loadGstreamerModules, GSTREAMER_INSTALL_HINT} from './gstreamerAvailability.js'; +Gio._promisify(Gio.DBusProxy, 'new_for_bus', 'new_for_bus_finish'); + /** * Ways to get a decoded frame onto an actor, tried in order and "pinned" * once one works. `new Clutter.Image()` failed with "is not a constructor" @@ -79,6 +81,7 @@ export class LiveWallpaperManager { this._fullscreenChangedId = 0; this._upowerProxy = null; this._upowerSignalId = 0; + this._powerWatchGeneration = 0; this._frameCount = 0; this._pollCount = 0; @@ -428,19 +431,33 @@ export class LiveWallpaperManager { this._logger.debug(`Fullscreen tracking unavailable, pause-when-fullscreen disabled (${e.message ?? e})`); } - try { - this._upowerProxy = Gio.DBusProxy.new_for_bus_sync( - Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, null, - 'org.freedesktop.UPower', '/org/freedesktop/UPower', 'org.freedesktop.UPower', null); - this._upowerSignalId = this._upowerProxy.connect( - 'g-properties-changed', () => this._checkBattery()); - this._checkBattery(); - } catch (e) { - this._logger.debug(`UPower unavailable, pause-on-battery disabled (${e.message ?? e})`); - } + // Deliberately async: the *_sync() variant blocks gnome-shell's + // single main thread (it *is* the compositor) until the system bus + // replies, which can take many seconds — or the full ~25s GDBus + // timeout — if upowerd is still starting up or the bus is busy, + // exactly the conditions right after login. That froze the entire + // screen (and could make the session look hung/crashed) since this + // runs every time live wallpaper starts, including automatically + // at login whenever it was left enabled. + const watchGeneration = ++this._powerWatchGeneration; + Gio.DBusProxy.new_for_bus( + Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, null, + 'org.freedesktop.UPower', '/org/freedesktop/UPower', 'org.freedesktop.UPower', null) + .then(proxy => { + if (watchGeneration !== this._powerWatchGeneration) + return; // Superseded by a _stop()/_connectPowerWatches() while this was in flight. + this._upowerProxy = proxy; + this._upowerSignalId = proxy.connect('g-properties-changed', () => this._checkBattery()); + this._checkBattery(); + }) + .catch(e => { + this._logger.debug(`UPower unavailable, pause-on-battery disabled (${e.message ?? e})`); + }); } _disconnectPowerWatches() { + this._powerWatchGeneration++; + if (this._fullscreenChangedId) { global.display.disconnect(this._fullscreenChangedId); this._fullscreenChangedId = 0; -- 2.54.0