diff options
Diffstat (limited to 'appearance/Hypridle.qml')
| -rw-r--r-- | appearance/Hypridle.qml | 161 |
1 files changed, 0 insertions, 161 deletions
diff --git a/appearance/Hypridle.qml b/appearance/Hypridle.qml deleted file mode 100644 index 1696f5f..0000000 --- a/appearance/Hypridle.qml +++ /dev/null @@ -1,161 +0,0 @@ -// 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 - -// The hypridle side. Commands are fixed: only each listener's timeout and -// whether it is active are editable. Everything before the first listener is -// kept verbatim so the general block and the rationale comments survive. -Singleton { - id: root - - readonly property string home: Quickshell.env("HOME") - readonly property string confPath: `${home}/.config/hypr/hypridle.conf` - - property string prefix: "" - property var listeners: [] - property string tail: "" - property string notice: "" - property bool loaded: false - - function getField(body, key) { - const m = body.match(new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`, "m")); - return m ? m[1] : null; - } - - // A disabled listener is written every line prefixed with "# ". Strip it - // before reading fields. - function uncomment(body) { - return body.split("\n").map(l => l.replace(/^([ \t]*)#[ \t]?/, "$1")).join("\n"); - } - - // The closing brace is matched at the start of a line, because the dpms - // command contains a brace mid-line and a non-greedy match would stop - // inside it. A disabled listener has both its opening line and its closing - // brace commented, so the closing pattern allows a leading "#". The first - // listener's leading trivia is empty because its text is the prefix. - function parse(text) { - const re = /^([ \t]*)(#?)[ \t]*listener\s*\{([\s\S]*?)^[ \t]*#?[ \t]*\}/gm; - const found = []; - let firstStart = -1; - let prevEnd = 0; - let m; - while ((m = re.exec(text)) !== null) { - const start = m.index; - if (firstStart < 0) { firstStart = start; prevEnd = start; } - const leading = text.slice(prevEnd, start); - const enabled = m[2] !== "#"; - const body = enabled ? m[3] : root.uncomment(m[3]); - const t = root.getField(body, "timeout"); - found.push({ - leading: leading, - timeout: t === null ? 0 : parseInt(t, 10), - onTimeout: root.getField(body, "on-timeout") ?? "", - onResume: root.getField(body, "on-resume") ?? "", - enabled: enabled, - }); - prevEnd = start + m[0].length; - } - if (firstStart < 0) return { prefix: text, listeners: [], tail: "" }; - return { prefix: text.slice(0, firstStart), listeners: found, tail: text.slice(prevEnd) }; - } - - function serialize(prefix, list, tail) { - let out = prefix; - for (const l of list) { - const lines = ["listener {", ` timeout = ${l.timeout}`, - ` on-timeout = ${l.onTimeout}`]; - if (l.onResume) lines.push(` on-resume = ${l.onResume}`); - lines.push("}"); - const body = l.enabled ? lines.join("\n") : lines.map(x => "# " + x).join("\n"); - out += l.leading + body; - } - return out + tail; - } - - function describe(l) { - const cmd = l.onTimeout; - if (cmd.indexOf("notify-send") === 0) return "notify before lock"; - if (cmd.indexOf("loginctl lock-session") >= 0) return "lock session"; - if (cmd.indexOf("dpms") >= 0) return "monitors off"; - if (cmd.indexOf("loginctl suspend") >= 0) return "suspend"; - return cmd; - } - - function selftest(): string { - const fixture = String.raw`general { - lock_cmd = pidof hyprlock || hyprlock -} - -# 10:00s - screen lock -listener { - timeout = 600 - on-timeout = loginctl lock-session -} - -# 10:30s - monitor off -listener { - timeout = 630 - on-timeout = hyprctl dispatch "hl.dsp.dpms({ state = \"off\" })" - on-resume = hyprctl dispatch "hl.dsp.dpms({ state = \"on\" })" -} -`; - const p = root.parse(fixture); - if (p.listeners.length !== 2) return `SELFTEST Hypridle FAIL: ${p.listeners.length} listeners`; - if (p.listeners[0].timeout !== 600 || p.listeners[0].enabled !== true) - return "SELFTEST Hypridle FAIL: first listener"; - if (p.listeners[1].onTimeout.indexOf(String.raw`state = \"off\"`) < 0) - return "SELFTEST Hypridle FAIL: dpms command truncated"; - if (p.listeners[1].onResume.indexOf(String.raw`state = \"on\"`) < 0) - return "SELFTEST Hypridle FAIL: dpms on-resume lost"; - if (root.serialize(p.prefix, p.listeners, p.tail) !== fixture) - return "SELFTEST Hypridle FAIL: round-trip not byte-identical"; - const off = p.listeners.slice(); - off[1] = Object.assign({}, off[1], { enabled: false }); - const p2 = root.parse(root.serialize(p.prefix, off, p.tail)); - if (p2.listeners.length !== 2 || p2.listeners[1].enabled !== false || - p2.listeners[1].timeout !== 630) - return "SELFTEST Hypridle FAIL: disabled listener not round-tripped"; - return "SELFTEST Hypridle PASS"; - } - - function refresh() { - confProc.running = false; - confProc.running = true; - } - - Process { - id: confProc - command: ["cat", root.confPath] - stdout: StdioCollector { - onStreamFinished: { - const p = root.parse(text); - root.prefix = p.prefix; - root.listeners = p.listeners; - root.tail = p.tail; - root.loaded = true; - } - } - } - - FileView { id: confFile; path: root.confPath } - - function save() { - if (!root.loaded) return; // not loaded yet: writing would truncate the file - confFile.setText(root.serialize(root.prefix, root.listeners, root.tail)); - Quickshell.execDetached(["sh", "-c", "pkill -x hypridle; setsid -f hypridle"]); - root.notice = "saved; idle timers reset"; - } -} |
