aboutsummaryrefslogtreecommitdiffstats
path: root/shared/Status.qml
blob: 03194e7c61d1475a5a947f3d51212ad798cddf81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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" }
}