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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
(ns glimmer-tui.ffi
"Raw bindings for libjolttui's retained-tree ABI
(`crates/jolt-tui/include/jolttui.h`).
Nothing here interprets a prop or an event; that is `glimmer-tui.core`'s job.
This namespace exists so the boundary is one readable list of symbols, and so
the marshalling rules that come with it are stated once.
**Strings returned by this library are borrowed.** They come out of scratch
buffers the next call of the same family overwrites — props, tags and screen
lines share one, dumps have their own, and an event's name and its text have
one each, so an event can be read whole. Jolt copies a `:string` return into a
Scheme string as it crosses, so holding the value is safe.
**Every call belongs to the thread that opened the session.** The library
keeps its state in thread-local storage, so a call from anywhere else is inert
rather than unsound — still a bug. `glimmer-tui.core` routes off-thread work
through glimmer's `schedule`."
(:require [jolt.ffi :as ffi]))
;; --- the session -------------------------------------------------------------
;; Two ways in, one code path out of them: `raw-open` takes the terminal, and
;; `raw-headless` opens a session of a fixed size with no terminal at all —
;; same layout, same painting, same focus ring, read back with `screen-line`.
(ffi/defcfn raw-open "tui_open" [:int] :int)
(ffi/defcfn raw-headless "tui_headless" [:int :int] :int)
(ffi/defcfn close! "tui_close" [] :void)
(ffi/defcfn raw-should-close "tui_should_close" [] :int)
(ffi/defcfn quit-loop! "tui_quit" [] :void)
;; --- the loop ----------------------------------------------------------------
;; `tick` waits up to timeout_ms for input and answers how much it handled;
;; `frame!` lays the tree out, paints it and sends only the cells that changed.
(ffi/defcfn tick "tui_tick" [:int] :int)
(ffi/defcfn frame! "tui_frame" [] :void)
;; --- the screen, in cells ----------------------------------------------------
(ffi/defcfn screen-width "tui_screen_width" [] :int)
(ffi/defcfn screen-height "tui_screen_height" [] :int)
(ffi/defcfn screen-line "tui_screen_line" [:int] :string)
;; --- input by hand -----------------------------------------------------------
;; The same entry points a real terminal's input arrives through, so a test
;; drives the UI exactly as a person does.
(ffi/defcfn raw-feed-key "tui_feed_key" [:string] :int)
(ffi/defcfn raw-feed-click "tui_feed_click" [:int :int] :int)
(ffi/defcfn raw-feed-wheel "tui_feed_wheel" [:int :int :int] :int)
(ffi/defcfn focus "tui_focus" [] :int)
;; --- the tree ----------------------------------------------------------------
(ffi/defcfn tree-root "tui_tree_root" [] :int)
(ffi/defcfn node-new "tui_node_new" [:string] :int)
(ffi/defcfn node-free! "tui_node_free" [:int] :void)
(ffi/defcfn raw-node-exists "tui_node_exists" [:int] :int)
(ffi/defcfn node-set-str! "tui_node_set_str" [:int :string :string] :void)
(ffi/defcfn node-set-num! "tui_node_set_num" [:int :string :double] :void)
(ffi/defcfn raw-node-set-bool "tui_node_set_bool" [:int :string :int] :void)
(ffi/defcfn node-clear-props! "tui_node_clear_props" [:int] :void)
(ffi/defcfn node-get-str "tui_node_get_str" [:int :string] :string)
(ffi/defcfn node-get-num "tui_node_get_num" [:int :string] :double)
(ffi/defcfn raw-node-get-bool "tui_node_get_bool" [:int :string] :int)
(ffi/defcfn node-tag "tui_node_tag" [:int] :string)
(ffi/defcfn node-parent "tui_node_parent" [:int] :int)
(ffi/defcfn node-child-count "tui_node_child_count" [:int] :int)
(ffi/defcfn node-child-at "tui_node_child_at" [:int :int] :int)
(ffi/defcfn tree-dump "tui_tree_dump" [:int] :string)
(ffi/defcfn raw-node-append "tui_node_append" [:int :int] :int)
(ffi/defcfn node-remove! "tui_node_remove" [:int :int] :void)
(ffi/defcfn raw-node-insert-after "tui_node_insert_after" [:int :int :int] :int)
(ffi/defcfn raw-node-replace "tui_node_replace" [:int :int :int] :int)
;; --- events ------------------------------------------------------------------
(ffi/defcfn raw-poll-event "tui_tree_poll_event" [] :int)
(ffi/defcfn event-node "tui_tree_event_node" [] :int)
(ffi/defcfn event-name "tui_tree_event_name" [] :string)
(ffi/defcfn event-text "tui_tree_event_text" [] :string)
(ffi/defcfn event-num "tui_tree_event_num" [] :double)
;; --- the int/bool seam -------------------------------------------------------
;; C has no booleans; every predicate here crosses as 0 or 1. Converting at the
;; binding rather than at each call site keeps the rest of the backend written
;; in jolt's own truthiness.
(defn open!
"Take the terminal: raw mode, the alternate screen, the cursor hidden unless a
focused entry asks for it, and mouse reporting when `mouse?`."
[mouse?]
(not (zero? (raw-open (if mouse? 1 0)))))
(defn headless!
"Open a session of `width` by `height` cells with no terminal at all."
[width height]
(not (zero? (raw-headless width height))))
(defn should-close? [] (not (zero? (raw-should-close))))
(defn node-exists? [node] (not (zero? (raw-node-exists node))))
(defn node-set-bool! [node key value] (raw-node-set-bool node key (if value 1 0)))
(defn node-get-bool [node key] (not (zero? (raw-node-get-bool node key))))
(defn node-append! [parent child] (not (zero? (raw-node-append parent child))))
(defn node-insert-after! [parent child sibling]
(not (zero? (raw-node-insert-after parent child sibling))))
(defn node-replace! [parent old new] (not (zero? (raw-node-replace parent old new))))
(defn poll-event! [] (not (zero? (raw-poll-event))))
(defn feed-key!
"Type one key, named as the terminal names it. True when the backend acted on
it, false when it went out as a `key` event instead."
[name]
(not (zero? (raw-feed-key name))))
(defn feed-click! [x y] (not (zero? (raw-feed-click x y))))
(defn feed-wheel! [x y by] (not (zero? (raw-feed-wheel x y by))))
|