// Copyright (C) 2026 Danilo M. // // 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" } }