Persist StayAwake indicator state

This commit is contained in:
David Heinemeier Hansson
2026-05-23 15:15:55 +02:00
parent 548882527f
commit b92a44d572
3 changed files with 151 additions and 19 deletions
+67 -2
View File
@@ -1,6 +1,71 @@
#!/bin/bash
# omarchy:summary=Toggle idle behavior so the system either idles normally or stays awake
# omarchy:group=toggle
# omarchy:args=[toggle|stay-awake|allow-idle|status]
# omarchy:examples=omarchy toggle idle | omarchy toggle idle stay-awake | omarchy-toggle-idle --status
omarchy-shell idle toggle
omarchy-shell -q Indicators refresh
STATE_DIR="$HOME/.local/state/omarchy/indicators"
STATE_FILE="$STATE_DIR/stay-awake"
stay_awake_enabled() {
[[ -f $STATE_FILE ]]
}
print_status() {
if stay_awake_enabled; then
printf '{"enabled":true,"class":"enabled","tooltip":"Allow Idle Lock & Screensaver"}\n'
else
printf '{"enabled":false,"class":"disabled","tooltip":"Stay Awake"}\n'
fi
}
print_idle_state() {
if stay_awake_enabled; then
printf 'disabled\n'
else
printf 'enabled\n'
fi
}
apply_state() {
case "$1" in
stay-awake)
mkdir -p "$STATE_DIR"
touch "$STATE_FILE"
omarchy-shell -q idle disable
;;
allow-idle)
rm -f "$STATE_FILE"
omarchy-shell -q idle enable
;;
esac
omarchy-shell -q omarchy.indicators refresh
}
case "${1:-toggle}" in
toggle)
if stay_awake_enabled; then
apply_state allow-idle
else
apply_state stay-awake
fi
print_idle_state
;;
stay-awake|awake|on)
apply_state stay-awake
print_idle_state
;;
allow-idle|idle|off)
apply_state allow-idle
print_idle_state
;;
status|--status)
print_status
;;
*)
echo "Usage: omarchy-toggle-idle [toggle|stay-awake|allow-idle|status]" >&2
exit 1
;;
esac
+5 -2
View File
@@ -29,14 +29,17 @@ BarIndicator {
Process {
id: statusProc
command: ["bash", "-lc", "omarchy-shell idle status 2>/dev/null"]
command: ["omarchy-toggle-idle", "--status"]
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: {
var data = root.extractData(text)
root.active = data && data.enabled === false
root.active = data && data.enabled === true
}
}
onExited: function(exitCode) {
if (exitCode !== 0) root.active = false
}
}
Timer {
+79 -15
View File
@@ -10,6 +10,9 @@ Item {
// Injected by omarchy-shell (the first-party service loader).
property var shell: null
readonly property string home: Quickshell.env("HOME")
readonly property string stayAwakeStateDir: home + "/.local/state/omarchy/indicators"
readonly property string stayAwakeStatePath: stayAwakeStateDir + "/stay-awake"
readonly property int defaultScreensaverSeconds: 150
readonly property int defaultLockSeconds: 300
readonly property var idleConfig: shell && shell.shellConfig && shell.shellConfig.idle ? shell.shellConfig.idle : ({})
@@ -18,9 +21,13 @@ Item {
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 idleEnabled: persisted.idleEnabled
readonly property bool idleEnabled: stayAwakeStateLoaded && !stayAwake
readonly property string screensaverClass: "org.omarchy.screensaver"
property bool stayAwake: false
property bool stayAwakeStateLoaded: false
property bool hasPendingStayAwakePersist: false
property bool pendingStayAwakePersist: false
property bool idledThisCycle: false
property bool screensaverStartedThisCycle: false
property string lastEvent: "starting"
@@ -28,12 +35,6 @@ Item {
property var screensaverWindows: ({})
property int screensaverWindowCount: 0
PersistentProperties {
id: persisted
reloadableId: "omarchy-idle"
property bool idleEnabled: true
}
function secondsFromConfig(value, fallback) {
var n = Number(value)
if (!isFinite(n) || n < 0) return fallback
@@ -199,6 +200,9 @@ Item {
function statusJson() {
return JSON.stringify({
enabled: root.idleEnabled,
stayAwake: root.stayAwake,
stayAwakeStateLoaded: root.stayAwakeStateLoaded,
stayAwakeStatePath: root.stayAwakeStatePath,
idle: idleMonitor.isIdle,
inIdleCycle: root.idledThisCycle,
screensaverStarted: root.screensaverStartedThisCycle,
@@ -222,16 +226,45 @@ Item {
})
}
function setIdleEnabled(value) {
var enabled = !!value
if (persisted.idleEnabled === enabled) return enabled ? "enabled" : "disabled"
function persistStayAwake(value) {
var command = value
? "mkdir -p \"$HOME/.local/state/omarchy/indicators\" && touch \"$HOME/.local/state/omarchy/indicators/stay-awake\""
: "rm -f \"$HOME/.local/state/omarchy/indicators/stay-awake\""
persisted.idleEnabled = enabled
logEvent("idle-enabled", enabled ? "enabled" : "disabled")
if (!enabled) cancelIdleCycle("disabled")
if (stayAwakeStateWriter.running) {
root.pendingStayAwakePersist = !!value
root.hasPendingStayAwakePersist = true
return
}
stayAwakeStateWriter.command = ["bash", "-lc", command]
stayAwakeStateWriter.running = true
}
function refreshStayAwakeState() {
if (!stayAwakeStateProbe.running) stayAwakeStateProbe.running = true
}
function applyStayAwake(value, persist, reason) {
var enabled = !!value
var changed = !root.stayAwakeStateLoaded || root.stayAwake !== enabled
if (persist) persistStayAwake(enabled)
root.stayAwake = enabled
root.stayAwakeStateLoaded = true
if (!changed) return enabled ? "disabled" : "enabled"
logEvent("stay-awake", (enabled ? "enabled" : "disabled") + (reason ? " " + reason : ""))
if (enabled) cancelIdleCycle("stay-awake")
else Qt.callLater(root.handleIdleChanged)
return enabled ? "enabled" : "disabled"
return enabled ? "disabled" : "enabled"
}
function setIdleEnabled(value) {
return applyStayAwake(!value, true, "ipc")
}
IdleMonitor {
@@ -285,9 +318,40 @@ Item {
onExited: function(exitCode, exitStatus) { root.logEvent("process-exit", "wake exitCode=" + exitCode + " status=" + exitStatus) }
}
Process {
id: stayAwakeStateProbe
command: ["bash", "-lc", "mkdir -p \"$HOME/.local/state/omarchy/indicators\"; if [[ -f $HOME/.local/state/omarchy/indicators/stay-awake ]]; then echo yes; else echo no; fi"]
stdout: SplitParser {
onRead: function(line) { root.applyStayAwake(String(line).trim() === "yes", false, "state-file") }
}
onExited: function() { stayAwakeStateDirWatcher.reload() }
}
Process {
id: stayAwakeStateWriter
onExited: function() {
if (root.hasPendingStayAwakePersist) {
var pending = root.pendingStayAwakePersist
root.hasPendingStayAwakePersist = false
root.persistStayAwake(pending)
return
}
root.refreshStayAwakeState()
}
}
FileView {
id: stayAwakeStateDirWatcher
path: root.stayAwakeStateDir
watchChanges: true
printErrors: false
onFileChanged: root.refreshStayAwakeState()
}
Component.onCompleted: {
logEvent("service-ready")
Qt.callLater(root.handleIdleChanged)
refreshStayAwakeState()
}
IpcHandler {