diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-15 11:08:05 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-15 11:13:07 +0200 |
| commit | 5e42fd4e4bf32c5214183f54c3e6bcec847c94b8 (patch) | |
| tree | 51b500d07a41eceaea205ffc6a4388ee1d5d403b /shared | |
| parent | 6d17f34c8f245f8a317e83b5b02ef0f06c95a247 (diff) | |
| download | quickshell-5e42fd4e4bf32c5214183f54c3e6bcec847c94b8.tar.gz quickshell-5e42fd4e4bf32c5214183f54c3e6bcec847c94b8.zip | |
feat(desktop): add the status singleton with the dnd mode
Modes live as files under XDG_RUNTIME_DIR, one per mode, holding 0 or 1,
with a missing file meaning off. That directory is tmpfs, so a reboot
resets every mode and no cleanup code is needed.
FileView covers both directions: atomicWrites for the write, watchChanges
for the watch, so an external writer repaints the drawer with no polling.
Both are already the documented defaults in 0.3.1 and are set explicitly
anyway, because statusctl watches close_write,moved_to precisely because an
atomic write lands as a rename. A future release flipping either default
would break the watcher with no error, and a declaration is a stronger
guarantee than a default.
The documented behaviour is that a FileView fires its own fileChanged on
setText, so the reparse compares before assigning and a self-write is a
no-op rather than a loop. That has not been observed running yet: nothing
consumes the singleton until the status module exists, so the guard stays
unproven until then.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
Diffstat (limited to 'shared')
| -rw-r--r-- | shared/Status.qml | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/shared/Status.qml b/shared/Status.qml new file mode 100644 index 0000000..03194e7 --- /dev/null +++ b/shared/Status.qml @@ -0,0 +1,80 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +pragma Singleton + +import Quickshell +import Quickshell.Io +import QtQuick + +// Desktop modes as state. One file per mode under $XDG_RUNTIME_DIR, holding +// "0" or "1"; a missing file means off. The runtime directory is tmpfs, so a +// reboot resets every mode with no cleanup code here. +// +// The files are the interface, not this singleton: statusctl reads and writes +// them directly so it works while the shell is down, and the FileView watch +// means an external write repaints the drawer with no polling. +Singleton { + id: root + + readonly property string dir: Quickshell.env("XDG_RUNTIME_DIR") || "/tmp" + + readonly property bool dnd: dndFile.value + + // Number of modes currently on. The tile shows this. + readonly property int activeCount: (root.dnd ? 1 : 0) + + function setMode(name, on) { + if (name === "dnd") dndFile.write(on); + } + + function toggleMode(name) { + if (name === "dnd") dndFile.write(!root.dnd); + } + + // One mode file. Reads "1" as true and anything else, including a missing + // file, as false. + component ModeFile: FileView { + id: mf + + property bool value: false + + // FileView is documented to fire fileChanged on its own setText, so a + // write would re-enter this handler. Comparing before assigning makes + // that harmless: the reparse yields the value just written and the + // binding does not change. + function reparse() { + const t = mf.text().trim(); + const v = (t === "1"); + if (v !== mf.value) mf.value = v; + } + + function write(on) { + const s = on ? "1\n" : "0\n"; + mf.value = on; + mf.setText(s); + } + + // Both are the documented defaults in 0.3.1, set explicitly because + // the CLI depends on them: statusctl watches close_write,moved_to + // precisely because an atomic write lands as a rename, so a future + // release flipping this default would break the watcher silently. + atomicWrites: true + watchChanges: true + printErrors: false + onFileChanged: mf.reload() + onLoaded: mf.reparse() + // A missing file is the off state, not an error worth logging. + onLoadFailed: mf.value = false + } + + ModeFile { id: dndFile; path: root.dir + "/status.dnd" } +} |
