nandi/jolt-nativepublic Fork 0
0fb017593acce485b16f9756141b599beb49dc76
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.

jolttui.h · 219 lines · 9.5 KBC Blame HistoryRaw
Paint the same tree into a terminal, for the machines with no window a7f6202 nandi 17d ago1/*
2 * jolt-tui — glimmer's terminal backend, behind the retained-tree ABI.
3 *
4 * The tree half of `vidya_tree.h` with a terminal under it instead of a GPU
5 * window: nodes are integer handles, mutated by the calls below, and nothing is
6 * drawn until `tui_frame`, which lays the whole tree out and paints it at once.
7 * Interactions come back as a queue the caller drains and routes to its own
8 * handlers — a callback cannot cross this boundary, so identity does instead.
9 *
10 * It is deliberately the same shape as libvidya's, because it is the same
11 * glimmer backend on the other side: one reconciler, and a choice of shared
12 * object. The differences are the ones a terminal actually forces — a focus
13 * ring and keys instead of a pointer and hit testing, cells instead of points,
14 * and colours that are the terminal's rather than a theme's.
15 *
16 * Every call stays on the thread that opened the session, which is where the
17 * state lives; a call from another thread is inert rather than unsound.
18 *
19 * Tags are hiccup names without the colon:
20 *
21 * containers window box hbox vbox frame scroll overlay listbox
22 * widgets label title title-2 dim-label button checkbutton entry
23 * separator spacer progress spinner
24 *
25 * An unrecognized tag is kept and paints as a vertical box, so a caller ahead
26 * of this backend still sees its children.
27 *
28 * Props are string-keyed, in the same names:
29 *
30 * every node sensitive (bool), margin, padding, width-request,
31 * height-request, halign / valign
32 * ("fill"|"start"|"center"|"end"), hexpand / vexpand (bool),
33 * color, bg, bold, dim, underline, reverse, blink, italic
34 * box orientation ("horizontal"|"vertical"), spacing
35 * frame label (drawn into the top border)
36 * scroll offset (rows, clamped to the content and written back)
37 * overlay label; floats centred over everything else
38 * text widgets label, or text
39 * button label, kind ("default"|"primary"|"destructive")
40 * checkbutton label, active (bool)
41 * entry text, placeholder, rows, autofocus (bool)
42 * listbox selected (row index; -1 for no cursor). Its children are
43 * the rows, one node each.
44 * spacer size
45 * progress value (0..1), label
46 *
47 * A colour is a name (`red`, `bright-blue`, `default`), an index into the
48 * xterm 256-colour palette (`"33"`), a hex triple (`#ff6432`, `#f64`), or
49 * `"r,g,b"`. Colour and attributes are inherited by a node's whole subtree.
50 */
51#ifndef JOLTTUI_H
52#define JOLTTUI_H
53
54#if defined(_WIN32)
55# if defined(JOLTTUI_BUILD)
56# define JOLTTUI_API __declspec(dllexport)
57# else
58# define JOLTTUI_API __declspec(dllimport)
59# endif
60#elif defined(__GNUC__)
61# define JOLTTUI_API __attribute__((visibility("default")))
62#else
63# define JOLTTUI_API
64#endif
65
66#ifdef __cplusplus
67extern "C" {
68#endif
69
70/* The session
71 *
72 * `tui_open` takes the terminal: raw mode, the alternate screen, no cursor
73 * unless a focused entry asks for one, and mouse reporting when `mouse` is
74 * non-zero. 1 on success, 0 if a session is already open or the terminal
75 * refused. `tui_close` gives all of it back and is safe to call twice.
76 *
77 * `tui_headless` opens a session of a fixed size with no terminal at all. The
78 * whole widget layer works there — layout, painting, focus, keys fed by hand —
79 * and `tui_screen_line` reads the result back, which is what a test suite and
80 * CI use. It is the same code path a real session paints through.
81 */
82JOLTTUI_API int tui_open(int mouse);
83JOLTTUI_API int tui_headless(int width, int height);
84JOLTTUI_API void tui_close(void);
85
86/* 1 once Ctrl-C, Ctrl-Q or `tui_quit` has asked the loop to stop — and before
87 * a session is open, so a loop written around it cannot spin forever. */
88JOLTTUI_API int tui_should_close(void);
89JOLTTUI_API void tui_quit(void);
90
91/*
92 * One turn of the loop.
93 *
94 * `tui_tick` waits up to `timeout_ms` for input, handles everything that
95 * arrived — keys, clicks, the wheel, a resize — and answers how many things it
96 * handled, so a caller can skip a repaint when nothing did. `tui_frame` lays
97 * the tree out, paints it, and sends only the cells that changed.
98 *
99 * A headless session ticks to 0 and is fed with the calls below instead.
100 */
101JOLTTUI_API int tui_tick(int timeout_ms);
102JOLTTUI_API void tui_frame(void);
103
104/* The screen, in cells. 0 before a session is open. */
105JOLTTUI_API int tui_screen_width(void);
106JOLTTUI_API int tui_screen_height(void);
107
108/*
109 * One painted row as text, trailing blanks trimmed — what a test asserts on
110 * and what a bug report pastes. The returned pointer belongs to the library
111 * and is valid only until the next string-returning read on this thread.
112 */
113JOLTTUI_API const char *tui_screen_line(int y);
114
115/*
116 * Input by hand.
117 *
118 * `tui_feed_key` takes a key by name, as the terminal's own keys are named:
119 * "a", "space", "enter", "tab", "shift+tab", "esc", "up", "page-down",
120 * "ctrl+u", "alt+f", "f5". It answers 1 when the backend acted on it and 0
121 * when it went out as a `key` event instead. `tui_feed_click` and
122 * `tui_feed_wheel` do the same for the mouse; the wheel's `by` is in rows and
123 * negative is up.
124 *
125 * These are the same entry points a real terminal's input arrives through, so
126 * a test drives the UI exactly as a person does.
127 */
128JOLTTUI_API int tui_feed_key(const char *name);
129JOLTTUI_API int tui_feed_click(int x, int y);
130JOLTTUI_API int tui_feed_wheel(int x, int y, int by);
131
132/* The focused node, 0 for none. Focus follows the ring — Tab and Shift-Tab
133 * walk it in paint order, `autofocus` claims it on the first frame, and a
134 * widget that is unmounted or turned insensitive gives it up. */
135JOLTTUI_API int tui_focus(void);
136
137/* The window node, created with the session. Mount everything under it. */
138JOLTTUI_API int tui_tree_root(void);
139
140JOLTTUI_API int tui_node_new(const char *tag);
141JOLTTUI_API void tui_node_free(int node);
142JOLTTUI_API int tui_node_exists(int node);
143
144JOLTTUI_API void tui_node_set_str(int node, const char *key, const char *value);
145JOLTTUI_API void tui_node_set_num(int node, const char *key, double value);
146JOLTTUI_API void tui_node_set_bool(int node, const char *key, int value);
147JOLTTUI_API void tui_node_clear_props(int node);
148
149/*
150 * Reads answer the empty string / 0 for a prop that is unset or of another
151 * type. The returned pointer belongs to the library and is valid only until
152 * the next string-returning call of its family on this thread — props, tags
153 * and screen lines are one family, dumps another, and an event's name and its
154 * text are each their own, so an event can be read whole.
155 */
156JOLTTUI_API const char *tui_node_get_str(int node, const char *key);
157JOLTTUI_API double tui_node_get_num(int node, const char *key);
158JOLTTUI_API int tui_node_get_bool(int node, const char *key);
159
160/* Reading the structure back. `tui_node_tag` answers the canonical tag name
161 * ("box" for both hbox and vbox); `tui_node_child_at` answers 0 past the end. */
162JOLTTUI_API const char *tui_node_tag(int node);
163JOLTTUI_API int tui_node_parent(int node);
164JOLTTUI_API int tui_node_child_count(int node);
165JOLTTUI_API int tui_node_child_at(int node, int index);
166
167/*
168 * The subtree at `node` as pretty-printed hiccup — `[:tag {props} children...]`,
169 * one node to a line. `node` 0 means the root, so `tui_tree_dump(0)` is the
170 * whole window. It answers what the tree *is*, read back from the arena,
171 * rather than what a caller meant to build. Props are sorted, so two dumps of
172 * the same tree compare as text; handlers are absent because they never
173 * crossed this boundary.
174 */
175JOLTTUI_API const char *tui_tree_dump(int node);
176
177JOLTTUI_API int tui_node_append(int parent, int child);
178/* Unparents AND frees `child` with everything under it. */
179JOLTTUI_API void tui_node_remove(int parent, int child);
180/* Moves `child` after `sibling`; `sibling` 0 means the first position. */
181JOLTTUI_API int tui_node_insert_after(int parent, int child, int sibling);
182/* Puts `new_child` where `old_child` was, and frees `old_child`. */
183JOLTTUI_API int tui_node_replace(int parent, int old_child, int new_child);
184
185/*
186 * Drain interactions. `tui_tree_poll_event` dequeues one and answers 1 while
187 * there was one; the accessors describe whichever was dequeued last.
188 *
189 * Event names are glimmer's handler props without the `on-`:
190 *
191 * click a button was pressed no payload
192 * toggled a checkbutton changed num is the new state
193 * change an entry's text changed text is the new text
194 * activate Enter in an entry or a list text is the text or the row
195 * select a list's cursor moved text is the row, num its index
196 * scroll a viewport moved num is the new offset
197 * close Esc in an overlay no payload
198 * key a key nothing here wanted text is the key's name
199 *
200 * A widget does not own its value: `toggled`, `change`, `select` and `scroll`
201 * write the new state back into the node's props as well, so a caller that
202 * ignores the event still sees a working control, and the next prop write is
203 * what settles it.
204 *
205 * A `key` event is reported on the focused node, or on the window when nothing
206 * has focus. Bubbling it to a container's `:on-key` is the caller's to do: it
207 * holds the handlers and knows the tree.
208 */
209JOLTTUI_API int tui_tree_poll_event(void);
210JOLTTUI_API int tui_tree_event_node(void);
211JOLTTUI_API const char *tui_tree_event_name(void);
212JOLTTUI_API const char *tui_tree_event_text(void);
213JOLTTUI_API double tui_tree_event_num(void);
214
215#ifdef __cplusplus
216}
217#endif
218
219#endif /* JOLTTUI_H */