move Theme, SystemStats, Modules to shell/services/
This commit is contained in:
parent
197f6976e0
commit
989182d603
59 changed files with 432 additions and 388 deletions
|
|
@ -1,185 +0,0 @@
|
|||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import "." as M
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// ── CPU ──────────────────────────────────────────────────────────────
|
||||
property int cpuUsage: 0
|
||||
property real cpuFreqGhz: 0
|
||||
property var cpuCores: [] // [{usage, freq_ghz, history:[]}] — only rebuilt while coreConsumers > 0
|
||||
property int coreConsumers: 0
|
||||
onCoreConsumersChanged: {
|
||||
if (coreConsumers > 0)
|
||||
_coreGraceTimer.stop();
|
||||
else
|
||||
_coreGraceTimer.start();
|
||||
}
|
||||
|
||||
property var _coreGraceTimer: Timer {
|
||||
interval: 30000
|
||||
onTriggered: root.cpuCores = root.cpuCores.map(() => ({
|
||||
"usage": 0,
|
||||
"freq_ghz": 0,
|
||||
"history": []
|
||||
}))
|
||||
}
|
||||
property var cpuCoreMaxFreq: []
|
||||
property var cpuCoreTypes: []
|
||||
|
||||
// ── Temperature ──────────────────────────────────────────────────────
|
||||
property int tempCelsius: 0
|
||||
property var tempHistory: [] // 150 samples @ 4s each ≈ 10 min
|
||||
property var tempDevices: [] // [{name, celsius}] sorted hottest-first
|
||||
|
||||
// ── GPU ──────────────────────────────────────────────────────────────
|
||||
property bool gpuAvailable: false
|
||||
property string gpuVendor: ""
|
||||
property int gpuUsage: 0
|
||||
property real gpuVramUsedGb: 0
|
||||
property real gpuVramTotalGb: 0
|
||||
property int gpuTempC: 0
|
||||
property var gpuHistory: [] // 60 samples @ ~4-8s each ≈ 4-8 min
|
||||
|
||||
// ── Memory ───────────────────────────────────────────────────────────
|
||||
property int memPercent: 0
|
||||
property real memUsedGb: 0
|
||||
property real memTotalGb: 0
|
||||
property real memAvailGb: 0
|
||||
property real memCachedGb: 0
|
||||
property real memBuffersGb: 0
|
||||
property var memHistory: []
|
||||
|
||||
// ── Disk ─────────────────────────────────────────────────────────────
|
||||
property var diskMounts: []
|
||||
property int diskRootPct: 0
|
||||
|
||||
// nova-stats stream (cpu + mem)
|
||||
property var _statsProc: Process {
|
||||
running: true
|
||||
command: {
|
||||
const ms = M.Modules.statsDaemon.interval;
|
||||
return ms > 0 ? ["nova-stats", "--interval", ms.toString()] : ["nova-stats"];
|
||||
}
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: line => {
|
||||
try {
|
||||
const ev = JSON.parse(line);
|
||||
if (ev.type === "cpu") {
|
||||
root.cpuUsage = ev.usage;
|
||||
root.cpuFreqGhz = ev.freq_ghz;
|
||||
if (root.coreConsumers > 0) {
|
||||
const histLen = 16;
|
||||
const prev = root.cpuCores;
|
||||
root.cpuCores = ev.cores.map((c, i) => {
|
||||
const oldHist = prev[i]?.history ?? [];
|
||||
const hist = oldHist.concat([c.usage]);
|
||||
return {
|
||||
usage: c.usage,
|
||||
freq_ghz: c.freq_ghz,
|
||||
history: hist.length > histLen ? hist.slice(hist.length - histLen) : hist
|
||||
};
|
||||
});
|
||||
} else if (root.cpuCores.length !== ev.cores.length) {
|
||||
// Keep count in sync so panel can size correctly before consumers activate
|
||||
root.cpuCores = ev.cores.map(c => ({
|
||||
"usage": c.usage,
|
||||
"freq_ghz": c.freq_ghz,
|
||||
"history": []
|
||||
}));
|
||||
}
|
||||
} else if (ev.type === "temp") {
|
||||
root.tempCelsius = ev.celsius;
|
||||
const th = root.tempHistory.concat([ev.celsius]);
|
||||
root.tempHistory = th.length > 150 ? th.slice(th.length - 150) : th;
|
||||
if (ev.devices)
|
||||
root.tempDevices = ev.devices;
|
||||
} else if (ev.type === "gpu") {
|
||||
root.gpuAvailable = true;
|
||||
root.gpuVendor = ev.vendor;
|
||||
root.gpuUsage = ev.usage;
|
||||
root.gpuVramUsedGb = ev.vram_used_gb;
|
||||
root.gpuVramTotalGb = ev.vram_total_gb;
|
||||
root.gpuTempC = ev.temp_c;
|
||||
const gh = root.gpuHistory.concat([ev.usage]);
|
||||
root.gpuHistory = gh.length > 60 ? gh.slice(gh.length - 60) : gh;
|
||||
} else if (ev.type === "mem") {
|
||||
root.memPercent = ev.percent;
|
||||
root.memUsedGb = ev.used_gb;
|
||||
root.memTotalGb = ev.total_gb;
|
||||
root.memAvailGb = ev.avail_gb;
|
||||
root.memCachedGb = ev.cached_gb;
|
||||
root.memBuffersGb = ev.buffers_gb;
|
||||
const h = root.memHistory.concat([ev.percent]);
|
||||
root.memHistory = h.length > 30 ? h.slice(h.length - 30) : h;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One-time: per-core max freq
|
||||
property var _maxFreqProc: Process {
|
||||
running: true
|
||||
command: ["sh", "-c", "for f in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/cpuinfo_max_freq; do [ -f \"$f\" ] && cat \"$f\" || echo 0; done 2>/dev/null"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.cpuCoreMaxFreq = text.trim().split("\n").filter(l => l).map(l => parseInt(l) / 1e6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One-time: P/E-core topology
|
||||
property var _coreTypesProc: Process {
|
||||
running: true
|
||||
command: ["sh", "-c", "for d in /sys/devices/system/cpu/cpu[0-9]*/topology/core_type; do [ -f \"$d\" ] && cat \"$d\" || echo Performance; done 2>/dev/null"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.cpuCoreTypes = text.trim().split("\n").filter(l => l).map(l => l.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disk via df
|
||||
property var _diskProc: Process {
|
||||
id: diskProc
|
||||
running: true
|
||||
command: ["sh", "-c", "df -x tmpfs -x devtmpfs -x squashfs -x efivarfs -x overlay -B1 --output=target,size,used 2>/dev/null | awk 'NR>1 && $2+0>0 {print $1\"|\"$2\"|\"$3}'"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const lines = text.trim().split("\n").filter(l => l);
|
||||
const mounts = [];
|
||||
for (const line of lines) {
|
||||
const parts = line.split("|");
|
||||
if (parts.length < 3)
|
||||
continue;
|
||||
const total = parseInt(parts[1]);
|
||||
const used = parseInt(parts[2]);
|
||||
if (total <= 0)
|
||||
continue;
|
||||
mounts.push({
|
||||
"target": parts[0],
|
||||
"pct": Math.round(used / total * 100),
|
||||
"usedBytes": used,
|
||||
"totalBytes": total
|
||||
});
|
||||
}
|
||||
root.diskMounts = mounts;
|
||||
const rm = mounts.find(m => m.target === "/");
|
||||
if (rm)
|
||||
root.diskRootPct = rm.pct;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
property var _diskTimer: Timer {
|
||||
interval: M.Modules.disk.interval || 30000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: diskProc.running = true
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue