Merge pull request #12538 from paulogeyer/fix/10860-idle-timeout-zero

Treat idle timeout 0 as disabled, not immediate
This commit is contained in:
David Heinemeier Hansson authored and GitHub committed 2026-10-04 07:44:44 -04:00
commit 7901d7d0b6
3 files changed
+97 -14

No files matched your search

+22
View File
@@ -4,6 +4,26 @@ function secondsFromConfig(value, fallback) {
return Math.floor(n)
}
// 0 means the action is disabled, not "fire immediately". min(0, 300) would
// otherwise make IdleMonitor report idle as soon as an inhibitor is released.
function firstIdleTimeout(screensaverSeconds, lockSeconds) {
var times = []
if (screensaverSeconds > 0) times.push(screensaverSeconds)
if (lockSeconds > 0) times.push(lockSeconds)
if (times.length === 0) return 0
var min = times[0]
for (var i = 1; i < times.length; i++) {
if (times[i] < min) min = times[i]
}
return min
}
function delayAfterFirstIdle(timeoutSeconds, firstIdleSeconds) {
if (!(timeoutSeconds > 0)) return 0
var delay = timeoutSeconds - firstIdleSeconds
return delay > 0 ? delay : 0
}
function eventParts(event, count) {
try {
if (event && event.parse) return event.parse(count)
@@ -46,6 +66,8 @@ function screensaverWindowsAfter(windows, address, visible) {
if (typeof module !== "undefined") {
module.exports = {
secondsFromConfig: secondsFromConfig,
firstIdleTimeout: firstIdleTimeout,
delayAfterFirstIdle: delayAfterFirstIdle,
eventParts: eventParts,
screensaverWindowsAfter: screensaverWindowsAfter
}
+30 -14
View File
@@ -21,9 +21,12 @@ Item {
? shell.shellConfig.idle : (shell && shell.idleConfig ? shell.idleConfig : ({}))
readonly property int screensaverTimeoutSeconds: secondsFromConfig(idleConfig.screensaver, defaultScreensaverSeconds)
readonly property int lockTimeoutSeconds: secondsFromConfig(idleConfig.lock, defaultLockSeconds)
readonly property int firstIdleTimeoutSeconds: Math.min(screensaverTimeoutSeconds, lockTimeoutSeconds)
readonly property int screensaverDelaySeconds: Math.max(0, screensaverTimeoutSeconds - firstIdleTimeoutSeconds)
readonly property int lockDelaySeconds: Math.max(0, lockTimeoutSeconds - firstIdleTimeoutSeconds)
readonly property bool screensaverEnabled: screensaverTimeoutSeconds > 0
readonly property bool lockEnabled: lockTimeoutSeconds > 0
readonly property bool idleTimersEnabled: screensaverEnabled || lockEnabled
readonly property int firstIdleTimeoutSeconds: IdleModel.firstIdleTimeout(screensaverTimeoutSeconds, lockTimeoutSeconds)
readonly property int screensaverDelaySeconds: IdleModel.delayAfterFirstIdle(screensaverTimeoutSeconds, firstIdleTimeoutSeconds)
readonly property int lockDelaySeconds: IdleModel.delayAfterFirstIdle(lockTimeoutSeconds, firstIdleTimeoutSeconds)
readonly property bool idleEnabled: stayAwakeStateLoaded && !stayAwake
readonly property string screensaverClass: "org.omarchy.screensaver"
@@ -92,11 +95,23 @@ Item {
root.screensaverStartedThisCycle = false
resetScreensaverWindows()
if (root.screensaverDelaySeconds === 0) launchScreensaver()
else screensaverTimer.restart()
// Set this cycle's deadlines once: a bound interval would restart a pending
// timer from the moment shell.json changes, locking early or late.
if (root.screensaverEnabled) {
if (root.screensaverDelaySeconds === 0) launchScreensaver()
else {
screensaverTimer.interval = root.screensaverDelaySeconds * 1000
screensaverTimer.restart()
}
}
if (root.lockDelaySeconds === 0) lockSystem("lock-timeout-immediate")
else lockTimer.restart()
if (root.lockEnabled) {
if (root.lockDelaySeconds === 0) lockSystem("lock-timeout-immediate")
else {
lockTimer.interval = root.lockDelaySeconds * 1000
lockTimer.restart()
}
}
}
function cancelIdleCycle(reason) {
@@ -173,7 +188,7 @@ Item {
function handleIdleChanged() {
logEvent("idle-monitor", idleMonitor.isIdle ? "idle" : "active")
if (!root.idleEnabled) return
if (!root.idleEnabled || !root.idleTimersEnabled) return
if (idleMonitor.isIdle) startIdleCycle()
else handleActiveSignal()
@@ -249,26 +264,27 @@ Item {
return applyStayAwake(!value, true, "ipc")
}
// With both timeouts at 0 the monitor stops reporting, so nothing else would end a running cycle.
onIdleTimersEnabledChanged: if (!idleTimersEnabled) cancelIdleCycle("idle-timers-disabled")
IdleMonitor {
id: idleMonitor
enabled: root.idleEnabled
timeout: root.firstIdleTimeoutSeconds
enabled: root.idleEnabled && root.idleTimersEnabled
timeout: Math.max(1, root.firstIdleTimeoutSeconds)
respectInhibitors: true
onIsIdleChanged: root.handleIdleChanged()
}
Timer {
id: screensaverTimer
interval: root.screensaverDelaySeconds * 1000
repeat: false
onTriggered: root.launchScreensaver()
onTriggered: if (root.screensaverEnabled) root.launchScreensaver()
}
Timer {
id: lockTimer
interval: root.lockDelaySeconds * 1000
repeat: false
onTriggered: if (root.idleEnabled && root.idledThisCycle) root.lockSystem("lock-timeout")
onTriggered: if (root.idleEnabled && root.idledThisCycle && root.lockEnabled) root.lockSystem("lock-timeout")
}
Timer {