-- Render a widget to a PNG without conky. -- -- Conky ships its Cairo bindings as a loadable module, so a widget's draw() -- can be called against an image surface from plain Lua. That turns -- edit-install-restart-toggle-screenshot into one command, which is how the -- weather card's proportions were fixed. -- -- lua test/render.lua weather 16 12 3 5 /tmp/w.png -- -- Caveat: this crops to the single card. It says nothing about how the card -- looks NEXT to its neighbours, which is a real failure mode (the weather -- card's type once looked fine here and tiny beside the clock). Check the -- live dashboard before believing anything about relative size. package.cpath = '/usr/lib64/conky/lib?.so;' .. package.cpath package.path = './?.lua;' .. package.path require 'cairo' local widget_name = arg[1] or error('usage: render.lua ') local COLS = tonumber(arg[2]) or 8 local ROWS = tonumber(arg[3]) or 6 local wspan = tonumber(arg[4]) or 2 local hspan = tonumber(arg[5]) or 3 local out = arg[6] or '/tmp/widget.png' local GAP, MARGIN = 16, 28 local sw, sh = 2560, 1080 -- The palette, read from the rendered conky.conf exactly as dashboard.lua -- does, so the PNG uses the real scheme rather than invented colours. local function config_colors() local home = os.getenv('HOME') if not home then return {} end local path = home .. '/.config/conky/conky.conf' local f = io.open(path, 'r') if not f then return {} end local src = f:read('*a') f:close() local c = {} for k, v in src:gmatch("([%w_]+)%s*=%s*'(#%x%x%x%x%x%x)'") do c[k] = v end return c end local CFG = config_colors() local function hex(s, fallback) local r, g, b = tostring(s or ''):match('^#?(%x%x)(%x%x)(%x%x)$') if not r then return fallback or { 1, 0, 1 } end return { tonumber(r, 16) / 255, tonumber(g, 16) / 255, tonumber(b, 16) / 255 } end local colors = { heading = hex(CFG.color1), label = hex(CFG.color2), border = hex(CFG.color3), rule = hex(CFG.color3), value = hex(CFG.color4), highlight = hex(CFG.color5), ok = hex(CFG.color6), critical = hex(CFG.color7), warning = hex(CFG.color8, { 0.88, 0.69, 0.41 }), body = hex(CFG.default_color), surface = hex(CFG.default_shade_color), } local cw = (sw - MARGIN * 2 - GAP * (COLS - 1)) / COLS local ch = (sh - MARGIN * 2 - GAP * (ROWS - 1)) / ROWS local rect = { x = MARGIN, y = MARGIN, w = cw * wspan + GAP * (wspan - 1), h = ch * hspan + GAP * (hspan - 1), } local surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect.w + 40, rect.h + 40) local cr = cairo_create(surf) -- A backdrop close to the dashboard's own, so contrast reads honestly. local bg = colors.surface cairo_set_source_rgb(cr, bg[1] * 0.6, bg[2] * 0.6, bg[3] * 0.7) cairo_paint(cr) cairo_translate(cr, 20 - rect.x, 20 - rect.y) local ok, mod = pcall(require, 'widgets.' .. widget_name) if not ok then print('cannot load widget ' .. widget_name .. ': ' .. tostring(mod)) os.exit(1) end local drew, err = pcall(mod.draw, cr, rect, colors) if not drew then print('DRAW ERROR: ' .. tostring(err)) os.exit(1) end cairo_destroy(cr) cairo_surface_write_to_png(surf, out) cairo_surface_destroy(surf) print(string.format('%s at %dx%d on a %dx%d grid -> %s', widget_name, wspan, hspan, COLS, ROWS, out))