import QtQuick import Quickshell.Io // Telemetry for one NVIDIA card, read from nvidia-smi. // // This file exists to keep the *shape* of a GPU reading separate from the way // one particular vendor reports it. Everything above it — the panel, the bar // glyph, the activity heuristic — reads only the properties declared here and // never learns where they came from. A second vendor is then a sibling file // exposing the same names, not a rewrite. // // The contract, in the units the panel expects: // // present bool a card of this vendor was found and answered // vendor string short label for the pills ("NVIDIA") // name string marketing name of the card // util int % // memUsed real MiB memTotal real MiB // temp int °C // powerDraw real W powerLimit real W, 0 when unsupported // clock int MHz clockMax int MHz, 0 when unknown // sampled() signal emitted after every reading that parsed // // AMD is the obvious next one and needs no process at all: amdgpu publishes the // same figures as plain files under /sys/class/drm/cardN/device — gpu_busy_percent, // mem_info_vram_used/_total, and hwmon's temp1_input, power1_average, freq1_input. // It is deliberately not written here: with no Radeon to test against, the // activity heuristic could only be guessed at, and this widget's whole point is // that the heuristic was measured rather than assumed. Item { id: source readonly property string vendor: "NVIDIA" property bool present: false property string name: "" property int util: 0 property real memUsed: 0 property real memTotal: 0 property int temp: 0 property real powerDraw: 0 property real powerLimit: 0 property int clock: 0 property int clockMax: 0 signal sampled() function poll() { if (!queryProc.running) queryProc.running = true } function parseNumber(value) { // nvidia-smi prints "[N/A]" for fields a chip does not implement, which // parseFloat turns into NaN; those read as 0 and the panel decides what to // show instead. See powerLimit, which is how POWER becomes CORE CLOCK. var n = parseFloat(String(value).trim()) return isFinite(n) ? n : 0 } Process { id: queryProc command: ["nvidia-smi", "--query-gpu=name,utilization.gpu,memory.used,memory.total,temperature.gpu,power.draw,power.limit,clocks.current.graphics,clocks.max.graphics", "--format=csv,noheader,nounits"] stdout: StdioCollector { waitForEnd: true onStreamFinished: { var raw = String(text || "").trim() if (raw === "") { source.present = false return } var parts = raw.split(",") for (var i = 0; i < parts.length; i++) parts[i] = parts[i].trim() source.present = true source.name = parts[0] || "" source.util = Math.round(source.parseNumber(parts[1])) source.memUsed = source.parseNumber(parts[2]) source.memTotal = source.parseNumber(parts[3]) source.temp = Math.round(source.parseNumber(parts[4])) source.powerDraw = source.parseNumber(parts[5]) source.powerLimit = source.parseNumber(parts[6]) source.clock = Math.round(source.parseNumber(parts[7])) source.clockMax = Math.round(source.parseNumber(parts[8])) source.sampled() } } } }