From 036254eb56ed1fcba05486d4c8974aa5e57e91fd Mon Sep 17 00:00:00 2001 From: Mark2Mac Date: Thu, 20 Aug 2026 11:58:45 +0200 Subject: [PATCH] fix(session): persist the idle inhibitor across shell restarts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idle inhibitor ("Keep Awake") lives only in memory: SessionService holds it as a plain `property bool idleInhibited: false` and nothing ever writes it out. Every restart of the shell silently clears it — the user's explicit choice is dropped with no message and no trace, and the pill just disappears from the bar. Restarting is not an edge case. `dms restart` is a first-class CLI command, and the unit shipped in this repo (assets/systemd/dms.service) sets `Restart=on-failure` with `TimeoutStopSec=10`, so a shell that is slow to stop is killed and brought back automatically. Each of those paths turns Keep Awake off without saying so. Every other Control Center toggle already survives a restart. Measured on upstream master, over the state read by Modules/ControlCenter/Components/ DragDropGrid.qml and its neighbours: doNotDisturb in SessionSpec: yes isLightMode in SessionSpec: yes nightModeEnabled in SessionSpec: yes wallpaperCyclingEnabled in SessionSpec: yes idleInhibited in SessionSpec: NO So this follows the doNotDisturb idiom exactly rather than inventing a mechanism: one spec entry, one property, one setter that saves. Restore happens through two entry points because the singletons are lazily created and their order is not fixed: `onLoaded` covers "service instantiated before the session file is read", `Component.onCompleted` covers the opposite. enableIdleInhibit() is idempotent, so whichever runs second is a no-op. The default stays false and SessionStore.toJson omits defaults, so an untouched session.json gains no new key. Related: #1483 asks for a configurable timer. The field added here is the same shape doNotDisturb uses with doNotDisturbUntil, so a later `idleInhibitedUntil` slots in beside it without another migration. --- quickshell/Common/SessionData.qml | 9 ++++++++ quickshell/Common/settings/SessionSpec.js | 1 + quickshell/Services/SessionService.qml | 25 +++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/quickshell/Common/SessionData.qml b/quickshell/Common/SessionData.qml index 856be9f94..8082a1415 100644 --- a/quickshell/Common/SessionData.qml +++ b/quickshell/Common/SessionData.qml @@ -34,6 +34,7 @@ Singleton { property bool isLightMode: false property bool doNotDisturb: false property real doNotDisturbUntil: 0 + property bool idleInhibited: false property string terminalOverride: "" property bool isSwitchingMode: false property bool suppressOSD: true @@ -626,6 +627,14 @@ Singleton { saveSettings(); } + function setIdleInhibited(enabled) { + const next = !!enabled; + if (idleInhibited === next) + return; + idleInhibited = next; + saveSettings(); + } + function setDoNotDisturbUntilTimestamp(timestampMs) { const target = Number(timestampMs) || 0; if (target <= Date.now()) { diff --git a/quickshell/Common/settings/SessionSpec.js b/quickshell/Common/settings/SessionSpec.js index 03136f134..fb3ddf8c3 100644 --- a/quickshell/Common/settings/SessionSpec.js +++ b/quickshell/Common/settings/SessionSpec.js @@ -4,6 +4,7 @@ var SPEC = { isLightMode: { def: false }, doNotDisturb: { def: false }, doNotDisturbUntil: { def: 0 }, + idleInhibited: { def: false }, terminalOverride: { def: "" }, wallpaperPath: { def: "" }, diff --git a/quickshell/Services/SessionService.qml b/quickshell/Services/SessionService.qml index 417b601c5..8b845875d 100644 --- a/quickshell/Services/SessionService.qml +++ b/quickshell/Services/SessionService.qml @@ -488,10 +488,34 @@ Singleton { // * Idle Inhibitor signal inhibitorChanged + // The inhibitor is a state the user turns on explicitly, so it is persisted in the session + // like the other Control Center toggles (doNotDisturb, nightModeEnabled, ...). Without this + // it lived only in memory and every shell restart silently cleared it. + // + // Both entry points are needed because the two singletons are lazily created and the order is + // not fixed: onLoaded covers "service first, session file read later", the restore below + // covers "session already loaded by the time the service is instantiated". enableIdleInhibit + // is idempotent, so whichever runs second does nothing. + function _restoreIdleInhibit() { + if (SessionData.idleInhibited && !idleInhibited) + enableIdleInhibit(); + } + + Connections { + target: SessionData + + function onLoaded() { + root._restoreIdleInhibit(); + } + } + + Component.onCompleted: _restoreIdleInhibit() + function enableIdleInhibit() { if (idleInhibited) return; idleInhibited = true; + SessionData.setIdleInhibited(true); inhibitorChanged(); } @@ -499,6 +523,7 @@ Singleton { if (!idleInhibited) return; idleInhibited = false; + SessionData.setIdleInhibited(false); inhibitorChanged(); }