-
-
Notifications
You must be signed in to change notification settings - Fork 1
Theme Architecture Codegen
How a project JSON becomes a runnable Conky theme folder.
Project (nodes, edges, canvas, props)
│
▼
builder.build_project()
│
├── scripts/ (family scripts + custom scripts + album-art fetchers)
├── images/ (resolved image props + Custom Lua asset_paths)
├── assets/ (same assets for Lua that expects ASSETS_DIR)
├── fonts/ (when collected)
├── render.lua (framework + per-node emission)
├── conky.conf (window + hooks + lua_load)
├── start.sh (setsid, lock, daemon loops)
├── theme.json
└── README.md
Live Preview uses the same builder path into a temp/workdir, then runs Conky — not a separate renderer.
| Piece | Role |
|---|---|
| NodeInstance |
id, type, x/y (editor only), props, z, visible, locked, click region fields |
| Edge |
src_node → dst_node + dst_prop (one wire per bindable prop) |
| CanvasSettings | width, height, alignment, gaps, fps, stats_hz, window_type, transparency |
| Groups / labels | Editor-only; ignored by codegen |
Hidden visuals (visible: false) are skipped entirely by codegen. Unused native sources are not polled. Custom Script nodes in daemon mode are still launched by start.sh even when unwired (pure-Lua themes read their caches directly).
Sources that need external scripts belong to a family (weather, gpu_stats, custom id, …).
| Mode | Who polls | start.sh role |
|---|---|---|
| execi | Conky (execi / equivalent in generated paths) |
No background loop for that family |
| daemon | Background shell loop | Initial run + while true; do sleep N; script; done under setsid |
| Album art | Always daemon-style (writes an image file) | Loop per album-art node interval |
daemon_families_used(project) collects intervals from:
- Wired sources with
poll_mode == daemon - Every Album Art node
-
Unwired
source.custom_scriptnodes withpoll_mode == daemon(so sensors.sh / weather.sh still run after a pure-Lua import)
If several nodes share a family key, the shortest interval wins.
| Case | Build behaviour |
|---|---|
Inline script_body non-empty |
Written to scripts/; wins over path |
Self-caching (self_caching prop or body/path matches CACHE_FILE / sensors.cache / weather.cache) |
Copied or written under original basename; no stdout-capture wrapper |
| Daemon, not self-caching | Wrapper script captures stdout → custom_<id>.cache
|
Companion .conf
|
Copied beside the script when path or importer recorded it |
| Cache path patch | Known HOME/.cache/... patterns rewritten toward theme .runtime-cache at import when possible |
Scripts write cache files. render.lua reads family caches with safe parsers so a missing file does not crash the draw. Custom Lua that still uses its own read_kv_cache(CACHE_DIR .. '/sensors.cache') relies on the daemon script, not on SRC[].
Generated by conky_conf_gen after session resolution:
| Step | Detail |
|---|---|
window_type |
Canvas auto → discovery.resolve_window_type() (session-specific default, usually normal) |
own_window* |
Undecorated / below / sticky / skip taskbar hints |
| size |
minimum_width/height from Canvas |
| timing |
update_interval from FPS |
| lua_load | Self-locating: resolves render.lua next to the conf via debug.getinfo so the folder is relocatable |
| lua_draw_hook_post |
main_draw (draw after Conky’s own pass) |
| lua_mouse_hook | Emitted only if the project has clickable nodes |
| empty conky.text | All visible HUD content comes from Lua/Cairo |
Optional Wayland/session warning is embedded as a conf comment when detection reports a problem.
Rough layers:
FRAMEWORK_LUA -- shared helpers, caches, surface, images
+ constants -- THEME_DIR, SCRIPTS_DIR, IMAGES_DIR, CACHE_DIR, FPS
+ refresh_sources() -- wired native / family / custom sources → SRC[]
+ draw_node_*() -- per visible visual, including Custom Lua bodies
+ main_draw() -- surface, refresh cadence, z-ordered draws
+ mouse_handler() -- optional click regions
Each visual.custom_lua becomes a draw_node_<id>(cr, W, H) that:
-
cairo_save+ translate by Offset X/Y - Injects
in1…in12: bound prop →SRC['…']expression; unwired →nil - Runs the node’s code text (imported or hand-written)
cairo_restore
Custom Lua is an escape hatch: it does not need edges to draw. Edges only matter if the author reads in1…in12.
| Store | Purpose |
|---|---|
| IMAGE_CACHE | PNG/SVG handles; false = known missing (warn once) |
| CACHE_KV | Parsed script cache files, refreshed on source refresh |
| HIST | Ring buffers for history graphs keyed by node id |
| SRC | Per-source current values from refresh_sources()
|
| Plugin tables | e.g. smoothers when plugins are present |
Logic nodes become expressions substituted into consumers. Evaluation order follows edge dependency.
get_draw_surface()
→ conky_surface() if available (Conky owns → do NOT destroy)
→ else cairo_xlib_surface_create (we own → must destroy)
Wrong ownership caused historical segfaults; the framework encodes the branch explicitly.
-
pcallaround the main draw and mouse handler -
safe_number/safe_parsefallbacks - Missing cache → empty / previous value rather than aborting the whole HUD
Not OS threads inside Conky for daemon scripts; separate shell processes in one process group:
setsid
├── conky -c conky.conf
├── script_a.sh (initial run)
├── (while sleep N; do script_a; done) &
└── … same for other daemon families / custom scripts / album art
| Mechanism | Role |
|---|---|
| setsid | Session leader; Manager can signal the whole group |
PID file under $XDG_RUNTIME_DIR
|
Single-instance lock; re-start replaces old HUD |
| DIR | Theme directory (relocatable) |
.runtime-cache |
Created up front for script/Lua caches |
execi-only themes may have no poller loops — Conky alone is enough.
Studio’s Manager uses detached start on start.sh so themes survive quitting the editor.
| Asset | Behaviour |
|---|---|
| Image paths on visual props | Copied into images/ when resolved at build |
Custom Lua asset_paths
|
Copied into assets/ and images/
|
| Fonts | Listed/copied when the build collects them |
| Script files | Generated or copied into scripts/ with execute bits |
Absolute host paths in the project JSON are a sharing hazard; the built folder is the portable artifact.
| Path | Output location | Runner |
|---|---|---|
| Live Preview | Temp / preview workdir | Studio-controlled Conky process + log |
| Build to Folder | User-chosen directory | Manual ./start.sh
|
| Build & Install | ~/.config/conky/<name>/ |
Manager Start → start.sh |
Same codegen; different destination and process ownership.
- Graph = what to sample and what to draw when using native nodes.
- Custom Lua / Custom Script = optional escape hatches; their text is the behaviour.
- Codegen = how Conky is told to run the graph every frame.
- start.sh = how long-running helpers stay alive beside Conky.
- Caches = async data into a sync draw loop without blocking Cairo.
There is no hidden Studio runtime on the user’s desktop after install — only Conky + shell + Lua.
- Expect one (or few) Custom Lua nodes plus daemon Custom Scripts, not a full native vitals graph.
- Data “works” when scripts run and paths point at
.runtime-cache/assets/. - Wiring a Studio Bar does not change Custom Lua until you edit that Lua to use
in1…in12.