-- 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) local fs = data.df_parse(data.slurp(CACHE) or '') if #fs == 0 then card.notice(cr, inner, colors, 'DISKS', 'no disk data', 'bin/disks-sample.sh') return end card.font(cr, card.FONT_MONO, label_size, false) card.title(cr, inner.x, inner.y + label_size, card.ICON.disks, 'DISKS', colors) local top = inner.y + label_size * 2.2 local avail_h = (inner.y + inner.h) - top -- All rings share one row: the cell width shrinks as mounts are added and -- the ring radius follows it, so the layout never wraps. 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 -- 0.27 rather than 0.30: two rows sit under each ring now, not one, and the -- radius is what gives up the height. local r = math.min(cell_w * 0.34, avail_h * 0.27) 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 -- Tied to the card's own label scale rather than a fixed px floor, so -- every piece of small mono text on the card tracks the same size. card.font(cr, card.FONT_MONO, clamp(r * 0.20, label_size * 0.7, label_size), false) card.rgba(cr, colors.label) local sw_ = card.measure(cr, sub) card.text(cr, cx - sw_ / 2, cy + r * 0.55, sub) end -- Free on top, total beneath, both centred under the ring. -- -- The percentage that used to sit here is gone: the ring already draws -- it as an angle, and a number repeating it costs a row in a column -- about 75px wide. Free carries the threshold colour because it is the -- measured quantity; the total is fixed, so it stays in `value`. One -- coloured number per column, as the rest of the board reads. local free_size = label_size local total_size = label_size * 0.88 card.font(cr, card.FONT_MONO, free_size, false) card.rgba(cr, colour) local free_txt = card.human(e.avail) .. ' free' local fw = card.measure(cr, free_txt) card.text(cr, cx - fw / 2, cy + r + free_size * 1.7, free_txt) card.font(cr, card.FONT_MONO, total_size, false) card.rgba(cr, colors.value) local total_txt = card.human(e.size) local tw = card.measure(cr, total_txt) card.text(cr, cx - tw / 2, cy + r + free_size * 1.7 + total_size * 1.4, total_txt) end else local step = label_size * 2.6 local bar_h = clamp(label_size * 0.5, 4, 8) 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 + label_size * 0.5, inner.w, bar_h, e.pct / 100, colour, colors) ey = ey + step end end end return M