| Paint the tree ABI a third way: Zig and dvui 7289263 nandi 9d ago | 1 | # jolt-zvui |
| 2 | |
| 3 | The retained-tree C ABI, in Zig, painted by [dvui](https://github.com/david-vanderson/dvui). |
| 4 | |
| 5 | `crates/jolt-vidya` exports this shape on egui and `crates/jolt-tui` exports it |
| 6 | again over terminal cells. This is a third painter under the same shape: a tree |
| 7 | the caller mutates between frames, one call that walks it, and a queue of what |
| 8 | the person did. Nothing in the tree knows what paints it, which is the property |
| 9 | that makes a third backend possible at all. |
| 10 | |
| 11 | The prefix is `zvui_` and the object is `libjoltzvui.so`, for the same reason |
| 12 | jolt-tui's are `tui_` and `libjolttui.so`: a consumer names one object in |
| 13 | `:jolt/native` and binds one set of symbols. |
| 14 | |
| 15 | ``` |
| 16 | src/tree.zig nodes, props, children, the event queue — no dvui in it |
| 17 | src/paint.zig one walk per frame, in dvui widgets |
| 18 | src/lib.zig the 32 exported symbols |
| 19 | include/zvui.h the header a C caller (or a jolt.ffi/defcfn) reads |
| 20 | demo.c a caller: builds a tree, paints it, drains events |
| 21 | ``` |
| 22 | |
| 23 | ## Building |
| 24 | |
| 25 | Zig 0.16 and nothing else installed — dvui, SDL3, FreeType and tree-sitter are |
| 26 | fetched and built by the Zig package manager, at the revs `build.zig.zon` pins. |
| 27 | |
| 28 | ```bash |
| 29 | zig build --release # zig-out/lib/libjoltzvui.so |
| 30 | zig build test # the tree, without a window |
| 31 | ``` |
| 32 | |
| 33 | `--release` rather than `-Doptimize=`: this library sets a preferred optimize |
| 34 | mode (ReleaseSafe), and Zig spells that choice `--release`. |
| 35 | |
| 36 | ## The demo |
| 37 | |
| 38 | ```bash |
| 39 | gcc demo.c ./zig-out/lib/libjoltzvui.so -Wl,-rpath,'$ORIGIN/zig-out/lib' -o demo |
| 40 | ./demo # a window |
| 41 | ./demo --dump # the tree on stdout, no window |
| 42 | ./demo --frames 3 # paint three frames and leave |
| 43 | ``` |
| 44 | |
| 45 | ## The vocabulary |
| 46 | |
| 47 | Tags: `window` `box` `hbox` `vbox` `page` `card` `frame` `scroll` `label` |
| 48 | `title` `dim-label` `button` `checkbox` `entry` `separator` `spacer`/`gap` |
| 49 | `progress`. An unknown tag is a container rather than an error, so a tree |
| 50 | written against a richer painter still shows its contents here. |
| 51 | |
| 52 | Props: `text` `label` `orientation` `spacing` `margin` `align` `max-width` |
| 53 | `width-request` `placeholder` `value` `active` `sensitive` `kind` `multiline` |
| 54 | `size` `fill-height`. |
| 55 | |
| 56 | Events: `click` `change` `toggled` `activate`. |
| 57 | |
| 58 | ## Two things worth knowing |
| 59 | |
| 60 | **Ids come back; identities do not.** The free list hands a node id out again |
| 61 | after a free — but widget identity is `(generation, index)`, so a node freed and |
| 62 | reallocated in the same frame does not inherit the cursor, scroll position or |
| 63 | animation of the node that used to live in that slot. That is the bug class |
| 64 | frq's `deps.edn` describes from the reconciler's side, closed here at the |
| 65 | backend rather than only upstream. |
| 66 | |
| 67 | **Zig cannot keep jolt-abi's unwinding rule.** There is no `catch_unwind`, so |
| 68 | `jolt_abi::guard`'s bargain — a panicking decoder is a black tile rather than a |
| 69 | dead process — is not available. A Zig panic aborts. The answer here is to not |
| 70 | panic: every entry point is total, a bad node id is a miss rather than an index, |
| 71 | and allocation failure answers the fallback a caught panic would have. The build |
| 72 | is ReleaseSafe by default for the same reason — a trap inside this library beats |
| 73 | quiet corruption of the caller's heap. |
| 74 | |
| 75 | The other two rules are kept exactly. Strings out live in one scratch slot until |
| 76 | the next string-returning call, and nothing here calls back: events are queued |
| 77 | and polled. |
| 78 | |
| 79 | ## Not done |
| 80 | |
| 81 | No `page`/`card` push-pop immediate API (the tree is the only interface), no |
| 82 | images, avatars, video frames, clipboard, file picking, font loading, or Android |
| 83 | entry points. `jolt-vidya` exports 57 symbols; this exports 32. |