nandi/jolt-nativepublic Fork 0
d5dfd53e28ca47b0a4fdb550d5225ffa10aff7cf
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Paint the tree ABI a third way: Zig and dvui 7289263 · on d5dfd53e28ca47b0a4fdb550d5225ffa10aff7cf · nandi · 9d ago
README.md · 83 lines · 3.5 KBmarkdown
Blame HistoryOpen raw

jolt-zvui

The retained-tree C ABI, in Zig, painted by dvui.

crates/jolt-vidya exports this shape on egui and crates/jolt-tui exports it
again over terminal cells. This is a third painter under the same shape: a tree
the caller mutates between frames, one call that walks it, and a queue of what
the person did. Nothing in the tree knows what paints it, which is the property
that makes a third backend possible at all.

The prefix is zvui_ and the object is libjoltzvui.so, for the same reason
jolt-tui's are tui_ and libjolttui.so: a consumer names one object in
:jolt/native and binds one set of symbols.

src/tree.zig   nodes, props, children, the event queue — no dvui in it
src/paint.zig  one walk per frame, in dvui widgets
src/lib.zig    the 32 exported symbols
include/zvui.h the header a C caller (or a jolt.ffi/defcfn) reads
demo.c         a caller: builds a tree, paints it, drains events

Building

Zig 0.16 and nothing else installed — dvui, SDL3, FreeType and tree-sitter are
fetched and built by the Zig package manager, at the revs build.zig.zon pins.

zig build --release       # zig-out/lib/libjoltzvui.so
zig build test            # the tree, without a window

--release rather than -Doptimize=: this library sets a preferred optimize
mode (ReleaseSafe), and Zig spells that choice --release.

The demo

gcc demo.c ./zig-out/lib/libjoltzvui.so -Wl,-rpath,'$ORIGIN/zig-out/lib' -o demo
./demo                    # a window
./demo --dump             # the tree on stdout, no window
./demo --frames 3         # paint three frames and leave

The vocabulary

Tags: window box hbox vbox page card frame scroll label
title dim-label button checkbox entry separator spacer/gap
progress. An unknown tag is a container rather than an error, so a tree
written against a richer painter still shows its contents here.

Props: text label orientation spacing margin align max-width
width-request placeholder value active sensitive kind multiline
size fill-height.

Events: click change toggled activate.

Two things worth knowing

Ids come back; identities do not. The free list hands a node id out again
after a free — but widget identity is (generation, index), so a node freed and
reallocated in the same frame does not inherit the cursor, scroll position or
animation of the node that used to live in that slot. That is the bug class
frq's deps.edn describes from the reconciler's side, closed here at the
backend rather than only upstream.

Zig cannot keep jolt-abi's unwinding rule. There is no catch_unwind, so
jolt_abi::guard's bargain — a panicking decoder is a black tile rather than a
dead process — is not available. A Zig panic aborts. The answer here is to not
panic: every entry point is total, a bad node id is a miss rather than an index,
and allocation failure answers the fallback a caught panic would have. The build
is ReleaseSafe by default for the same reason — a trap inside this library beats
quiet corruption of the caller's heap.

The other two rules are kept exactly. Strings out live in one scratch slot until
the next string-returning call, and nothing here calls back: events are queued
and polled.

Not done

No page/card push-pop immediate API (the tree is the only interface), no
images, avatars, video frames, clipboard, file picking, font loading, or Android
entry points. jolt-vidya exports 57 symbols; this exports 32.

 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
# jolt-zvui

The retained-tree C ABI, in Zig, painted by [dvui](https://github.com/david-vanderson/dvui).

`crates/jolt-vidya` exports this shape on egui and `crates/jolt-tui` exports it
again over terminal cells. This is a third painter under the same shape: a tree
the caller mutates between frames, one call that walks it, and a queue of what
the person did. Nothing in the tree knows what paints it, which is the property
that makes a third backend possible at all.

The prefix is `zvui_` and the object is `libjoltzvui.so`, for the same reason
jolt-tui's are `tui_` and `libjolttui.so`: a consumer names one object in
`:jolt/native` and binds one set of symbols.

```
src/tree.zig   nodes, props, children, the event queue — no dvui in it
src/paint.zig  one walk per frame, in dvui widgets
src/lib.zig    the 32 exported symbols
include/zvui.h the header a C caller (or a jolt.ffi/defcfn) reads
demo.c         a caller: builds a tree, paints it, drains events
```

## Building

Zig 0.16 and nothing else installed — dvui, SDL3, FreeType and tree-sitter are
fetched and built by the Zig package manager, at the revs `build.zig.zon` pins.

```bash
zig build --release       # zig-out/lib/libjoltzvui.so
zig build test            # the tree, without a window
```

`--release` rather than `-Doptimize=`: this library sets a preferred optimize
mode (ReleaseSafe), and Zig spells that choice `--release`.

## The demo

```bash
gcc demo.c ./zig-out/lib/libjoltzvui.so -Wl,-rpath,'$ORIGIN/zig-out/lib' -o demo
./demo                    # a window
./demo --dump             # the tree on stdout, no window
./demo --frames 3         # paint three frames and leave
```

## The vocabulary

Tags: `window` `box` `hbox` `vbox` `page` `card` `frame` `scroll` `label`
`title` `dim-label` `button` `checkbox` `entry` `separator` `spacer`/`gap`
`progress`. An unknown tag is a container rather than an error, so a tree
written against a richer painter still shows its contents here.

Props: `text` `label` `orientation` `spacing` `margin` `align` `max-width`
`width-request` `placeholder` `value` `active` `sensitive` `kind` `multiline`
`size` `fill-height`.

Events: `click` `change` `toggled` `activate`.

## Two things worth knowing

**Ids come back; identities do not.** The free list hands a node id out again
after a free — but widget identity is `(generation, index)`, so a node freed and
reallocated in the same frame does not inherit the cursor, scroll position or
animation of the node that used to live in that slot. That is the bug class
frq's `deps.edn` describes from the reconciler's side, closed here at the
backend rather than only upstream.

**Zig cannot keep jolt-abi's unwinding rule.** There is no `catch_unwind`, so
`jolt_abi::guard`'s bargain — a panicking decoder is a black tile rather than a
dead process — is not available. A Zig panic aborts. The answer here is to not
panic: every entry point is total, a bad node id is a miss rather than an index,
and allocation failure answers the fallback a caught panic would have. The build
is ReleaseSafe by default for the same reason — a trap inside this library beats
quiet corruption of the caller's heap.

The other two rules are kept exactly. Strings out live in one scratch slot until
the next string-returning call, and nothing here calls back: events are queued
and polled.

## Not done

No `page`/`card` push-pop immediate API (the tree is the only interface), no
images, avatars, video frames, clipboard, file picking, font loading, or Android
entry points. `jolt-vidya` exports 57 symbols; this exports 32.