diff --git a/modules/Battery.qml b/modules/Battery.qml index 6cfb19b..df60c3e 100644 --- a/modules/Battery.qml +++ b/modules/Battery.qml @@ -31,7 +31,7 @@ M.BarSection { NumberAnimation { target: root property: "_blinkOpacity" - to: 0.45 + to: 0.2 duration: 400 easing.type: Easing.InOutQuad } diff --git a/modules/Cpu.qml b/modules/Cpu.qml index cfe49c1..db88749 100644 --- a/modules/Cpu.qml +++ b/modules/Cpu.qml @@ -32,11 +32,6 @@ M.BarSection { readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered readonly property bool _showPanel: _anyHover || _pinned - property M.ProcessList _procs: M.ProcessList { - sortBy: "cpu" - active: root._showPanel - } - on_AnyHoverChanged: { if (_anyHover) _unpinTimer.stop(); @@ -263,7 +258,7 @@ M.BarSection { // Top processes by CPU Repeater { - model: root._procs.processes + model: M.ProcessList.byCpu delegate: Item { required property var modelData diff --git a/modules/Memory.qml b/modules/Memory.qml index 9df3491..56df098 100644 --- a/modules/Memory.qml +++ b/modules/Memory.qml @@ -21,11 +21,6 @@ M.BarSection { readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered readonly property bool _showPanel: _anyHover || _pinned - property M.ProcessList _procs: M.ProcessList { - sortBy: "mem" - active: root._showPanel - } - on_AnyHoverChanged: { if (_anyHover) _unpinTimer.stop(); @@ -228,7 +223,7 @@ M.BarSection { // Top processes by memory Repeater { - model: root._procs.processes + model: M.ProcessList.byMem delegate: Item { required property var modelData diff --git a/modules/NiriIpc.qml b/modules/NiriIpc.qml index b64fbb3..504888d 100644 --- a/modules/NiriIpc.qml +++ b/modules/NiriIpc.qml @@ -11,29 +11,6 @@ QtObject { signal windowFocusChanged(var windowId) signal windowOpenedOrChanged(var window) - property string focusedTitle: "" - property string focusedAppId: "" - property bool overviewOpen: false - - property var _focusedProc: Process { - running: true - command: ["niri", "msg", "--json", "focused-window"] - stdout: StdioCollector { - onStreamFinished: { - try { - const w = JSON.parse(text); - if (w) { - root.focusedTitle = w.title || ""; - root.focusedAppId = w.app_id || ""; - } else { - root.focusedTitle = ""; - root.focusedAppId = ""; - } - } catch (e) {} - } - } - } - property var _eventStream: Process { running: true command: ["niri", "msg", "--json", "event-stream"] @@ -46,24 +23,10 @@ QtObject { root.workspacesChanged(ev.WorkspacesChanged.workspaces); else if (ev.WorkspaceActivated !== undefined) root.workspaceActivated(ev.WorkspaceActivated.id, ev.WorkspaceActivated.focused); - else if (ev.WindowFocusChanged !== undefined) { + else if (ev.WindowFocusChanged !== undefined) root.windowFocusChanged(ev.WindowFocusChanged.id); - if (ev.WindowFocusChanged.id !== null) - root._focusedProc.running = true; - else { - root.focusedTitle = ""; - root.focusedAppId = ""; - } - } else if (ev.OverviewOpenedOrClosed !== undefined) { - root.overviewOpen = ev.OverviewOpenedOrClosed.is_open; - } else if (ev.WindowOpenedOrChanged !== undefined) { + else if (ev.WindowOpenedOrChanged !== undefined) root.windowOpenedOrChanged(ev.WindowOpenedOrChanged.window); - const w = ev.WindowOpenedOrChanged.window; - if (w.is_focused) { - root.focusedTitle = w.title || ""; - root.focusedAppId = w.app_id || ""; - } - } } catch (e) {} } } diff --git a/modules/OverviewBackdrop.qml b/modules/OverviewBackdrop.qml index c9652a6..05d098f 100644 --- a/modules/OverviewBackdrop.qml +++ b/modules/OverviewBackdrop.qml @@ -37,18 +37,10 @@ PanelWindow { property color uC1: M.Theme.base0E property color uC2: M.Theme.base09 - Connections { - target: M.NiriIpc - function onOverviewOpenChanged() { - if (!M.NiriIpc.overviewOpen) - fx.uWavePhase = -200; - } - } - - // Wave animation: 6s sweep + 8s pause, only while overview is open + // Wave animation: 6s sweep + 8s pause SequentialAnimation on uWavePhase { loops: Animation.Infinite - running: M.NiriIpc.overviewOpen + running: true NumberAnimation { from: -200 to: fx.width + 200 diff --git a/modules/ProcessList.qml b/modules/ProcessList.qml index 6678ab0..b5bfbb9 100644 --- a/modules/ProcessList.qml +++ b/modules/ProcessList.qml @@ -1,43 +1,48 @@ +pragma Singleton + import QtQuick import Quickshell.Io +import "." as M QtObject { id: root - property string sortBy: "cpu" // "cpu" or "mem" + property var byCpu: [] + property var byMem: [] property int maxItems: 8 - property bool active: false - - property var processes: [] property Process _proc: Process { - command: ["sh", "-c", "ps --no-headers -eo pid,pcpu,pmem,comm --sort=-%" + root.sortBy + " | head -" + root.maxItems] + id: proc + running: true + command: ["sh", "-c", "ps aux --sort=-%cpu 2>/dev/null | awk 'NR>1 && NR<=50 {cmd=$11; for(i=12;i<=NF&&i<=13;i++) cmd=cmd\" \"$i; print $1\"|\"$2\"|\"$3\"|\"$4\"|\"cmd}'"] stdout: StdioCollector { onStreamFinished: { const rows = []; for (const line of text.trim().split("\n")) { if (!line) continue; - const p = line.trim().split(/\s+/); - if (p.length < 4) + const p = line.split("|"); + if (p.length < 5) continue; + const cmd = p[4].replace(/^.*\//, ""); rows.push({ - "pid": parseInt(p[0]), - "cpu": parseFloat(p[1]), - "mem": parseFloat(p[2]), - "cmd": p.slice(3).join(" ") + "user": p[0], + "pid": parseInt(p[1]), + "cpu": parseFloat(p[2]), + "mem": parseFloat(p[3]), + "cmd": cmd || p[4] }); } - root.processes = rows; + root.byCpu = rows.slice().sort((a, b) => b.cpu - a.cpu).slice(0, root.maxItems); + root.byMem = rows.slice().sort((a, b) => b.mem - a.mem).slice(0, root.maxItems); } } } property Timer _timer: Timer { interval: 2000 - running: root.active + running: true repeat: true - triggeredOnStart: true - onTriggered: root._proc.running = true + onTriggered: proc.running = true } } diff --git a/modules/WindowTitle.qml b/modules/WindowTitle.qml index 8fdd0ad..6d48e6e 100644 --- a/modules/WindowTitle.qml +++ b/modules/WindowTitle.qml @@ -1,23 +1,63 @@ import QtQuick import QtQuick.Effects import Quickshell +import Quickshell.Io import Quickshell.Widgets import "." as M M.BarSection { id: root spacing: M.Theme.moduleSpacing - visible: M.Modules.windowTitle.enable && M.NiriIpc.focusedTitle !== "" + visible: M.Modules.windowTitle.enable && root._title !== "" - tooltip: M.NiriIpc.focusedAppId ? M.NiriIpc.focusedAppId + "\n" + M.NiriIpc.focusedTitle : M.NiriIpc.focusedTitle + tooltip: root._appId ? root._appId + "\n" + root._title : root._title + property string _title: "" + property string _appId: "" readonly property string _iconSource: { - if (!M.NiriIpc.focusedAppId) + if (!root._appId) return ""; - const entry = DesktopEntries.heuristicLookup(M.NiriIpc.focusedAppId); + const entry = DesktopEntries.heuristicLookup(root._appId); return entry ? Quickshell.iconPath(entry.icon) : ""; } + // Initial state — niri event-stream doesn't replay current focus + Process { + id: initProc + running: true + command: ["niri", "msg", "--json", "focused-window"] + stdout: StdioCollector { + onStreamFinished: { + try { + const w = JSON.parse(text); + if (w) { + root._title = w.title || ""; + root._appId = w.app_id || ""; + } + } catch (e) {} + } + } + } + + // Live updates via shared NiriIpc singleton + Connections { + target: M.NiriIpc + function onWindowFocusChanged(windowId) { + if (windowId !== null) + initProc.running = true; + else { + root._title = ""; + root._appId = ""; + } + } + function onWindowOpenedOrChanged(window) { + if (window.is_focused) { + root._title = window.title || ""; + root._appId = window.app_id || ""; + } + } + } + IconImage { visible: root._iconSource !== "" source: root._iconSource @@ -31,7 +71,7 @@ M.BarSection { } M.BarLabel { - label: M.NiriIpc.focusedTitle + label: root._title color: root.accentColor elide: Text.ElideRight anchors.verticalCenter: parent.verticalCenter diff --git a/modules/qmldir b/modules/qmldir index 705bf23..327123f 100644 --- a/modules/qmldir +++ b/modules/qmldir @@ -34,7 +34,7 @@ IdleInhibitor 1.0 IdleInhibitor.qml Notifications 1.0 Notifications.qml singleton NiriIpc 1.0 NiriIpc.qml singleton SystemStats 1.0 SystemStats.qml -ProcessList 1.0 ProcessList.qml +singleton ProcessList 1.0 ProcessList.qml singleton NotifService 1.0 NotifService.qml NotifItem 1.0 NotifItem.qml NotifPopup 1.0 NotifPopup.qml