Add top-processes drawer to the CPU widget
Clicking the CPU pill opens a popup listing the highest-CPU processes, refreshed live while open. services/topproc.sh computes top-style per-process CPU% and mem% from two /proc snapshots and prints one ranked frame per run; SysStats streams it only while procPollEnabled (drawer open), so the bar pays nothing at rest. CLAUDE.md documents the architecture and the runtime constraints discovered here (helper scripts must be git-tracked for `nix run`; single-shot + exit to flush stdout; bash+coreutils only; StdioCollector/Timer don't fire in this Quickshell build; fixed-size popup to avoid stale-buffer on resize under Sway). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,15 @@ Singleton {
|
||||
property var coreLoads: []
|
||||
property int coreCount: 0
|
||||
|
||||
// --- top processes (only streamed while a consumer opts in) ---
|
||||
// Each entry: { name, cpu (top-style %, 100 = one full core), mem (% of RAM) }.
|
||||
// Set procPollEnabled true (e.g. when the CPU drawer opens) to start the
|
||||
// streamer; false when nothing is watching, so the bar pays nothing at rest.
|
||||
property var topProcs: []
|
||||
readonly property int procCount: 6 // how many to show, highest CPU first
|
||||
property bool procPollEnabled: false
|
||||
readonly property int procInterval: 2 // seconds between frames
|
||||
|
||||
readonly property bool hasBattery: battery >= 0
|
||||
|
||||
// poll interval in seconds
|
||||
@@ -214,6 +223,25 @@ Singleton {
|
||||
root._prevIface = ifc;
|
||||
}
|
||||
|
||||
// Build topProcs from one run of topproc.sh: lines of
|
||||
// "P <cpu%> <mem%> <name>". The script already did the delta math and
|
||||
// ranking, so this just maps the lines to objects.
|
||||
function _parseProcFrame(text) {
|
||||
if (!text) return;
|
||||
const lines = text.split("\n");
|
||||
const out = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const p = lines[i].split(" ");
|
||||
if (p[0] !== "P" || p.length < 4) continue;
|
||||
out.push({
|
||||
cpu: Number(p[1]),
|
||||
mem: Number(p[2]),
|
||||
name: p.slice(3).join(" ")
|
||||
});
|
||||
}
|
||||
root.topProcs = out;
|
||||
}
|
||||
|
||||
function _tickOnce() {
|
||||
statView.reload();
|
||||
cpuInfoView.reload();
|
||||
@@ -280,6 +308,41 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- top processes (opt-in, drawer-driven) ----------------------------
|
||||
// services/topproc.sh prints one ranked frame and exits; we re-run it every
|
||||
// ~procInterval (the Timer re-arms it once it exits, like dfProc) only while
|
||||
// procPollEnabled (a drawer is open), so the bar pays nothing at rest. The
|
||||
// script itself sleeps procInterval between its two /proc snapshots, so each
|
||||
// run takes about that long; the Timer just relaunches promptly after.
|
||||
|
||||
property var _procFrame: [] // stdout lines accumulated for the current run
|
||||
|
||||
// topproc.sh prints one ranked frame and exits; accumulate its lines with a
|
||||
// SplitParser (StdioCollector.streamFinished doesn't fire in this Quickshell
|
||||
// build) and parse them on exit, then re-arm for the next frame while the
|
||||
// drawer stays open. discover.sh is the reference for the SplitParser pattern.
|
||||
Process {
|
||||
id: procScan
|
||||
command: ["bash", Quickshell.shellPath("services/topproc.sh"),
|
||||
String(root.procInterval), String(root.procCount)]
|
||||
onStarted: root._procFrame = [];
|
||||
onExited: {
|
||||
root._parseProcFrame(root._procFrame.join("\n"));
|
||||
if (root.procPollEnabled) // re-arm for the next frame
|
||||
Qt.callLater(function () { procScan.running = true; });
|
||||
}
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: line => { if (line) root._procFrame.push(line); }
|
||||
}
|
||||
}
|
||||
|
||||
// Kick the loop when the drawer opens; clear rows when it closes.
|
||||
onProcPollEnabledChanged: {
|
||||
if (procPollEnabled) procScan.running = true;
|
||||
else root.topProcs = [];
|
||||
}
|
||||
|
||||
// Human-readable byte rate with kB as the smallest unit, e.g. "1.2M", "8K".
|
||||
function fmtRate(bytes) {
|
||||
const u = ["K", "M", "G"];
|
||||
|
||||
Reference in New Issue
Block a user