# status Desktop modes as state: `dnd`, `presentation` and `nolock`, owned by the `Status` singleton and stored as files under `$XDG_RUNTIME_DIR`. ## The files are the interface $XDG_RUNTIME_DIR/status.dnd $XDG_RUNTIME_DIR/status.presentation $XDG_RUNTIME_DIR/status.nolock Each holds `0` or `1`; a missing file means off. That directory is tmpfs, so a reboot resets every mode and there is no cleanup code. A shell restart does not: the files outlive the process and the singleton reads them back. `nolock` is stored as "auto-lock disabled", which is the inverse of the switch the page shows. The file keeps the registry's "missing means off" rule, and the page inverts it so the label reads as the default: screen lock on. Anything can read a mode with `cat`. `statusctl` is the convenience, not the mechanism, which is why it keeps working while quickshell is down. ## statusctl statusctl get prints 0 or 1 statusctl set 0|1 statusctl toggle statusctl watch waybar JSON on every change The repo copy is the source; the user installs it to `~/bin`. `watch` watches the directory rather than the file, because an atomic write replaces the file and a watch on the old inode dies with it. Setting a mode with `statusctl` records the state without firing its effects. The shell sees the change through its own `FileView` watch and asserts them, so the effects follow either way. If the shell is down, the state is recorded and reasserted when it returns. ## Effects `dnd` has none of its own. It is state the notification daemon reads. `presentation` sets `dnd` and `nolock`, asserts a Wayland idle inhibitor, and pauses breaktimer. Turning it off restores `dnd` and `nolock` to the values they had before rather than clearing them, so hand-set DND or a hand-set disable survives a presentation. `nolock` disables auto-lock by holding the idle inhibitor. That is the only thing that stops hypridle's `loginctl lock-session`, so the mode governs idle-triggered locking and nothing else: the `SUPER+l` bind and the lock on suspend are separate paths and stay live. The page can set it permanently or for a number of minutes, in which case the singleton's timer re-enables it. The timer is shell-lifetime: a restart during a timed disable leaves the lock off until it is toggled, the same class of limit as `dndBeforePresentation`. breaktimer is paused and resumed by verb, never by writing its state file. See Breaktimer below, which is also the read direction. ## Breaktimer The traffic runs both ways, and only one way writes. **Reading.** The daemon publishes three files the singleton watches: $XDG_RUNTIME_DIR/breaktimer.state running | paused | stopped $XDG_RUNTIME_DIR/breaktimer.phase working | breaking | longbreak | stopped $XDG_RUNTIME_DIR/breaktimer.remain seconds left in the phase exposed as `Status.btState`, `btPhase` and `btRemain`, with `btRunning` and `btPaused` derived from the first. `waybar-breaktimer.sh` reads the same three files and the two consumers do not know about each other. **Writing: never.** The daemon owns those files and rewrites state and phase on every transition, so a second writer would race its loop. Every control calls a verb through `runBreaktimer()`, which is why presentation mode has always called `pause` rather than writing `breaktimer.state`. **A stopped daemon** is read from the state file, not probed. Both paths that end the daemon write `stopped` there: `stop_daemon`, and the `cleanup` trap on `TERM`. A daemon lost to `KILL` leaves a stale `running` and the drawer shows a frozen countdown, a visible wrong answer the Start button resolves, which is cheaper than a liveness probe on every repaint. QML cannot send a signal, so the `kill -0` check the waybar module uses is not available here anyway. **The countdown counts in five second steps**, because that is the daemon's tick and the shell does not interpolate between its writes. A local one second timer would be a second clock drifting against the first, correcting itself with a visible jump every five seconds, and it would keep counting while the daemon is frozen outside the work window or paused. **The tile** shows breaktimer below every mode, so an active mode still owns the line and breaktimer replaces only the idle `All clear`. It does not count toward `activeCount`: a running daemon is not a mode the user switched on. The daemon's own configuration lives in `~/.config/breaktimer.conf` and is not edited from here; `breaktimer.sh config` prints what is in effect. ## Waybar `custom/presentation` reads `statusctl presentation watch`. It replaces waybar's built-in `idle_inhibitor`, which cannot be kept alongside it: that module owns its own inhibitor object, so both would have to be released before the screen could lock. ## The check ./test-statusctl.sh Points `XDG_RUNTIME_DIR` at a temporary directory, so it never touches live modes. Covers the file format, the atomic write, the toggle, the unknown-mode error, both the activated report and the absent-file off report.