Merge pull request #12538 from paulogeyer/fix/10860-idle-timeout-zero
Treat idle timeout 0 as disabled, not immediate
This commit is contained in:
3 files changed
+97
-14
No files matched your search
@@ -4,6 +4,26 @@ function secondsFromConfig(value, fallback) {
|
|||||||
return Math.floor(n)
|
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) {
|
function eventParts(event, count) {
|
||||||
try {
|
try {
|
||||||
if (event && event.parse) return event.parse(count)
|
if (event && event.parse) return event.parse(count)
|
||||||
@@ -46,6 +66,8 @@ function screensaverWindowsAfter(windows, address, visible) {
|
|||||||
if (typeof module !== "undefined") {
|
if (typeof module !== "undefined") {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
secondsFromConfig: secondsFromConfig,
|
secondsFromConfig: secondsFromConfig,
|
||||||
|
firstIdleTimeout: firstIdleTimeout,
|
||||||
|
delayAfterFirstIdle: delayAfterFirstIdle,
|
||||||
eventParts: eventParts,
|
eventParts: eventParts,
|
||||||
screensaverWindowsAfter: screensaverWindowsAfter
|
screensaverWindowsAfter: screensaverWindowsAfter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,12 @@ Item {
|
|||||||
? shell.shellConfig.idle : (shell && shell.idleConfig ? shell.idleConfig : ({}))
|
? shell.shellConfig.idle : (shell && shell.idleConfig ? shell.idleConfig : ({}))
|
||||||
readonly property int screensaverTimeoutSeconds: secondsFromConfig(idleConfig.screensaver, defaultScreensaverSeconds)
|
readonly property int screensaverTimeoutSeconds: secondsFromConfig(idleConfig.screensaver, defaultScreensaverSeconds)
|
||||||
readonly property int lockTimeoutSeconds: secondsFromConfig(idleConfig.lock, defaultLockSeconds)
|
readonly property int lockTimeoutSeconds: secondsFromConfig(idleConfig.lock, defaultLockSeconds)
|
||||||
readonly property int firstIdleTimeoutSeconds: Math.min(screensaverTimeoutSeconds, lockTimeoutSeconds)
|
readonly property bool screensaverEnabled: screensaverTimeoutSeconds > 0
|
||||||
readonly property int screensaverDelaySeconds: Math.max(0, screensaverTimeoutSeconds - firstIdleTimeoutSeconds)
|
readonly property bool lockEnabled: lockTimeoutSeconds > 0
|
||||||
readonly property int lockDelaySeconds: Math.max(0, lockTimeoutSeconds - firstIdleTimeoutSeconds)
|
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 bool idleEnabled: stayAwakeStateLoaded && !stayAwake
|
||||||
readonly property string screensaverClass: "org.omarchy.screensaver"
|
readonly property string screensaverClass: "org.omarchy.screensaver"
|
||||||
|
|
||||||
@@ -92,11 +95,23 @@ Item {
|
|||||||
root.screensaverStartedThisCycle = false
|
root.screensaverStartedThisCycle = false
|
||||||
resetScreensaverWindows()
|
resetScreensaverWindows()
|
||||||
|
|
||||||
if (root.screensaverDelaySeconds === 0) launchScreensaver()
|
// Set this cycle's deadlines once: a bound interval would restart a pending
|
||||||
else screensaverTimer.restart()
|
// 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")
|
if (root.lockEnabled) {
|
||||||
else lockTimer.restart()
|
if (root.lockDelaySeconds === 0) lockSystem("lock-timeout-immediate")
|
||||||
|
else {
|
||||||
|
lockTimer.interval = root.lockDelaySeconds * 1000
|
||||||
|
lockTimer.restart()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelIdleCycle(reason) {
|
function cancelIdleCycle(reason) {
|
||||||
@@ -173,7 +188,7 @@ Item {
|
|||||||
|
|
||||||
function handleIdleChanged() {
|
function handleIdleChanged() {
|
||||||
logEvent("idle-monitor", idleMonitor.isIdle ? "idle" : "active")
|
logEvent("idle-monitor", idleMonitor.isIdle ? "idle" : "active")
|
||||||
if (!root.idleEnabled) return
|
if (!root.idleEnabled || !root.idleTimersEnabled) return
|
||||||
|
|
||||||
if (idleMonitor.isIdle) startIdleCycle()
|
if (idleMonitor.isIdle) startIdleCycle()
|
||||||
else handleActiveSignal()
|
else handleActiveSignal()
|
||||||
@@ -249,26 +264,27 @@ Item {
|
|||||||
return applyStayAwake(!value, true, "ipc")
|
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 {
|
IdleMonitor {
|
||||||
id: idleMonitor
|
id: idleMonitor
|
||||||
enabled: root.idleEnabled
|
enabled: root.idleEnabled && root.idleTimersEnabled
|
||||||
timeout: root.firstIdleTimeoutSeconds
|
timeout: Math.max(1, root.firstIdleTimeoutSeconds)
|
||||||
respectInhibitors: true
|
respectInhibitors: true
|
||||||
onIsIdleChanged: root.handleIdleChanged()
|
onIsIdleChanged: root.handleIdleChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: screensaverTimer
|
id: screensaverTimer
|
||||||
interval: root.screensaverDelaySeconds * 1000
|
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: root.launchScreensaver()
|
onTriggered: if (root.screensaverEnabled) root.launchScreensaver()
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: lockTimer
|
id: lockTimer
|
||||||
interval: root.lockDelaySeconds * 1000
|
|
||||||
repeat: false
|
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 {
|
Timer {
|
||||||
|
|||||||
@@ -5,11 +5,56 @@ set -euo pipefail
|
|||||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
run_node_test <<'JS'
|
run_node_test <<'JS'
|
||||||
|
const fs = require('fs')
|
||||||
const idle = requireFromRoot('shell/plugins/services/idle/IdleModel.js')
|
const idle = requireFromRoot('shell/plugins/services/idle/IdleModel.js')
|
||||||
|
const serviceSource = fs.readFileSync(root + '/shell/plugins/services/idle/Service.qml', 'utf8')
|
||||||
|
|
||||||
assertEqual(idle.secondsFromConfig('42.9', 10), 42, 'idle floors configured seconds')
|
assertEqual(idle.secondsFromConfig('42.9', 10), 42, 'idle floors configured seconds')
|
||||||
assertEqual(idle.secondsFromConfig('-1', 10), 10, 'idle rejects negative seconds')
|
assertEqual(idle.secondsFromConfig('-1', 10), 10, 'idle rejects negative seconds')
|
||||||
assertEqual(idle.secondsFromConfig('nope', 10), 10, 'idle rejects invalid seconds')
|
assertEqual(idle.secondsFromConfig('nope', 10), 10, 'idle rejects invalid seconds')
|
||||||
|
assertEqual(idle.secondsFromConfig(0, 300), 0, 'idle keeps an explicit zero timeout')
|
||||||
|
assertEqual(idle.firstIdleTimeout(150, 300), 150, 'idle uses the sooner of screensaver and lock')
|
||||||
|
assertEqual(idle.firstIdleTimeout(0, 300), 300, 'idle ignores a disabled screensaver when computing first idle')
|
||||||
|
assertEqual(idle.firstIdleTimeout(150, 0), 150, 'idle ignores a disabled lock when computing first idle')
|
||||||
|
assertEqual(idle.firstIdleTimeout(0, 0), 0, 'idle has no first-idle timeout when both actions are disabled')
|
||||||
|
assertEqual(idle.delayAfterFirstIdle(300, 150), 150, 'idle delays lock until its own timeout')
|
||||||
|
assertEqual(idle.delayAfterFirstIdle(150, 150), 0, 'idle fires an action immediately when it is the first timeout')
|
||||||
|
assertEqual(idle.delayAfterFirstIdle(0, 150), 0, 'idle does not schedule a disabled action')
|
||||||
|
|
||||||
|
assert(
|
||||||
|
serviceSource.includes('IdleModel.firstIdleTimeout(screensaverTimeoutSeconds, lockTimeoutSeconds)'),
|
||||||
|
'idle ignores a zero timeout when computing the first idle deadline'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/enabled: root\.idleEnabled && root\.idleTimersEnabled/.test(serviceSource),
|
||||||
|
'idle monitor is off when both screensaver and lock are disabled'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/if \(root\.lockEnabled\) \{\s*\n\s*if \(root\.lockDelaySeconds === 0\) lockSystem/.test(serviceSource),
|
||||||
|
'idle does not lock immediately when lock timeout is disabled'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/id: screensaverTimer[\s\S]*?onTriggered: if \(root\.screensaverEnabled\) root\.launchScreensaver\(\)/.test(serviceSource),
|
||||||
|
'idle does not launch a pending screensaver once its timeout is set to 0'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/id: lockTimer[\s\S]*?onTriggered: if \([^)]*root\.lockEnabled\) root\.lockSystem\("lock-timeout"\)/.test(serviceSource),
|
||||||
|
'idle does not fire a pending lock once its timeout is set to 0'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/lockTimer\.interval = root\.lockDelaySeconds \* 1000\s*\n\s*lockTimer\.restart\(\)/.test(serviceSource) &&
|
||||||
|
!/interval: root\.lockDelaySeconds/.test(serviceSource),
|
||||||
|
'idle keeps a pending lock on its deadline when shell.json changes mid-cycle'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/screensaverTimer\.interval = root\.screensaverDelaySeconds \* 1000\s*\n\s*screensaverTimer\.restart\(\)/.test(serviceSource) &&
|
||||||
|
!/interval: root\.screensaverDelaySeconds/.test(serviceSource),
|
||||||
|
'idle keeps a pending screensaver on its deadline when shell.json changes mid-cycle'
|
||||||
|
)
|
||||||
|
assert(
|
||||||
|
/onIdleTimersEnabledChanged: if \(!idleTimersEnabled\) cancelIdleCycle\(/.test(serviceSource),
|
||||||
|
'idle ends a running cycle when both timeouts are set to 0'
|
||||||
|
)
|
||||||
|
|
||||||
assertDeepEqual(idle.eventParts({ data: 'a,b,c' }, 2), ['a', 'b', 'c'], 'idle parses raw event data')
|
assertDeepEqual(idle.eventParts({ data: 'a,b,c' }, 2), ['a', 'b', 'c'], 'idle parses raw event data')
|
||||||
assertDeepEqual(
|
assertDeepEqual(
|
||||||
|
|||||||
Reference in new issue
Block a user