diff options
Diffstat (limited to 'desktop/modules')
| -rw-r--r-- | desktop/modules/status/LockRow.qml | 113 | ||||
| -rw-r--r-- | desktop/modules/status/README.md | 24 | ||||
| -rw-r--r-- | desktop/modules/status/StatusPage.qml | 10 | ||||
| -rw-r--r-- | desktop/modules/status/StatusTile.qml | 1 | ||||
| -rwxr-xr-x | desktop/modules/status/statusctl | 2 | ||||
| -rwxr-xr-x | desktop/modules/status/test-statusctl.sh | 7 |
6 files changed, 151 insertions, 6 deletions
diff --git a/desktop/modules/status/LockRow.qml b/desktop/modules/status/LockRow.qml new file mode 100644 index 0000000..c59c2f1 --- /dev/null +++ b/desktop/modules/status/LockRow.qml @@ -0,0 +1,113 @@ +// 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. + +import QtQuick +import QtQuick.Controls +import "../.." + +// A switch plus an optional minutes field. The switch is the auto-lock state: +// on means the screen locks on idle as usual. Turning it off asserts the idle +// inhibitor, which is the only thing that stops hypridle. A number in the field +// makes that disable temporary and it re-enables itself; an empty field makes +// it permanent. The singleton owns the state and the timer, so the effect +// outlives this row, which dies with the drawer page. +Item { + id: row + + // A tick so the row repaints when a timed disable expires on its own. + property double now: Date.now() + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: row.now = Date.now() + } + + readonly property bool lockOn: !Status.nolock + readonly property bool timed: Status.nolockUntil > row.now + + implicitHeight: Math.max(texts.implicitHeight, controls.implicitHeight) + 16 + + function hhmm(ts) { + const d = new Date(ts); + return ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2); + } + + function apply(lockOn) { + if (lockOn) { + Status.setMode("nolock", false); + } else { + const n = parseInt(minutes.text, 10); + Status.disableLockFor(isNaN(n) || n < 1 ? 0 : n); + } + } + + Column { + id: texts + anchors { + left: parent.left + right: controls.left + rightMargin: 12 + verticalCenter: parent.verticalCenter + } + spacing: 2 + + Text { + text: "Screen lock" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2; bold: true } + color: Theme.text + } + + Text { + width: parent.width + wrapMode: Text.WordWrap + text: row.lockOn ? "The screen locks automatically when the session is idle." + : row.timed ? "Auto-lock is off until " + row.hhmm(Status.nolockUntil) + "." + : "Auto-lock stays off until you turn it back on." + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.subtext + } + } + + Row { + id: controls + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + spacing: 8 + + TextField { + id: minutes + width: 48 + height: 28 + placeholderText: "min" + horizontalAlignment: TextInput.AlignHCenter + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + background: Rectangle { + radius: 6 + color: Qt.alpha(Theme.surface, 0.9) + border.width: 1 + border.color: Qt.alpha(Theme.text, 0.15) + } + validator: IntValidator { bottom: 1; top: 1440 } + } + + Switch { + id: sw + checked: row.lockOn + onToggled: row.apply(!row.lockOn) + } + } + + // The shared Switch's click handler writes `checked` directly, which drops + // the declarative binding above. Resync on any state change so the timer, + // statusctl or presentation mode moves the switch back into agreement. + onLockOnChanged: sw.checked = row.lockOn +} diff --git a/desktop/modules/status/README.md b/desktop/modules/status/README.md index 2092890..571f859 100644 --- a/desktop/modules/status/README.md +++ b/desktop/modules/status/README.md @@ -1,17 +1,22 @@ # status -Desktop modes as state: `dnd` and `presentation`, owned by the `Status` -singleton and stored as files under `$XDG_RUNTIME_DIR`. +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. @@ -35,9 +40,18 @@ and reasserted when it returns. `dnd` has none of its own. It is state the notification daemon reads. -`presentation` sets `dnd`, asserts a Wayland idle inhibitor, and pauses -breaktimer. Turning it off restores `dnd` to the value it had before rather -than clearing it, so hand-set DND survives a presentation. +`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 owns `$XDG_RUNTIME_DIR/breaktimer.state`. This module calls `breaktimer.sh pause|resume` and never writes that file: its daemon loop diff --git a/desktop/modules/status/StatusPage.qml b/desktop/modules/status/StatusPage.qml index 2fbdc9b..470856d 100644 --- a/desktop/modules/status/StatusPage.qml +++ b/desktop/modules/status/StatusPage.qml @@ -47,6 +47,16 @@ Column { color: Qt.alpha(Theme.text, 0.08) } + LockRow { + width: page.width + } + + Rectangle { + width: page.width + height: 1 + color: Qt.alpha(Theme.text, 0.08) + } + SnoozeRow { width: page.width } diff --git a/desktop/modules/status/StatusTile.qml b/desktop/modules/status/StatusTile.qml index c4b87d4..ab3a7af 100644 --- a/desktop/modules/status/StatusTile.qml +++ b/desktop/modules/status/StatusTile.qml @@ -19,5 +19,6 @@ Text { color: Status.activeCount > 0 ? Theme.text : Theme.subtext text: Status.presentation ? "Presenting" : Status.dnd ? "Do not disturb" + : Status.nolock ? "No lock" : "All clear" } diff --git a/desktop/modules/status/statusctl b/desktop/modules/status/statusctl index 8877ff3..571beef 100755 --- a/desktop/modules/status/statusctl +++ b/desktop/modules/status/statusctl @@ -25,7 +25,7 @@ set -u -MODES="dnd presentation" +MODES="dnd presentation nolock" DIR="${XDG_RUNTIME_DIR:-/tmp}" usage() { diff --git a/desktop/modules/status/test-statusctl.sh b/desktop/modules/status/test-statusctl.sh index 82e2de1..7b1b188 100755 --- a/desktop/modules/status/test-statusctl.sh +++ b/desktop/modules/status/test-statusctl.sh @@ -56,6 +56,13 @@ check "toggle from 0" "1" "$("$ctl" dnd get)" "$ctl" dnd set 0 check "set 0 writes the file" "0" "$("$ctl" dnd get)" +# nolock is a mode like any other: the mechanism is generic, this only pins +# that the name is accepted by the mode list. +"$ctl" nolock set 1 +check "nolock set 1" "1" "$("$ctl" nolock get)" +"$ctl" nolock toggle +check "nolock toggle" "0" "$("$ctl" nolock get)" + # An unknown mode is an error, not a silent success: a typo must not look # like a mode that is off. "$ctl" nosuch get >/dev/null 2>&1 |
