fix(qs,fluxo) performance fix for repaints
This commit is contained in:
+101
-140
@@ -7,12 +7,18 @@ import Quickshell.Io
|
||||
// Hardware telemetry, sourced from the user's `fluxo` daemon. fluxo's hardware
|
||||
// modules are configured to emit raw pipe-delimited values (see
|
||||
// ~/.config/fluxo/config.toml); everything here is parsing and formatting.
|
||||
//
|
||||
// Fed by a single long-lived `fluxo stream` subscription. This used to be two
|
||||
// Timers driving seven separate `Process` objects, each forking a `fluxo
|
||||
// <module>` client — five forks every 2 s plus two more every 20 s, so about
|
||||
// 2.5 process spawns a second, each one exec'ing and dynamically linking a 13 MB
|
||||
// binary to read a number the daemon already had in memory, and each metric
|
||||
// still up to one interval stale. `fluxo stream` keeps one connection open and
|
||||
// pushes a JSON line only when a module's output actually changes: no forks, and
|
||||
// values land as soon as the daemon has them.
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Poll interval. The bar reads these continuously, so keep it modest.
|
||||
property int interval: 2000
|
||||
|
||||
// ------------------------------------------------------------------ cpu
|
||||
property real cpuUsage: 0
|
||||
property real cpuTemp: 0
|
||||
@@ -56,157 +62,112 @@ Singleton {
|
||||
// UPower drives the battery UI; fluxo supplies the TLP/AC detail line.
|
||||
property string powerDetail: ""
|
||||
|
||||
// fluxo wraps values in zero-width spaces for Waybar's benefit.
|
||||
// The one-shot `fluxo <module>` client wraps text in zero-width spaces so
|
||||
// Waybar's proportional font stops reflowing. The stream deliberately does
|
||||
// not, but stripping them costs nothing and keeps this parser usable against
|
||||
// either source.
|
||||
function _clean(s: string): string {
|
||||
return (s || "").replace(//g, "").trim();
|
||||
}
|
||||
|
||||
function _fields(json: string): var {
|
||||
try {
|
||||
const o = JSON.parse(json);
|
||||
return { parts: root._clean(o.text).split("|"), obj: o };
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _num(v): real {
|
||||
const n = parseFloat(v);
|
||||
return isNaN(n) ? 0 : n;
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: root.interval
|
||||
// ------------------------------------------------------------------ feed
|
||||
// Modules fluxo pushes to us. `disk` streams the default mount, which fluxo
|
||||
// resolves to "/" (see `signaler_default_args` in its registry).
|
||||
readonly property var modules: ["cpu", "mem", "gpu", "net", "sys", "disk", "power"]
|
||||
|
||||
// One line of the stream: {"module":"cpu","text":"7.4|41.0","tooltip":...}.
|
||||
function _ingest(line: string): void {
|
||||
let ev;
|
||||
try {
|
||||
ev = JSON.parse(line);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
if (!ev || !ev.module)
|
||||
return;
|
||||
|
||||
const parts = root._clean(ev.text).split("|");
|
||||
const tip = ev.tooltip || "";
|
||||
|
||||
switch (ev.module) {
|
||||
case "cpu":
|
||||
root.cpuUsage = root._num(parts[0]);
|
||||
root.cpuTemp = root._num(parts[1]);
|
||||
root.cpuModel = tip;
|
||||
break;
|
||||
case "mem":
|
||||
root.memUsed = root._num(parts[0]);
|
||||
root.memTotal = root._num(parts[1]);
|
||||
break;
|
||||
case "gpu":
|
||||
// fluxo emits a single non-numeric field ("No GPU") when it cannot
|
||||
// find one, so field count is the availability test.
|
||||
if (parts.length < 4) {
|
||||
root.gpuAvailable = false;
|
||||
break;
|
||||
}
|
||||
root.gpuAvailable = true;
|
||||
root.gpuUsage = root._num(parts[0]);
|
||||
root.gpuVramUsed = root._num(parts[1]);
|
||||
root.gpuVramTotal = root._num(parts[2]);
|
||||
root.gpuTemp = root._num(parts[3]);
|
||||
const gm = /Model:\s*(.+)/.exec(tip);
|
||||
if (gm)
|
||||
root.gpuModel = gm[1];
|
||||
break;
|
||||
case "net":
|
||||
root.netInterface = parts[0] || "";
|
||||
root.netIp = parts[1] || "";
|
||||
root.netRx = root._num(parts[2]);
|
||||
root.netTx = root._num(parts[3]);
|
||||
break;
|
||||
case "sys":
|
||||
root.uptime = parts[0] || "";
|
||||
root.load1 = root._num(parts[1]);
|
||||
root.load5 = root._num(parts[2]);
|
||||
root.load15 = root._num(parts[3]);
|
||||
const pm = /Processes:\s*(\d+)/.exec(tip);
|
||||
if (pm)
|
||||
root.procs = parseInt(pm[1]);
|
||||
break;
|
||||
case "disk":
|
||||
root.diskUsed = root._num(parts[1]);
|
||||
root.diskTotal = root._num(parts[2]);
|
||||
root.diskPercent = ev.percentage || 0;
|
||||
const fm = /Free:\s*(\S+)/.exec(tip);
|
||||
root.diskFree = fm ? fm[1] : "";
|
||||
break;
|
||||
case "power":
|
||||
root.powerDetail = tip;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: source
|
||||
command: ["fluxo", "stream"].concat(root.modules)
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
cpuReader.running = true;
|
||||
memReader.running = true;
|
||||
gpuReader.running = true;
|
||||
netReader.running = true;
|
||||
sysReader.running = true;
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: data => root._ingest(data)
|
||||
}
|
||||
|
||||
// The daemon restarting (or not being up yet at login) drops us. Retry
|
||||
// on a slow cadence: this is a reconnect, not a poll, so it only ever
|
||||
// fires while the stream is actually down.
|
||||
onExited: retry.restart()
|
||||
}
|
||||
|
||||
// Disk and battery move slowly; poll them a tenth as often.
|
||||
Timer {
|
||||
interval: root.interval * 10
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
diskReader.running = true;
|
||||
powerReader.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cpuReader
|
||||
command: ["fluxo", "cpu"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f) return;
|
||||
root.cpuUsage = root._num(f.parts[0]);
|
||||
root.cpuTemp = root._num(f.parts[1]);
|
||||
root.cpuModel = f.obj.tooltip || "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: memReader
|
||||
command: ["fluxo", "mem"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f) return;
|
||||
root.memUsed = root._num(f.parts[0]);
|
||||
root.memTotal = root._num(f.parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: gpuReader
|
||||
command: ["fluxo", "gpu"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f || f.parts.length < 4) {
|
||||
root.gpuAvailable = false;
|
||||
return;
|
||||
}
|
||||
root.gpuAvailable = true;
|
||||
root.gpuUsage = root._num(f.parts[0]);
|
||||
root.gpuVramUsed = root._num(f.parts[1]);
|
||||
root.gpuVramTotal = root._num(f.parts[2]);
|
||||
root.gpuTemp = root._num(f.parts[3]);
|
||||
const m = /Model:\s*(.+)/.exec(f.obj.tooltip || "");
|
||||
if (m) root.gpuModel = m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: diskReader
|
||||
command: ["fluxo", "disk", "/"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f) return;
|
||||
root.diskUsed = root._num(f.parts[1]);
|
||||
root.diskTotal = root._num(f.parts[2]);
|
||||
root.diskPercent = f.obj.percentage || 0;
|
||||
const m = /Free:\s*(\S+)/.exec(f.obj.tooltip || "");
|
||||
root.diskFree = m ? m[1] : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: netReader
|
||||
command: ["fluxo", "net"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f) return;
|
||||
root.netInterface = f.parts[0] || "";
|
||||
root.netIp = f.parts[1] || "";
|
||||
root.netRx = root._num(f.parts[2]);
|
||||
root.netTx = root._num(f.parts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: sysReader
|
||||
command: ["fluxo", "sys"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const f = root._fields(text);
|
||||
if (!f) return;
|
||||
root.uptime = f.parts[0] || "";
|
||||
root.load1 = root._num(f.parts[1]);
|
||||
root.load5 = root._num(f.parts[2]);
|
||||
root.load15 = root._num(f.parts[3]);
|
||||
const m = /Processes:\s*(\d+)/.exec(f.obj.tooltip || "");
|
||||
if (m) root.procs = parseInt(m[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: powerReader
|
||||
command: ["fluxo", "power"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
root.powerDetail = JSON.parse(text).tooltip || "";
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
id: retry
|
||||
interval: 2000
|
||||
repeat: false
|
||||
onTriggered: source.running = true
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ formatting
|
||||
|
||||
Reference in New Issue
Block a user