pragma Singleton import QtQuick import Quickshell.Io import "." as S import NovaStats as NS QtObject { id: root property string systemState: "unknown" property string userState: "unknown" property var systemUnits: [] property var userUnits: [] readonly property int totalFailedCount: systemUnits.length + userUnits.length signal journalReady(string unitName, bool isUser, string text) function refresh() { if (!_pollProc.running) _pollProc.running = true; } // Per-fetch journal state: kept here (not on the Process) so qmllint can see them. property string _journalUnitName: "" property bool _journalIsUser: false function fetchJournal(unitName, isUser) { _journalProc.command = isUser ? ["journalctl", "--user", "-u", unitName, "-n", "80", "--no-pager", "--output=short-precise"] : ["journalctl", "-u", unitName, "-n", "80", "--no-pager", "--output=short-precise"]; root._journalUnitName = unitName; root._journalIsUser = isUser; if (_journalProc.running) _journalProc.running = false; _journalProc.running = true; } function restartUnit(unitName, isUser) { _restartProc.command = isUser ? ["systemctl", "--user", "restart", unitName] : ["pkexec", "systemctl", "restart", unitName]; if (_restartProc.running) _restartProc.running = false; _restartProc.running = true; } function _parseState(json) { try { return JSON.parse(json).data || "unknown"; } catch (e) { return "unknown"; } } function _parseUnits(json) { try { const parsed = JSON.parse(json); return (parsed.data?.[0] || []).map(u => ({ name: u[0], description: u[1], loadState: u[2], activeState: u[3], subState: u[4] })); } catch (e) { return []; } } property Timer _poll: Timer { interval: NS.ModulesService.systemdInterval ?? 15000 running: NS.ModulesService.systemdEnable repeat: true triggeredOnStart: true onTriggered: if (!root._pollProc.running) root._pollProc.running = true } // 4 lines of output: systemState, systemUnits, userState, userUnits property Process _pollProc: Process { command: ["sh", "-c", "busctl get-property --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SystemState 2>/dev/null || echo '{}'; " + "busctl call --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager ListUnitsFiltered as 1 failed 2>/dev/null || echo '{}'; " + "busctl --user get-property --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager SystemState 2>/dev/null || echo '{}'; " + "busctl --user call --json=short org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager ListUnitsFiltered as 1 failed 2>/dev/null || echo '{}'"] stdout: StdioCollector { onStreamFinished: { const lines = text.trim().split("\n"); root.systemState = root._parseState(lines[0] ?? ""); root.systemUnits = root._parseUnits(lines[1] ?? ""); root.userState = root._parseState(lines[2] ?? ""); root.userUnits = root._parseUnits(lines[3] ?? ""); } } } property Process _journalProc: Process { stdout: StdioCollector { onStreamFinished: root.journalReady(root._journalUnitName, root._journalIsUser, text) } } property Process _restartProc: Process { onRunningChanged: if (!running) root.refresh() } }