From bee9ab476ca82fce78225210a4f9aa74fd16def1 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 21 Jul 2026 15:25:49 -0700 Subject: [PATCH] Bind the Stay Awake indicator to the idle service The indicator polled omarchy-toggle-idle over a Process and re-ran it on a timer to toggle, while the CLI called back into the shell over IPC to apply and refresh the state it had just changed. Now the indicator binds straight to the idle service's stayAwake property and flips it in process. The CLI only touches the state file, which the service already watches, so toggling from keybindings and scripts still reaches the shell without any reentrant IPC. Co-Authored-By: Claude Fable 5 --- bin/omarchy-toggle-idle | 4 -- shell/plugins/bar/indicators/StayAwake.qml | 40 ++----------------- .../fixtures/indicator-contract/shell.qml | 15 ++++++- test/shell.d/idle-test.sh | 18 +++++++++ 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/bin/omarchy-toggle-idle b/bin/omarchy-toggle-idle index 26e948ed..022a685c 100755 --- a/bin/omarchy-toggle-idle +++ b/bin/omarchy-toggle-idle @@ -33,15 +33,11 @@ apply_state() { 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 diff --git a/shell/plugins/bar/indicators/StayAwake.qml b/shell/plugins/bar/indicators/StayAwake.qml index 7a2eb9e3..70a7829e 100644 --- a/shell/plugins/bar/indicators/StayAwake.qml +++ b/shell/plugins/bar/indicators/StayAwake.qml @@ -1,51 +1,19 @@ import QtQuick -import Quickshell.Io import qs.Ui BarIndicator { id: root + readonly property var idleService: bar?.shell?.firstPartyServiceFor("omarchy.idle") + + active: idleService ? idleService.stayAwake : false activeText: "󰅶" inactiveText: "󰅶" activeTooltipText: "Allow Idle Lock & Screensaver" inactiveTooltipText: "Stay Awake" - function refresh() { - if (!statusProc.running) statusProc.running = true - } - function toggle() { - if (root.bar) root.bar.run("omarchy-toggle-idle") - refreshTimer.restart() - } - - Component.onCompleted: refresh() - - Connections { - target: root.indicatorHost - ignoreUnknownSignals: true - function onRefreshRequested() { root.refresh() } - } - - Process { - id: statusProc - command: ["omarchy-toggle-idle", "--status"] - stdout: StdioCollector { - waitForEnd: true - onStreamFinished: { - var data = root.extractData(text) - root.active = data && data.enabled === true - } - } - onExited: function(exitCode) { - if (exitCode !== 0) root.active = false - } - } - - Timer { - id: refreshTimer - interval: 1500 - onTriggered: root.refresh() + if (root.idleService) root.idleService.setIdleEnabled(root.active) } onPressed: function() { root.toggle() } diff --git a/test/shell.d/fixtures/indicator-contract/shell.qml b/test/shell.d/fixtures/indicator-contract/shell.qml index 842e7bb4..ad02aa13 100644 --- a/test/shell.d/fixtures/indicator-contract/shell.qml +++ b/test/shell.d/fixtures/indicator-contract/shell.qml @@ -24,10 +24,21 @@ ShellRoot { } } + QtObject { + id: idleService + property bool stayAwake: false + readonly property bool idleEnabled: !stayAwake + function setIdleEnabled(value) { + stayAwake = !value + } + } + QtObject { id: mockShell function firstPartyServiceFor(id) { - return id === "omarchy.notifications" ? notificationService : null + if (id === "omarchy.notifications") return notificationService + if (id === "omarchy.idle") return idleService + return null } } @@ -151,7 +162,7 @@ ShellRoot { stayAwake.moduleName = "StayAwake" root.injectBar(stayAwake) stayAwake.triggerPress(Qt.LeftButton) - root.assertTrue(root.commandCount("omarchy-toggle-idle") === 1, "Stay Awake left click runs idle toggle command") + root.assertTrue(idleService.stayAwake === true, "Stay Awake left click toggles the idle service") } root.writeResult() diff --git a/test/shell.d/idle-test.sh b/test/shell.d/idle-test.sh index 3d6dc64c..62a19c2c 100644 --- a/test/shell.d/idle-test.sh +++ b/test/shell.d/idle-test.sh @@ -34,3 +34,21 @@ assertDeepEqual( 'idle leaves screensaver windows unchanged without an address' ) JS + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +test_home="$test_tmp/home" +mkdir -p "$test_home" + +HOME="$test_home" "$ROOT/bin/omarchy-toggle-idle" stay-awake >/dev/null +[[ -f $test_home/.local/state/omarchy/indicators/stay-awake ]] || fail "Stay Awake toggle persists enabled state" + +HOME="$test_home" "$ROOT/bin/omarchy-toggle-idle" allow-idle >/dev/null +[[ ! -f $test_home/.local/state/omarchy/indicators/stay-awake ]] || fail "Stay Awake toggle persists disabled state" + +if rg -q 'omarchy-shell' "$ROOT/bin/omarchy-toggle-idle"; then + fail "Stay Awake toggle avoids reentrant shell IPC" +fi + +pass "Stay Awake toggle persists state without reentrant shell IPC"