diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 12:23:06 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 12:23:06 +0200 |
| commit | 59211a1bc2912a3bf39b848e48fc2dfc4c1ecd8e (patch) | |
| tree | bd7d631de1543843ff9448365b87a8d7cb2685d1 /widgets | |
| parent | a2a06d5d3e5b4b2352b0b327aa5d558b48174ab9 (diff) | |
| download | conky-theme-udt-59211a1bc2912a3bf39b848e48fc2dfc4c1ecd8e.tar.gz conky-theme-udt-59211a1bc2912a3bf39b848e48fc2dfc4c1ecd8e.zip | |
feat: add the system card with a per-core equaliser
Diffstat (limited to 'widgets')
| -rw-r--r-- | widgets/system.lua | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/widgets/system.lua b/widgets/system.lua new file mode 100644 index 0000000..c76b5b4 --- /dev/null +++ b/widgets/system.lua @@ -0,0 +1,111 @@ +-- System: CPU load as a per-core equaliser, memory, and every temperature. +-- +-- The hwmon bindings and temperature ceilings below are host-specific, which +-- is why they live here rather than in lib/data.lua: that file is the part +-- that ports to another machine, this is the part that does not. + +local card = require 'lib.card' +local data = require 'lib.data' + +local M = {} + +-- chip, file, label, warn, crit. +-- +-- Ceilings are per sensor because a single pair cannot serve both: 70C is +-- unremarkable on a CPU package and alarming on an NVMe. Starting values, +-- easy to retune once real numbers under load are known. +local TEMPS = { + { 'k10temp', 'temp1_input', 'CPU', 75, 90 }, + { 'k10temp', 'temp3_input', 'CCD1', 75, 90 }, + { 'nvme', 'temp1_input', 'NVME', 60, 70 }, + { 'gigabyte_wmi', 'temp2_input', 'BOARD', 60, 70 }, + { 'gigabyte_wmi', 'temp3_input', 'BOARD', 60, 70 }, +} + +-- One counter per core, created on first draw and kept for the process's life: +-- load is a delta between samples, so the state has to outlive the frame. +local counters = {} +local total_counter = data.new_cpu_counter() + +function M.draw(cr, rect, colors) + local inner = card.card(cr, rect, colors) + local function clamp(v, lo, hi) return math.max(lo, math.min(hi, v)) end + + local label_size = clamp(inner.w * 0.030, 11, 18) + local big_size = clamp(inner.w * 0.085, 26, 56) + local row_size = clamp(inner.w * 0.030, 11, 17) + + local stat = data.slurp('/proc/stat') + local agg_total, agg_idle = data.cpu_times(stat or '') + local load = total_counter:sample(agg_total, agg_idle) + + local y = inner.y + + -- Aggregate load, the headline number. + card.font(cr, card.FONT_MONO, label_size, false) + card.rgba(cr, colors.label) + card.text(cr, inner.x, y + label_size, 'CPU') + + card.font(cr, card.FONT_HEAVY, big_size, false) + card.rgba(cr, load and card.threshold(load, 25, 75, colors) or colors.label) + card.text(cr, inner.x, y + label_size + big_size, + load and string.format('%d%%', math.floor(load + 0.5)) or '--') + + local ey = y + label_size + big_size + 14 + + -- The equaliser: one bar per core, rising from a common baseline. + local cores = data.per_cpu_times(stat or '') + local n = #cores + if n > 0 then + local gap = math.max(2, inner.w * 0.006) + local bw = (inner.w - gap * (n - 1)) / n + local eh = clamp(inner.h * 0.16, 24, 80) + -- Below about 3px a row of sixteen bars is an illegible smear; drop to the + -- aggregate bar instead of drawing one. + if bw >= 3 then + for i, c in ipairs(cores) do + counters[i] = counters[i] or data.new_cpu_counter() + local pct = counters[i]:sample(c.total, c.idle) or 0 + card.vbar(cr, inner.x + (i - 1) * (bw + gap), ey + eh, bw, eh, + pct / 100, card.threshold(pct, 25, 75, colors), colors) + end + else + card.bar(cr, inner.x, ey + eh - 8, inner.w, 8, (load or 0) / 100, + card.threshold(load or 0, 25, 75, colors), colors) + end + ey = ey + eh + 16 + end + + -- Memory. MemAvailable, not free: it already excludes reclaimable cache, so + -- it is the figure a person recognises as "used". + local mem = data.mem_info(data.slurp('/proc/meminfo') or '') + if mem then + local frac = mem.used / mem.total + card.font(cr, card.FONT_MONO, row_size, false) + card.rgba(cr, colors.label) + card.text(cr, inner.x, ey, 'RAM') + card.rgba(cr, colors.value) + card.text_right(cr, inner.x + inner.w, ey, + string.format('%s / %s', card.human(mem.used * 1024), card.human(mem.total * 1024))) + ey = ey + 8 + card.bar(cr, inner.x, ey, inner.w, 8, frac, + card.threshold(frac * 100, 25, 75, colors), colors) + ey = ey + 22 + end + + -- Temperatures, each against its own ceiling. + card.font(cr, card.FONT_MONO, row_size, false) + local step = row_size * 1.8 + for _, t in ipairs(TEMPS) do + if ey + step > inner.y + inner.h then break end -- out of room: stop + local v = data.sensor(t[1], t[2]) + card.rgba(cr, colors.label) + card.text(cr, inner.x, ey, t[3]) + card.rgba(cr, v and card.threshold(v, t[4], t[5], colors) or colors.label) + card.text_right(cr, inner.x + inner.w, ey, + v and string.format('%d\u{00B0}', v) or '--') + ey = ey + step + end +end + +return M |
