diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 12:27:28 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 12:27:28 +0200 |
| commit | 5013893b4cbb63277b2a4c92a0dc8b851816aa93 (patch) | |
| tree | a0cd680dccee25b41e9f3b6a51c5ba284b7d7669 | |
| parent | d9c68ef7a62b45527cfb87d61e51866c5e2931a3 (diff) | |
| download | conky-theme-udt-5013893b4cbb63277b2a4c92a0dc8b851816aa93.tar.gz conky-theme-udt-5013893b4cbb63277b2a4c92a0dc8b851816aa93.zip | |
feat: add the disks and cache cards
disks draws a ring per filesystem from the df sample, with the NFS
server address read from the device field and a labelled-bar fallback
when the rings would be too small to read. cache shows the ~/.cache
total and its four largest children from the du sample.
card.ring now resets the cairo path first: a preceding card.text()
leaves a current point, and cairo_arc() joins to it with a straight
line, so the disks rings were drawn with stray chords. The ring
primitive had no caller until now, which is why it went unnoticed.
| -rw-r--r-- | dashboard.lua | 2 | ||||
| -rw-r--r-- | lib/card.lua | 4 | ||||
| -rw-r--r-- | widgets/cache.lua | 63 | ||||
| -rw-r--r-- | widgets/disks.lua | 135 |
4 files changed, 204 insertions, 0 deletions
diff --git a/dashboard.lua b/dashboard.lua index cbfdd68..9b527ce 100644 --- a/dashboard.lua +++ b/dashboard.lua @@ -24,6 +24,8 @@ local layout = { { widget = 'weather', col = 1, row = 6, w = 3, h = 5 }, { widget = 'system', col = 4, row = 1, w = 3, h = 6 }, { widget = 'gpu', col = 7, row = 1, w = 3, h = 3 }, + { widget = 'disks', col = 7, row = 4, w = 5, h = 4 }, + { widget = 'cache', col = 12, row = 4, w = 3, h = 4 }, } -- ========================================================================== diff --git a/lib/card.lua b/lib/card.lua index 8e04e0b..48a7f2e 100644 --- a/lib/card.lua +++ b/lib/card.lua @@ -184,6 +184,10 @@ function M.ring(cr, cx, cy, r, frac, colour, colors, width) if frac < 0 then frac = 0 elseif frac > 1 then frac = 1 end width = width or math.max(3, r * 0.18) + -- Start a fresh path. A preceding card.text() leaves a current point behind, + -- and cairo_arc() joins to it with a straight line, so without this every + -- ring after a label is drawn with a stray chord to the text baseline. + cairo_new_path(cr) cairo_set_line_width(cr, width) cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND) diff --git a/widgets/cache.lua b/widgets/cache.lua new file mode 100644 index 0000000..a1a42e3 --- /dev/null +++ b/widgets/cache.lua @@ -0,0 +1,63 @@ +-- Cache: ~/.cache total and its four largest subdirectories. +-- +-- Reads a cache file written by bin/cache-sample.sh. du walks the tree and +-- costs about 100ms, which cannot happen on a 2-second draw. + +local card = require 'lib.card' +local data = require 'lib.data' + +local CACHE = (os.getenv('XDG_CACHE_HOME') or (os.getenv('HOME') .. '/.cache')) + .. '/udt/cachesize.txt' + +local M = {} + +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, 10, 16) + local big_size = clamp(inner.w * 0.075, 22, 46) + + card.font(cr, card.FONT_MONO, label_size, false) + card.rgba(cr, colors.label) + card.text(cr, inner.x, inner.y + label_size, 'CACHE') + + local c = data.du_parse(data.slurp(CACHE) or '') + if not c then + card.rgba(cr, colors.label) + card.font(cr, card.FONT_UI, label_size * 1.2, true) + card.text(cr, inner.x, inner.y + label_size * 3.4, 'no cache data') + card.font(cr, card.FONT_MONO, label_size, false) + card.text(cr, inner.x, inner.y + label_size * 5, 'bin/cache-sample.sh') + return + end + + card.font(cr, card.FONT_HEAVY, big_size, false) + card.rgba(cr, colors.body) + card.text(cr, inner.x, inner.y + label_size + big_size, c.total.label) + + local ey = inner.y + label_size + big_size + label_size * 2 + local step = label_size * 2.2 + + card.font(cr, card.FONT_MONO, label_size, false) + for i, e in ipairs(c.items) do + if ey + step > inner.y + inner.h then break end + -- The biggest offender is the point of the card, so it carries colour: + -- critical when it is over half the total, warning otherwise. The rest + -- stay in the ordinary value colour so the eye lands on the one to delete. + local colour = colors.value + if i == 1 then + local share = c.total.bytes > 0 and (e.bytes / c.total.bytes) or 0 + colour = share > 0.5 and colors.critical or colors.warning + end + card.rgba(cr, colors.label) + -- Long names are truncated rather than allowed to collide with the size. + local name = e.name + if #name > 18 then name = name:sub(1, 17) .. '\u{2026}' end + card.text(cr, inner.x, ey, name) + card.rgba(cr, colour) + card.text_right(cr, inner.x + inner.w, ey, e.label) + ey = ey + step + end +end + +return M diff --git a/widgets/disks.lua b/widgets/disks.lua new file mode 100644 index 0000000..aff3e5b --- /dev/null +++ b/widgets/disks.lua @@ -0,0 +1,135 @@ +-- Disks: one ring per filesystem, after idea2.png. +-- +-- Reads a cache file written by bin/disks-sample.sh and never calls statfs +-- itself: an unreachable NFS server blocks that call, and blocking the draw +-- freezes the whole dashboard. + +local card = require 'lib.card' +local data = require 'lib.data' + +local M = {} + +local CACHE = (os.getenv('XDG_CACHE_HOME') or (os.getenv('HOME') .. '/.cache')) + .. '/udt/disks.txt' + +-- mount -> centre glyph and the small text under it. +-- +-- The icon IS the label: no mount name is drawn, because a Slackware S or a +-- house says which filesystem this is faster than the word does. Every +-- codepoint here was taken from the old conky config and re-rendered to +-- confirm it, rather than guessed: an earlier guess at the Slackware mark +-- (F83C) turned out to draw a stomach, and a wrong-but-present codepoint +-- fails plausibly rather than visibly. +-- +-- The two NFS shares are told apart by their server's address, which is read +-- from the mount's device field at runtime rather than written here. The +-- addresses belong on the screen, not in a public repo, and deriving them also +-- means the card keeps working if the LAN is renumbered. It fits: at a 50px +-- radius the usable inner width is about 75px and an address measures 58px at +-- 10px type. +-- +-- A mount not listed still draws, with a generic disk glyph and the last path +-- segment, so adding one to the sampler needs no change here. +local GLYPH = { + ['/'] = '\u{F318}', -- Slackware + ['/home'] = '\u{F015}', -- house + ['/data'] = '\u{F02CA}', -- hard disk +} +local NFS_GLYPH = '\u{F06F3}' -- network share +local FALLBACK_GLYPH = '\u{F02CA}' + +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, 10, 16) + + card.font(cr, card.FONT_MONO, label_size, false) + card.rgba(cr, colors.label) + card.text(cr, inner.x, inner.y + label_size, 'DISKS') + + local fs = data.df_parse(data.slurp(CACHE) or '') + if #fs == 0 then + card.rgba(cr, colors.label) + card.font(cr, card.FONT_UI, label_size * 1.2, true) + card.text(cr, inner.x, inner.y + label_size * 3.4, 'no disk data') + card.font(cr, card.FONT_MONO, label_size, false) + card.text(cr, inner.x, inner.y + label_size * 5, 'bin/disks-sample.sh') + return + end + + local top = inner.y + label_size * 2.2 + local avail_h = (inner.y + inner.h) - top + + -- Lay the rings out in a row, wrapping if the card is narrow. Below the + -- radius where a ring reads, fall back to labelled bars rather than drawing + -- a row of illegible dots. + local n = #fs + local per_row = n + local cell_w = inner.w / per_row + local r = math.min(cell_w * 0.34, avail_h * 0.30) + + if r >= 18 then + for i, e in ipairs(fs) do + -- host is non-nil exactly for a network mount, since a local device is + -- a path and carries no 'server:' prefix. + local glyph = GLYPH[e.mount] or (e.host and NFS_GLYPH) or FALLBACK_GLYPH + local sub = e.host + if not sub and not GLYPH[e.mount] then + sub = (e.mount:match('([^/]+)/?$') or e.mount):upper() + end + local cx = inner.x + cell_w * (i - 0.5) + local cy = top + r + 6 + local colour = card.threshold(e.pct, 25, 75, colors) + + card.ring(cr, cx, cy, r, e.pct / 100, colour, colors) + + -- Glyph at the centre. Drawn at 0.55 of the radius rather than 0.7: the + -- network-share glyph carries a wide horizontal connector that runs past + -- the ring at larger sizes, which reads as a line joining the rings + -- together. + local gsize = r * 0.55 + card.font(cr, card.FONT_MONO, gsize, false) + card.rgba(cr, colors.label) + local gw = card.measure(cr, glyph) + -- Raised when there is text under it, centred when there is not. + card.text(cr, cx - gw / 2, cy + (sub and gsize * 0.10 or gsize * 0.38), glyph) + + -- The address, inside the ring beneath the glyph. Only the NFS shares + -- have one; the local mounts are identified by their icon alone. + if sub then + card.font(cr, card.FONT_MONO, clamp(r * 0.20, 9, 12), false) + card.rgba(cr, colors.label) + local sw_ = card.measure(cr, sub) + card.text(cr, cx - sw_ / 2, cy + r * 0.55, sub) + end + + -- The percentage sits under the ring, in the threshold colour. + card.font(cr, card.FONT_MONO, label_size, false) + card.rgba(cr, colour) + local pct = string.format('%d%%', e.pct) + local pw = card.measure(cr, pct) + card.text(cr, cx - pw / 2, cy + r + label_size * 1.7, pct) + end + else + local step = label_size * 2.6 + local ey = top + label_size + for _, e in ipairs(fs) do + if ey + step > inner.y + inner.h then break end + -- The bar fallback has no room for a ring, so it shows the glyph inline + -- followed by the address or the mount name. + local glyph = GLYPH[e.mount] or (e.host and NFS_GLYPH) or FALLBACK_GLYPH + local name = e.host or (e.mount:match('([^/]+)/?$') or e.mount):upper() + local colour = card.threshold(e.pct, 25, 75, colors) + card.font(cr, card.FONT_MONO, label_size, false) + card.rgba(cr, colors.label) + card.text(cr, inner.x, ey, glyph .. ' ' .. name) + card.rgba(cr, colour) + card.text_right(cr, inner.x + inner.w, ey, + string.format('%s / %s', card.human(e.used), card.human(e.size))) + card.bar(cr, inner.x, ey + 6, inner.w, 6, e.pct / 100, colour, colors) + ey = ey + step + end + end +end + +return M |
