aboutsummaryrefslogtreecommitdiffstats
path: root/test/render.lua
blob: 3ed5f7ebcb82210fbb33554161b57ac39519f9f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
-- 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 <widget> <cols> <rows> <w> <h> <out.png>')
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))