aboutsummaryrefslogtreecommitdiffstats
path: root/appearance/Hypridle.qml
diff options
context:
space:
mode:
Diffstat (limited to 'appearance/Hypridle.qml')
-rw-r--r--appearance/Hypridle.qml161
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";
- }
-}