Skip to main content

kui_ffi/
types.rs

1//! Opaque handles and the repr(C) mirrors of the core's spec structs —
2//! what `include/kui.h` declares, field for field. See the ABI notes in
3//! `abi` for which side may write which struct.
4
5use super::*;
6
7/// A C callback that builds into a context: `kui_run`'s view, and the body
8/// of a `kui_titlebar_with` / `kui_tooltip_with`. Here rather than in
9/// `run`, because the widgets take one whether or not this build has a
10/// windowed runner.
11pub(crate) type ViewFn = extern "C" fn(user: *mut c_void, ctx: *mut KuiCtx);
12/// A C callback that receives one event: `kui_run`'s, and only its — so
13/// unlike `ViewFn` it is not here in a build without the runner.
14#[cfg(feature = "runner")]
15pub(crate) type EventFn = extern "C" fn(user: *mut c_void, ev: *const KuiEvent);
16/// A C callback that hears the window go: `kui_on_teardown`'s, with
17/// `kui_run`'s `user`. Runner-only, as `EventFn` is.
18#[cfg(feature = "runner")]
19pub(crate) type TeardownFn = extern "C" fn(user: *mut c_void);
20
21// ---------------------------------------------------------------------------
22// Opaque + repr(C) types
23
24/// The opaque context every `kui_*` call takes: a core plus its pending
25/// event queue.
26///
27/// A standalone context from [`kui_ctx_new`] owns its core and lives until
28/// [`kui_ctx_free`]. The context handed to a `kui_run` view callback or a
29/// plugin's `kui_ext_view` borrows the runner's frame instead and is valid
30/// for that call only; the builder entry points work the same on both,
31/// while input, polling and draw data belong to the owning side.
32pub struct KuiCtx {
33    pub(crate) core: *mut Core,
34    /// Keep-alive for standalone contexts; never read directly.
35    pub(crate) _owned: Option<Box<Core>>,
36    pub(crate) events: Vec<UiEvent>,
37    /// Payload most recently handed out by kui_poll_event; freed on the next
38    /// poll (or context free) so C never manages event payload lifetime.
39    pub(crate) last_payload: Option<Box<KuiValue>>,
40    /// Text most recently handed out by kui_edit_text; freed on the next call.
41    pub(crate) last_edit_text: Option<String>,
42    /// The WGSL module most recently handed out by `kui_fragment_source`;
43    /// valid until the next call, like every other borrowed string here.
44    pub(crate) fragment_source: String,
45    /// This frame's fragment draws, in `KuiFragmentDraw` form, so
46    /// `kui_draw_data` can hand out a pointer that outlives the call.
47    pub(crate) fragment_draws: Vec<KuiFragmentDraw>,
48    /// `kui_draw_data`'s transcription of the frame's texture draws.
49    pub(crate) texture_draws: Vec<KuiTextureDraw>,
50    /// Whether the two above are this frame's: `kui_draw_data` transcribes
51    /// once a frame, so a second call in the same frame hands out the same
52    /// arrays rather than freeing the ones the first call handed out.
53    /// Cleared by `kui_frame_begin`, which is where kui.h ends them, and
54    /// by `kui_frame_finish`, so a call while the frame built does not
55    /// stand for the frame it finished.
56    pub(crate) draws_current: bool,
57    /// What `kui_image_pixels` last handed out, so the pointer outlives
58    /// the call.
59    pub(crate) image_pixels: Option<std::sync::Arc<Vec<u8>>>,
60    /// Warnings most recently handed out by kui_take_warnings; their strings
61    /// stay valid until the next call.
62    pub(crate) last_warnings: Vec<kui_core::Warning>,
63    /// Warnings drained from the core and not handed out yet: a `cap`
64    /// shorter than what was raised leaves the rest here.
65    pub(crate) pending_warnings: Vec<kui_core::Warning>,
66    /// Access tree most recently handed out by kui_access_tree; its strings
67    /// stay valid until the next call.
68    pub(crate) last_access: kui_core::AccessTree,
69    /// The runs most recently handed out by kui_access_runs, held apart
70    /// from `last_access` so reading an editor's runs leaves the strings
71    /// kui_access_tree handed out alone.
72    pub(crate) last_runs: Vec<kui_core::AccessRun>,
73    /// Announcements most recently handed out by kui_take_announcements;
74    /// their strings stay valid until the next call.
75    pub(crate) last_announcements: Vec<kui_core::Announcement>,
76    /// Window commands taken from the core and not yet handed out one at a
77    /// time by `kui_take_window_command`.
78    pub(crate) window_commands: VecDeque<WindowCommand>,
79    /// The same for the menu actions `kui_take_menu_action` hands out, and
80    /// the text of the one most recently handed out — borrowed by the
81    /// caller until the next call, like every other string here.
82    pub(crate) menu_actions: VecDeque<kui_core::MenuAction>,
83    pub(crate) menu_text: String,
84    pub(crate) menu_html: String,
85    /// The label most recently read back by `kui_menu_bar_menu`,
86    /// `kui_menu_bar_item` or `kui_menu_item`, and the accelerator beside
87    /// it: their own, so reading a menu leaves a taken action's `text`
88    /// alone, as kui.h says it is (borrowed until the next
89    /// `kui_take_menu_action`).
90    pub(crate) row_text: String,
91    pub(crate) menu_accel: String,
92    /// What `kui_request_copy` most recently handed out, on the same terms.
93    pub(crate) copy_text: String,
94    /// The chord most recently handed out by `kui_devtools_key`, on the
95    /// same terms.
96    pub(crate) devtools_key: String,
97    /// The file dialog `kui_take_file_request` most recently handed out,
98    /// whose strings — and the filter `kui_file_request_filter` read,
99    /// its extensions joined — are borrowed until the next call.
100    pub(crate) file_request: Option<kui_core::FileDialog>,
101    pub(crate) file_filter_text: String,
102    /// The tab name most recently handed out by `kui_devtools_current_tab`,
103    /// on the same terms.
104    pub(crate) devtools_tab: String,
105    /// The selection most recently handed out by `kui_selection_text` /
106    /// `kui_selection_html`; valid until the next such call, like every
107    /// other borrowed string here.
108    pub(crate) selection_text: String,
109    pub(crate) selection_html: String,
110    /// The family names most recently handed out by `kui_font_families`,
111    /// held so the `KuiStr`s written into the host's array stay valid
112    /// until the next call.
113    pub(crate) font_families: Vec<String>,
114    /// The families most recently handed out by `kui_system_fonts`, held
115    /// for the same reason: their names and weights are what the
116    /// `KuiSystemFont`s written into the host's array point at.
117    pub(crate) system_fonts: Vec<kui_core::SystemFont>,
118    /// The node list most recently handed out by `kui_nodes`; borrowed
119    /// until the next call, like every other reading here.
120    pub(crate) nodes: Option<Box<KuiValue>>,
121    /// The name most recently handed out by kui_ctx_window_name; valid
122    /// until the next call.
123    pub(crate) last_window_name: Option<Rc<str>>,
124    /// Which slot this context is a C extension's fill of, and the params
125    /// the host passed it: what `kui_slot_name` / `kui_slot_params`
126    /// answer. `None` on every other context - a standalone one, a C host's
127    /// view callback - where they answer false and NULL.
128    pub(crate) slot_name: Option<String>,
129    pub(crate) slot_namespace: Option<String>,
130    pub(crate) slot_params: Option<KuiValue>,
131    /// The extensions this context hosts, in origin order.
132    /// `kui_ctx_add_extension` fills it, `kui_slot` fills *them* in place,
133    /// `kui_frame_finish` lets them take `ns/root` and warn about slots
134    /// nobody declared, and an event whose origin names one is delivered to
135    /// it rather than queued for the host. `kui_run_with` moves it into
136    /// the window's runner. Empty on a borrowing context: an extension
137    /// does not host extensions, and a host's under the runner is the
138    /// runner's.
139    pub(crate) extensions: kui_core::Extensions,
140    /// Why the last `kui_ctx_add_extension` said false. Valid until the
141    /// next call, like every other borrowed string here.
142    pub(crate) last_ext_error: String,
143    /// Set only for the length of a `kui_run_with` view callback: the
144    /// runner's own `Ui`, erased to a pointer because `KuiCtx` is what C
145    /// holds and has no lifetime to carry one.
146    ///
147    /// It exists because under the windowed runner the extension list is
148    /// the *runner's*, not this context's — `kui_native::Launcher` owns it, and
149    /// the `Ui` it built the frame with is what knows how to fill a slot.
150    /// So `kui_slot` hands the declaration to that `Ui` when this is set,
151    /// and fills from `extensions` when it is not. Null on every other
152    /// context: a standalone one, and an extension's own.
153    pub(crate) host_ui: *mut c_void,
154}
155
156impl KuiCtx {
157    pub(crate) fn core(&mut self) -> &mut Core {
158        unsafe { &mut *self.core }
159    }
160
161    /// Takes the core a standalone context owns, for `kui_run_with` to
162    /// open the window on, and leaves the context a fresh
163    /// one so that it stays a context — still the caller's to free, and
164    /// to use, as one that has registered nothing. `None` on a borrowing
165    /// context, whose core is someone else's frame.
166    #[cfg(any(feature = "runner", test))]
167    pub(crate) fn take_core(&mut self) -> Option<Box<Core>> {
168        let taken = self._owned.take()?;
169        let mut fresh = Box::new(Core::new());
170        fresh.set_diagnostics(false);
171        self.core = &mut *fresh;
172        self._owned = Some(fresh);
173        Some(taken)
174    }
175
176    /// A context that borrows someone else's frame instead of owning a
177    /// `Core`: what `kui_run`'s view callback and a C extension's both get.
178    /// Only the builder entry points are meaningful on one - the queues
179    /// below stay empty, because the runner owns event delivery.
180    ///
181    /// The borrow is not in the type (`KuiCtx` is what C holds, and it has
182    /// no lifetime), so the caller keeps it: use the context inside the
183    /// scope this `&mut Core` came from and let it go at the end of it.
184    pub(crate) fn borrowing(core: &mut Core) -> Self {
185        Self {
186            core,
187            _owned: None,
188            events: Vec::new(),
189            last_payload: None,
190            last_edit_text: None,
191            fragment_source: String::new(),
192            fragment_draws: Vec::new(),
193            texture_draws: Vec::new(),
194            draws_current: false,
195            image_pixels: None,
196            last_warnings: Vec::new(),
197            pending_warnings: Vec::new(),
198            last_access: Default::default(),
199            last_runs: Vec::new(),
200            last_announcements: Vec::new(),
201            window_commands: VecDeque::new(),
202            menu_actions: VecDeque::new(),
203            menu_text: String::new(),
204            row_text: String::new(),
205            menu_accel: String::new(),
206            copy_text: String::new(),
207            devtools_key: String::new(),
208            file_request: None,
209            file_filter_text: String::new(),
210            devtools_tab: String::new(),
211            menu_html: String::new(),
212            selection_text: String::new(),
213            selection_html: String::new(),
214            font_families: Vec::new(),
215            system_fonts: Vec::new(),
216            nodes: None,
217            last_window_name: None,
218            slot_name: None,
219            slot_namespace: None,
220            slot_params: None,
221            extensions: Default::default(),
222            last_ext_error: String::new(),
223            host_ui: std::ptr::null_mut(),
224        }
225    }
226
227    /// [`Self::borrowing`] for the windowed runner's view callback, which
228    /// has a whole `Ui` rather than a bare `Core`: same context, plus the
229    /// pointer `kui_slot` needs to reach the runner's extensions. Valid
230    /// for the callback and not one instruction longer, like the borrow
231    /// itself.
232    pub(crate) fn borrowing_in(ui: &mut kui_core::Ui<'_>) -> Self {
233        // `borrowing` keeps the core as a raw pointer, so the borrow of
234        // `ui` it takes ends when it returns and the `Ui` can be kept too.
235        let mut this = Self::borrowing(ui.core());
236        this.host_ui = std::ptr::from_mut(ui).cast();
237        this
238    }
239
240    /// Takes a batch of events the core just produced: the host's own are
241    /// queued for `kui_poll_event`, and one whose origin names a loaded
242    /// extension is delivered to it instead, its replies queued in its
243    /// place. `Extensions::route` is the walk, the
244    /// same one the Rust runner's `route_events` takes.
245    pub(crate) fn absorb(&mut self, events: impl IntoIterator<Item = UiEvent>) {
246        let out = &mut self.events;
247        self.extensions.route(events, |ev| out.push(ev));
248    }
249
250    /// Records a just-opened node's hover hint: the core floats it on
251    /// `kui_close` while the node is hovered (`Core::hint`).
252    pub(crate) fn push_tooltip(&mut self, key: kui_core::Key, hint: KuiStr) {
253        if let Some(h) = opt_str(hint) {
254            self.core().hint(key, h.into_owned());
255        }
256    }
257}
258
259/// An opaque dynamic value: null, bool, int, float, string, list or map.
260///
261/// Payloads a host attaches to nodes (`on_click` and the other tags) and
262/// the payloads events carry back are values. Build one with
263/// `kui_value_*` and read one with [`kui_value_get`], [`kui_value_at`],
264/// [`kui_value_as_str`] and friends. One you built is yours until a call
265/// documented as consuming it takes it; otherwise free it with
266/// [`kui_value_free`]. One the library hands out is borrowed.
267#[repr(transparent)]
268pub struct KuiValue(pub(crate) Value);
269
270/// A borrowed string: `len` bytes of UTF-8 at `ptr`, not NUL-terminated.
271///
272/// `KUI_STR("literal")` makes one in C and `kui_str_eq` compares one to a
273/// C string. A string the library hands out points into memory it owns and
274/// is valid until the next call of the same function on that context.
275/// Invalid UTF-8 going in is replaced. The layout is frozen.
276#[repr(C)]
277#[derive(Clone, Copy)]
278pub struct KuiStr {
279    pub ptr: *const u8,
280    pub len: usize,
281}
282
283/// One axis of a node's size: a `KUI_*` tag and its value.
284#[repr(C)]
285#[derive(Clone, Copy)]
286pub struct KuiSizing {
287    /// `KUI_FIT` (0), `KUI_GROW` (1, `value` the weight), `KUI_FIXED` (2,
288    /// logical px), `KUI_PERCENT` (3, `value` 0..1) or `KUI_CALC` (4,
289    /// `value` an expression number from `kui_size_*`).
290    pub tag: u32,
291    pub value: f32,
292}
293
294/// `KuiSpec.min_w` / `min_h` as the node's own fit size (`KUI_MIN_FIT`).
295pub const KUI_MIN_FIT: f32 = -1.0;
296/// `KuiSpec.min_w` / `min_h` as a declared floor of 0: no floor at all,
297/// not even the content's that a share in an overflowing row gets for an
298/// undeclared (0) one; CSS's `min-width: 0`.
299pub const KUI_MIN_NONE: f32 = -2.0;
300/// Which of a `KuiKeyframe`'s fields are set (its `set` bits).
301pub const KUI_KF_AT: u32 = 1 << 0;
302pub const KUI_KF_WIDTH: u32 = 1 << 1;
303pub const KUI_KF_HEIGHT: u32 = 1 << 2;
304pub const KUI_KF_BG: u32 = 1 << 3;
305pub const KUI_KF_RADIUS: u32 = 1 << 4;
306pub const KUI_KF_OPACITY: u32 = 1 << 5;
307pub const KUI_KF_ROTATE: u32 = 1 << 6;
308pub const KUI_KF_SCALE: u32 = 1 << 7;
309pub const KUI_KF_OFFSET: u32 = 1 << 8;
310
311/// One keyframe stop (`KuiSpec.keyframes`): a zeroed stop sets nothing.
312/// `set` says which fields count, so 0 stays a legal value for each.
313#[repr(C)]
314#[derive(Clone, Copy)]
315pub struct KuiKeyframe {
316    pub set: u32,
317    /// 0..1 (KUI_KF_AT); unset stops spread evenly, a lone one sits at 1.
318    pub at: f32,
319    pub width: KuiSizing,
320    pub height: KuiSizing,
321    /// 0xRRGGBBAA
322    pub bg: u32,
323    pub radius: f32,
324    /// Group opacity 0..1 (KUI_KF_OPACITY).
325    pub opacity: f32,
326    /// A turn in turns clockwise (KUI_KF_ROTATE), ADR 0043. ABI 27.
327    pub rotate: f32,
328    /// A uniform scale about the node's pivot (KUI_KF_SCALE). ABI 27.
329    pub scale: f32,
330    /// Logical px from where layout put the node (KUI_KF_OFFSET, both
331    /// together; backlog F132). ABI 27.
332    pub dx: f32,
333    pub dy: f32,
334}
335
336/// `KuiGradient.kind`: along a line, or out from a centre.
337pub const KUI_GRADIENT_LINEAR: u32 = 0;
338pub const KUI_GRADIENT_RADIAL: u32 = 1;
339
340/// One stop of a `KuiGradient`: a colour and where along the gradient
341/// it sits, 0..1 — or a negative `at` for a stop spaced evenly between
342/// its neighbours that have one.
343#[repr(C)]
344#[derive(Clone, Copy)]
345pub struct KuiGradientStop {
346    /// 0xRRGGBBAA
347    pub color: u32,
348    pub at: f32,
349}
350
351/// A box's gradient (`KuiSpec.gradient`,
352/// `docs/adr/0042-a-gradient-is-an-image-the-core-paints.md`): linear
353/// along `angle` — turns clockwise from east, in the box's unit square —
354/// or radial out from (`at_x`, `at_y`), fractions of the box, to its
355/// farthest corner. `stops_len` stops, two or more.
356#[repr(C)]
357#[derive(Clone, Copy)]
358pub struct KuiGradient {
359    /// `KUI_GRADIENT_*`.
360    pub kind: u32,
361    pub angle: f32,
362    pub at_x: f32,
363    pub at_y: f32,
364    pub stops: *const KuiGradientStop,
365    pub stops_len: usize,
366}
367
368/// Which of a `KuiEnter`'s fields are set (its `set` bits); 0 = no entrance.
369/// `KuiSpec.float_mode`: in flow, or which rect the float attaches to. The
370/// non-zero values are `kui_core::FLOAT_PRESETS` indices plus one, so zero
371/// can still mean "no float".
372pub const KUI_FLOAT_NONE: u32 = 0;
373pub const KUI_FLOAT_PARENT: u32 = 1;
374pub const KUI_FLOAT_VIEWPORT: u32 = 2;
375
376pub const KUI_ENTER_OFFSET: u32 = 1 << 0;
377pub const KUI_ENTER_WIDTH: u32 = 1 << 1;
378pub const KUI_ENTER_HEIGHT: u32 = 1 << 2;
379pub const KUI_ENTER_BG: u32 = 1 << 3;
380pub const KUI_ENTER_RADIUS: u32 = 1 << 4;
381pub const KUI_ENTER_OPACITY: u32 = 1 << 5;
382pub const KUI_ENTER_ROTATE: u32 = 1 << 6;
383pub const KUI_ENTER_SCALE: u32 = 1 << 7;
384
385/// Where a node starts the first frame it is seen (`KuiSpec.enter`): the
386/// slots `set` names ease in from these values over `transition_ms`
387/// instead of snapping. A zeroed struct is no entrance.
388#[repr(C)]
389#[derive(Clone, Copy)]
390pub struct KuiEnter {
391    pub set: u32,
392    /// Logical px the node slides in from (KUI_ENTER_OFFSET).
393    pub dx: f32,
394    pub dy: f32,
395    pub width: KuiSizing,
396    pub height: KuiSizing,
397    /// 0xRRGGBBAA
398    pub bg: u32,
399    pub radius: f32,
400    /// Group opacity 0..1 (KUI_ENTER_OPACITY); 0 fades the subtree in.
401    pub opacity: f32,
402    /// A turn in turns clockwise (KUI_ENTER_ROTATE), ADR 0043. ABI 27.
403    pub rotate: f32,
404    /// A uniform scale (KUI_ENTER_SCALE); 0 scales the subtree in from
405    /// nothing. ABI 27.
406    pub scale: f32,
407}
408
409/// Everything a box node is built from: size, layout, paint, behaviour
410/// tags and accessibility, as the `kui_open*` family and the stock
411/// widgets read it.
412///
413/// Zero-initialize it and set what you need; a zeroed field is its
414/// documented default. The library reads the whole struct, so build
415/// against the header that matches `kui_abi_version()`. Pointers (`KuiStr`
416/// and `KuiValue`) are borrowed while the node opens and never retained.
417#[repr(C)]
418#[derive(Clone, Copy)]
419pub struct KuiSpec {
420    pub width: KuiSizing,
421    pub height: KuiSizing,
422    /// Clamps applied after sizing resolves; 0 for max means unconstrained,
423    /// 0 for min is undeclared, `KUI_MIN_FIT` the node's own fit size and
424    /// `KUI_MIN_NONE` a declared 0.
425    pub min_w: f32,
426    pub max_w: f32,
427    pub min_h: f32,
428    pub max_h: f32,
429    /// `KUI_COLUMN` (0), `KUI_ROW` (1) or `KUI_TABLE` (2): a column whose
430    /// rows' children line up in columns.
431    pub dir: u32,
432    pub pad_l: f32,
433    pub pad_r: f32,
434    pub pad_t: f32,
435    pub pad_b: f32,
436    pub gap: f32,
437    /// 0 = start, 1 = center, 2 = end
438    pub main_align: u32,
439    pub cross_align: u32,
440    /// 0xRRGGBBAA; 0 = transparent
441    pub bg: u32,
442    pub border_color: u32,
443    pub border_w: f32,
444    pub radius: f32,
445    /// `KUI_CLIP` | `KUI_SCROLL_X` | `KUI_SCROLL_Y`; the same bits the
446    /// binary protocol carries, applied by `NodeSpec::overflow_bits`.
447    pub overflow: u32,
448    /// `KUI_FLOAT_NONE` (in flow), `KUI_FLOAT_PARENT` or `KUI_FLOAT_VIEWPORT`.
449    /// `kui_spec_float_preset` fills this and the fields below from one of
450    /// the named presets.
451    pub float_mode: u32,
452    /// Attach points as align values (0 start, 1 center, 2 end).
453    pub float_anchor_x: u32,
454    pub float_anchor_y: u32,
455    pub float_self_x: u32,
456    pub float_self_y: u32,
457    pub float_dx: f32,
458    pub float_dy: f32,
459    /// Non-zero: flip across the anchor / clamp to stay in the viewport.
460    pub float_fit: u32,
461    /// Non-zero: hover-track this node (kui_is_hovered) without a payload.
462    pub hoverable: u32,
463    /// Window-chrome role: 0 = none, 1 = drag, 2 = close button,
464    /// 3 = minimize button, 4 = maximize button. Chrome nodes emit window
465    /// commands (kui_take_window_command), never events.
466    pub window_role: u32,
467    /// Positive: ease sizing/colors/radius changes over this many ms (the
468    /// node needs a stable key, i.e. kui_open_keyed). Needs kui_set_time.
469    pub transition_ms: f32,
470    /// KUI_EASE_* curve for `transition_ms`.
471    pub easing: u32,
472    /// Non-zero (with `transition_ms`): also ease the node's position, so
473    /// reordered siblings slide into place.
474    pub slide: u32,
475    /// 0xRRGGBBAA background while hovered (or while any node in the same
476    /// hover group is); 0 = none. Implies hover tracking; eases with
477    /// `transition_ms`.
478    pub hover_bg: u32,
479    /// 0xRRGGBBAA background while pressed; 0 = none. Implies hover tracking.
480    pub pressed_bg: u32,
481    /// Hover group name (empty = none): members show hover_bg / pressed_bg
482    /// together. Hashed by the core; the string is not retained.
483    pub hover_group: KuiStr,
484    /// Non-zero: `radius_tl..radius_bl` are the four corner radii and
485    /// `radius` is ignored (zero = uniform `radius` on every corner).
486    pub per_corner: u32,
487    pub radius_tl: f32,
488    pub radius_tr: f32,
489    pub radius_br: f32,
490    pub radius_bl: f32,
491    /// KUI_REPEAT_* direction for `keyframes` (CSS animation-direction).
492    pub repeat: u32,
493    /// Holds the keyframe cycle back by this many ms (CSS animation-delay).
494    pub delay_ms: f32,
495    /// CSS-style stops (`keyframes_len` of them; NULL/0 = none): the slots
496    /// they name cycle over `transition_ms`, forever, without the view
497    /// redrawing. Read while the node opens; not retained.
498    pub keyframes: *const KuiKeyframe,
499    pub keyframes_len: usize,
500    /// Entrance: with `set` non-zero, the named slots ease in from these
501    /// values on the node's first sight (see `KuiEnter`).
502    pub enter: KuiEnter,
503    /// Registered sounds (`kui_sound_add`) played when the node is clicked /
504    /// the pointer enters it; 0 = none. Either makes the node hover-tracked.
505    pub click_sound: u64,
506    pub hover_sound: u64,
507    /// Layout tag (NULL = none): the node's laid-out rect arrives as
508    /// `{kind="layout", x, y, w, h, parent, tag}` on its first frame and
509    /// whenever it changes. Borrowed — cloned while the node opens, so the
510    /// caller keeps ownership and frees it as usual; `kui_value_null()`
511    /// asks for the events without a tag.
512    pub on_layout: *const KuiValue,
513    /// KUI_ROLE_* (0 = unset: the core derives one). What the node is to
514    /// assistive technology; KUI_ROLE_NONE hides it and its subtree.
515    pub role: u32,
516    /// Accessible name (empty = none). Copied while the node opens.
517    pub label: KuiStr,
518    /// Non-zero: a checkbox / radio / switch role is on.
519    pub checked: u32,
520    /// KUI_VALUE_NOW / MIN / MAX bits saying which of the three below are
521    /// set (a slider role's position and range).
522    pub value_set: u32,
523    pub value_now: f32,
524    pub value_min: f32,
525    pub value_max: f32,
526    /// On a KUI_ROLE_LINE of a custom editor: the caret's byte offset into
527    /// the line's text, and the selection's other end (KUI_VALUE_CARET /
528    /// KUI_VALUE_ANCHOR in `value_set` say which are present).
529    /// KUI_VALUE_CARET_SOLID beside KUI_VALUE_CARET says the caret is
530    /// solid — a block caret in a modal editor's normal mode — so
531    /// `kui_has_caret` leaves it out and no blink clock is armed on it,
532    /// while it still anchors the IME and reads to assistive technology.
533    pub caret: u32,
534    pub selection_anchor: u32,
535    /// Non-zero: reachable by Tab (and focused by a click) without a click
536    /// payload or a control role.
537    pub focusable: u32,
538    /// Non-zero: inert — no click, drag or key sink, no hover / pressed /
539    /// focus background, skipped by Tab, reported disabled to assistive
540    /// technology; hover tracking stays so a tooltip can say why.
541    pub disabled: u32,
542    /// 0xRRGGBBAA background while the node holds keyboard-visible focus
543    /// (Tab or assistive technology put it there); 0 = the core's default
544    /// ring.
545    pub focus_bg: u32,
546    /// Hover hint (empty = none), the `tooltip` prop of the other
547    /// bindings: makes the node hover-tracked, becomes its accessible
548    /// description, and floats `kui_core::widgets::tooltip` below it while
549    /// the pointer is over it: as its last child for the `kui_open*`
550    /// family and the `kui_fragment*` doors (backlog RG124), which
551    /// `kui_close` draws, and beside a leaf, anchored to it,
552    /// for the leaf doors (`leaf_spec_of`, backlog RG113).
553    pub tooltip: KuiStr,
554    /// Modal surface (NULL = none): while this node is declared the Tab
555    /// ring is its subtree, everything outside it is inert to the pointer,
556    /// the wheel and assistive technology, and Escape or a press outside
557    /// emits `{kind="dismiss", reason, tag}` on it — the app stops opening
558    /// the node. The last one declared wins (a confirm inside a dialog);
559    /// a modal that must cover the app is a float. Borrowed — cloned
560    /// while the node opens, so the caller keeps ownership;
561    /// `kui_value_null()` asks for the behaviour without a tag.
562    pub modal: *const KuiValue,
563    /// Context menu (NULL = none): a secondary-button press over this node
564    /// emits `{kind="contextmenu", x, y, tag}` on it, at the logical point
565    /// to open the menu at. The press moves no focus, places no caret and
566    /// produces no click. Borrowed — cloned while the node opens, so the
567    /// caller keeps ownership; `kui_value_null()` asks for the behaviour
568    /// without a tag.
569    pub on_context_menu: *const KuiValue,
570    /// KUI_CURSOR_* (0 = unset: the I-beam over text, the arrow otherwise
571    /// — a clickable or draggable node included). The pointer shape while
572    /// the pointer is over this node; a node with only a cursor is
573    /// hover-tracked so it can be found.
574    pub cursor: u32,
575    /// Non-zero: this node is the current one of its set — the shown tab,
576    /// the picked row, the link for the page you are on. A KUI_ROLE_TAB
577    /// reports the state either way; a row or a link reports it only when
578    /// this is set.
579    pub selected: u32,
580    /// KUI_EXPANDED_* (0 = unset: the node does not expand and says
581    /// nothing about it). What a twisty, an accordion header or a menu
582    /// button reads as.
583    pub expanded: u32,
584    /// Non-zero: `opacity` is the node's group opacity (0 without this bit
585    /// means "not set", so a fully transparent subtree stays expressible).
586    pub opacity_set: u32,
587    /// Group opacity 0..1 with `opacity_set`: fades this node and its whole
588    /// subtree. A per-quad alpha multiply, not an offscreen composite, so
589    /// overlapping pieces of one subtree show their seams through the fade;
590    /// layout, hit-testing and the access tree are untouched. Eases with
591    /// `transition_ms`.
592    pub opacity: f32,
593    /// 0xRRGGBBAA drop-shadow color; 0 = no shadow, and nothing else here
594    /// draws without it. The shadow is the node's rounded rect moved by
595    /// `shadow_x`/`shadow_y`, grown by `shadow_spread` and blurred over
596    /// `shadow_blur`, painted behind the node. Outer shadows only, and the
597    /// shape is not knocked out of the middle.
598    pub shadow_color: u32,
599    /// Blur radius (logical px): the edge ramps over this distance and
600    /// reaches this far past the shape. 0 = a hard edge.
601    pub shadow_blur: f32,
602    /// Offset (logical px); positive `shadow_y` casts downward.
603    pub shadow_x: f32,
604    pub shadow_y: f32,
605    /// Grows (negative: shrinks) the shape before blurring (logical px).
606    pub shadow_spread: f32,
607    /// Non-zero: children that don't fit the main axis start a new line
608    /// instead of overflowing or shrinking. Rows only — a column, or a row
609    /// with KUI_OVERFLOW_SCROLL_X, lays out as if this were 0 and raises a
610    /// `wrap-ignored` warning.
611    pub wrap_children: u32,
612    /// Space between wrap lines, across the main axis (`gap` stays the
613    /// space between children along it).
614    pub cross_gap: f32,
615    /// Non-zero: where focus lands when the enclosing `modal` scope is
616    /// entered - the first node in the modal's Tab ring declaring it,
617    /// instead of the ring's first, so a destructive confirm opens on its
618    /// Cancel. Read on entry only; nothing declaring it (or only nodes
619    /// the ring skips) keeps the ring's first node.
620    pub initial_focus: u32,
621    /// Exit: with `set` non-zero and a `transition_ms`, the frame after the
622    /// view stops declaring this node its subtree is copied out of the last
623    /// frame that had it and replayed — frozen where layout left it, on top
624    /// of everything and inert — while the named slots ease from where they
625    /// were to these values (see `KuiEnter`, which an exit reuses: an exit
626    /// is an entrance read the other way).
627    pub exit: KuiEnter,
628    /// KUI_LIVE_* (0 = KUI_LIVE_OFF, the default): when the text inside
629    /// this node changes, a screen reader reads the change without being
630    /// asked. A node that declares it is semantic, so a plain box marked
631    /// live is not elided from the access tree. For a one-off with no node
632    /// behind it, `kui_announce` is the other half.
633    pub live: u32,
634    /// Non-zero, with a non-NULL `on_key` on `kui_open_with`: the sink hears
635    /// releases too, as the same `{kind="key"}` payload with `phase="up"`
636    /// (`text` null, `repeat` false) — for a held-key interaction (WASD,
637    /// press-and-hold, a key that arms a mode while it is down). A key only
638    /// comes up where it went down, and focus leaving while a key is held
639    /// delivers the `up` first. Zero: presses only, which is what a keymap
640    /// wants — one that heard both halves would run every binding twice.
641    pub key_up: u32,
642    /// What a slider role's position reads as, empty for none (ARIA's
643    /// `aria-valuetext`). Without one a reader has only `value_now` and the
644    /// range and says a percentage — 25 in [5..60] is "36 percent" — so a
645    /// value whose unit carries the meaning says it here: "25 minutes". It
646    /// replaces the number in the reading rather than joining it, and a
647    /// nudge announces the new text. Copied while the node opens. It
648    /// arrives back as `KuiAccessNode.value` (KUI_ACCESS_HAS_VALUE), the
649    /// one string slot a node has.
650    pub value_text: KuiStr,
651    /// The accessible description (empty = none): the extra sentence a
652    /// reader says after the name, for what the name cannot say on its own
653    /// — what a button will do, why a control is disabled, what format a
654    /// field wants. `tooltip` is the shorthand that also draws the string
655    /// and hover-tracks the node; this is the description alone, for a hint
656    /// that is spoken and never drawn. Both write the one slot and this one
657    /// is applied second, so it wins over a `tooltip` on the same node. It
658    /// reads only on a node that reaches the access tree — a role, a label,
659    /// a control — since a plain box is elided and takes its description
660    /// with it. Borrowed while the node opens.
661    pub description: KuiStr,
662    /// Non-zero: ask for another frame after this one, every frame this
663    /// node is declared (`animate`). What a `kui_fragment` reading `time`
664    /// needs. Opt-in, because it takes the loop off input-driven; one node
665    /// asking is enough for the window.
666    pub animate: u32,
667    /// Non-zero: paint this node's background in the OS accent colour
668    /// (`kui_env_set_system`), keeping `bg` where the host never said what
669    /// it is. On `kui_button_with` it takes the hover and pressed shades
670    /// and the label colour with it.
671    pub accent: u32,
672    /// Non-zero: this node is a selection scope. The text of every node
673    /// inside it selects as one run, and a press-drag across them takes
674    /// the lot. Declared on the container, not on each label.
675    pub selectable: u32,
676    /// Force-click tag (`on_force_click`): a press that deepens past the
677    /// second stage of a Force Touch trackpad over this node emits
678    /// `{kind:"forceclick", x, y, tag}` on it. Borrowed while the node
679    /// opens, like every other tag.
680    pub on_force_click: *const KuiValue,
681    /// Non-zero: this node's subtree is a focus region, a Tab ring of its
682    /// own that the ring outside never enters and that never leaves.
683    /// Entered on purpose: `kui_focus_region`, a press inside it, or a
684    /// focus on a node in it. Nothing else about the node changes.
685    pub focus_region: u32,
686    /// When this node's scrollbars are drawn: `KUI_SCROLLBAR_*` (the
687    /// `scrollbar` row's index plus one), 0 for the default, which is
688    /// `KUI_SCROLLBAR_VISIBLE`.
689    pub scrollbar: u32,
690    /// The thumb's width at rest, logical px; 0 for the stock 4. Under
691    /// the pointer or dragged it is 2 px wider.
692    pub scrollbar_width: f32,
693    /// The thumb at rest and under the pointer, `0xRRGGBBAA`; 0 (fully
694    /// transparent, like every unset colour) for the theme's `scrollbar`
695    /// and `scrollbar_active`.
696    pub scrollbar_color: u32,
697    pub scrollbar_active_color: u32,
698    /// Non-zero: scroll anchoring on this scrolling node (CSS's
699    /// `overflow-anchor`): the first child in view keeps its place on
700    /// screen when the content before it changes size.
701    pub anchor: u32,
702    /// Scroll tag (`on_scroll`): the wheel over this node emits
703    /// `{kind:"scroll", x, y, dx, dy, lines, tag}` on it instead of
704    /// scrolling anything (`lines` the whole lines a `cells` grid's delta
705    /// covers, null elsewhere), and a drag-select held past a grid's edge
706    /// arrives the same way once a frame. Borrowed while the node opens,
707    /// like every other tag.
708    pub on_scroll: *const KuiValue,
709    /// Drop-zone tag (`on_drop`): files dragged in from the OS over this
710    /// node emit `{kind:"drop", phase, paths, x, y, tag}` on it, `phase`
711    /// one of `enter`, `move`, `leave`, `drop`. A node inside a zone is
712    /// the zone's; a node that is no zone is looked past. Borrowed while
713    /// the node opens, like every other tag. ABI 18.
714    pub on_drop: *const KuiValue,
715    /// Background while dragged files are over this node, `0xRRGGBBAA`;
716    /// 0 for none. Wins over `pressed_bg`, `focus_bg` and `hover_bg`;
717    /// eases with `transition`. ABI 18.
718    pub drop_bg: u32,
719    /// Non-zero: a `KUI_FLOAT_PARENT` float takes its parent's clip, as a
720    /// child does, instead of escaping every ancestor's: cut at a `clip`
721    /// canvas's edge and not hit past it. Read with the parent anchor
722    /// only; still painted as a layer over its in-flow siblings. ABI 19.
723    pub float_clip: u32,
724    /// Width over height (`aspectRatio`); 0 for none. It sizes the axis
725    /// whose sizing is fit: a fit height from the final width, a fit width
726    /// from a fixed height. ABI 19.
727    pub aspect_ratio: f32,
728    /// A checkbox that is neither on nor off (`mixed`): read as mixed
729    /// whatever `checked` says, drawn as a dash by `kui_checkbox`. ABI 19.
730    pub mixed: u32,
731    /// A slider's step (`valueStep`), present when `KUI_VALUE_STEP` is in
732    /// `value_set`. ABI 19.
733    pub value_step: f32,
734    /// A slider's change tag (`onChange`): the core turns a press, a drag,
735    /// the arrows, PageUp / PageDown and Home / End into
736    /// `{kind:"change", value, phase, tag}`. Borrowed while the node
737    /// opens, like every other tag. ABI 19.
738    pub on_change: *const KuiValue,
739    /// Non-zero: paint the background, border, shadow and fragment with each edge
740    /// on a whole physical pixel (`pixelSnap`), so snapped boxes that
741    /// share an edge in layout, and one beside a text's background, meet
742    /// without a seam. A zeroed field is a box drawn where layout put it.
743    /// ABI 20.
744    pub pixel_snap: u32,
745    /// Non-zero: a press on this node or inside it leaves keyboard focus
746    /// where it was (`keepFocus`). ABI 20.
747    pub keep_focus: u32,
748    /// Focus entering or leaving this node's subtree emits `{kind:"focus",
749    /// phase, by, tag}` (`onFocus`). Borrowed while the node opens, like
750    /// every other tag. ABI 20.
751    pub on_focus: *const KuiValue,
752    /// A table's grid rules (`rules`), 0xRRGGBBAA; 0 draws none. ABI 20.
753    pub rules: u32,
754    /// Their width in logical px (`ruleWidth`); 0 is 1. ABI 20.
755    pub rule_w: f32,
756    /// The non-primary buttons as `{kind:"button", phase, button, x, y,
757    /// clicks, tag}` events on the node that claims them, captured from
758    /// press to release (`onButton`). Borrowed while the node opens, like
759    /// every other tag. ABI 20.
760    pub on_button: *const KuiValue,
761    /// Which buttons `on_button` claims, as `KUI_BUTTONS_*` bits; 0 is all
762    /// three. ABI 20.
763    pub buttons: u32,
764    /// Whether a scroll gesture starting over this scroller at its limit
765    /// goes on to the one around it (`overscroll`): `KUI_OVERSCROLL_*`,
766    /// the row's index plus one; 0 is `auto`. ABI 20.
767    pub overscroll: u32,
768    /// Which axes `on_scroll` takes (`scrollAxes`): `KUI_SCROLL_AXES_*`,
769    /// the row's index plus one; 0 is both. ABI 20.
770    pub scroll_axes: u32,
771    /// Non-zero, with `on_key`: the modifier and lock keys arrive as keys
772    /// of their own (`modifierKeys`), with codes "shift", "ctrl", "alt",
773    /// "super", "capslock", "numlock", "scrolllock" and the side in
774    /// `location`. Zero: a modifier is only ever held. ABI 21.
775    pub modifier_keys: u32,
776    /// The clamps as size expressions: a `KUI_FIXED`, `KUI_PERCENT` or
777    /// `KUI_CALC` sizing (`kui_size_*`) here replaces the float of the
778    /// same name, resolved by layout against the parent's content box;
779    /// zeroed (`KUI_FIT`), the float holds. ABI 22.
780    pub min_w_size: KuiSizing,
781    pub max_w_size: KuiSizing,
782    pub min_h_size: KuiSizing,
783    pub max_h_size: KuiSizing,
784    /// How far a spring overshoots (`bounce`), in place of a spring
785    /// easing's own, and making a timed easing a spring; 0 is the
786    /// easing's own, since a spring with none is `KUI_EASE_SMOOTH`.
787    /// ABI 23.
788    pub bounce: f32,
789    /// A gradient painted over `bg`, under the border and the children;
790    /// NULL for none. Read during the call. ABI 24.
791    pub gradient: *const KuiGradient,
792    /// The modifiers `on_scroll` is for (`scrollMods`), as `KUI_KMOD_*`
793    /// bits: with any set, the node hears only a scroll gesture begun
794    /// with one of them held, ahead of every scroller under the pointer.
795    /// 0 is none: a handler like any other. ABI 25.
796    pub scroll_mods: u32,
797    /// Blur what was drawn beneath the node, inside its rounded box, by
798    /// this radius in logical px (`backdropBlur`, backlog F129); 0 is
799    /// none. ABI 26.
800    pub backdrop_blur: f32,
801    /// A turn of the node and everything under it, in turns clockwise,
802    /// about its pivot, after layout (`rotate`, ADR 0043); 0 is none.
803    /// ABI 27.
804    pub rotate: f32,
805    /// A uniform scale about the pivot (`scale`); 0, the zeroed spec, is
806    /// 1 — a scale of nothing is `opacity`'s job. ABI 27.
807    pub scale: f32,
808    /// `KUI_PIVOT_X` / `KUI_PIVOT_Y` bits saying which of `pivot_x` /
809    /// `pivot_y` hold — fractions of the box (`pivotX` / `pivotY`); an
810    /// axis not set keeps the centre, so a pivot on the top edge or at
811    /// the top-left corner stays expressible. ABI 27.
812    pub pivot_set: u32,
813    pub pivot_x: f32,
814    pub pivot_y: f32,
815    /// How many times the `keyframes` cycle runs (`iterations`, backlog
816    /// F133); 0, the zeroed spec, is for ever. A finite cycle plays from
817    /// the first frame the node is declared with it and rests where its
818    /// last iteration ended. ABI 27.
819    pub iterations: f32,
820}
821
822/// `KuiSpec.pivot_set` bits: which of `pivot_x` / `pivot_y` hold.
823pub const KUI_PIVOT_X: u32 = 1 << 0;
824pub const KUI_PIVOT_Y: u32 = 1 << 1;
825
826/// One laid-out run of an editor's text (`kui_access_runs`): what a
827/// screen reader reads by character and word. `text` ends with `"\n"`
828/// (counted as a zero-width character) when the line continues into
829/// another. Character positions are relative to `x`. Arrays and strings
830/// are borrowed until the next `kui_access_tree` / `kui_access_runs` on
831/// the context.
832#[repr(C)]
833#[derive(Clone, Copy)]
834pub struct KuiAccessRun {
835    /// The run's own id (a `KuiAccessNode.anchor_run` / `focus_run`, and
836    /// what `kui_input_access_text` takes).
837    pub key: u64,
838    /// The line it belongs to (a buffer line, or a KUI_ROLE_LINE ordinal
839    /// for a custom editor) and its byte range in that line's text.
840    pub line: u32,
841    pub start: u32,
842    pub end: u32,
843    pub text: KuiStr,
844    pub x: f32,
845    pub y: f32,
846    pub w: f32,
847    pub h: f32,
848    pub char_count: u32,
849    pub char_lengths: *const u8,
850    pub char_positions: *const f32,
851    pub char_widths: *const f32,
852    pub word_start_count: u32,
853    pub word_starts: *const u8,
854    pub rtl: u32,
855}
856
857pub const KUI_VALUE_CARET: u32 = 1 << 3;
858pub const KUI_VALUE_ANCHOR: u32 = 1 << 4;
859pub const KUI_VALUE_CARET_SOLID: u32 = 1 << 5;
860pub const KUI_ACCESS_HAS_TEXT_SELECTION: u32 = 1 << 9;
861
862/// One node of the access tree (`kui_access_tree`): what assistive
863/// technology sees. `role` is KUI_ROLE_*, `flags` KUI_ACCESS_HAS_* /
864/// FOCUSED / CHECKED bits saying which optional fields hold, `actions`
865/// the KUI_ACCESS_* bits the node accepts through `kui_input_access`.
866/// Strings are borrowed until the next `kui_access_tree` on the context.
867#[repr(C)]
868#[derive(Clone, Copy)]
869pub struct KuiAccessNode {
870    pub key: u64,
871    /// The nearest semantic ancestor; 0 for the root.
872    pub parent: u64,
873    pub origin: u32,
874    pub role: u32,
875    pub flags: u32,
876    pub actions: u32,
877    pub name: KuiStr,
878    pub description: KuiStr,
879    /// The node's one string value (KUI_ACCESS_HAS_VALUE): an editor's
880    /// text, or a slider's `value_text` — the platform has one slot, and a
881    /// slider that named its reading reads as that instead of its number.
882    pub value: KuiStr,
883    /// Logical px, viewport coordinates, cut to the node's clip (zero-size
884    /// on the clip's edge when wholly clipped).
885    pub x: f32,
886    pub y: f32,
887    pub w: f32,
888    pub h: f32,
889    /// Byte offsets into `value` (KUI_ACCESS_HAS_VALUE / HAS_SELECTION).
890    pub caret: u32,
891    pub selection_start: u32,
892    pub selection_end: u32,
893    /// A slider's position and range (KUI_ACCESS_HAS_NUMBER / MIN / MAX).
894    pub value_now: f32,
895    pub value_min: f32,
896    pub value_max: f32,
897    /// A scroll view's offsets and range (KUI_ACCESS_HAS_SCROLL).
898    pub scroll_x: f32,
899    pub scroll_y: f32,
900    pub scroll_max_x: f32,
901    pub scroll_max_y: f32,
902    /// An editor's caret (`focus_*`) and the selection's other end
903    /// (`anchor_*`) as run positions (KUI_ACCESS_HAS_TEXT_SELECTION):
904    /// a run key from `kui_access_runs` and a character index into it.
905    pub anchor_run: u64,
906    pub anchor_char: u32,
907    pub focus_run: u64,
908    pub focus_char: u32,
909    /// How many runs `kui_access_runs` returns for this node.
910    pub run_count: u32,
911    /// "3 of 7" (KUI_ACCESS_HAS_POS_IN_SET / HAS_SET_SIZE): the item's
912    /// zero-based ordinal among its list's or tab list's items, and the
913    /// count on that container.
914    pub pos_in_set: u32,
915    pub set_size: u32,
916    /// KUI_ORIENTATION_* (0 = unset: this node is not a composite
917    /// container). How the container arranges its items, from its own
918    /// `dir`.
919    pub orientation: u32,
920}
921
922pub const KUI_ACCESS_HAS_VALUE: u32 = 1 << 0;
923pub const KUI_ACCESS_HAS_SELECTION: u32 = 1 << 1;
924pub const KUI_ACCESS_FOCUSED: u32 = 1 << 2;
925pub const KUI_ACCESS_CHECKED_SET: u32 = 1 << 3;
926pub const KUI_ACCESS_CHECKED: u32 = 1 << 4;
927pub const KUI_ACCESS_HAS_NUMBER: u32 = 1 << 5;
928pub const KUI_ACCESS_HAS_MIN: u32 = 1 << 6;
929pub const KUI_ACCESS_HAS_MAX: u32 = 1 << 7;
930pub const KUI_ACCESS_HAS_SCROLL: u32 = 1 << 8;
931/// The node is `disabled`: inert, not a Tab stop (bit 9 is
932/// KUI_ACCESS_HAS_TEXT_SELECTION).
933pub const KUI_ACCESS_DISABLED: u32 = 1 << 10;
934/// The node is the frame's `modal` surface (`aria-modal`): focus and input
935/// are confined to it.
936pub const KUI_ACCESS_MODAL: u32 = 1 << 11;
937/// The node has a selected state at all, and what it is: every
938/// KUI_ROLE_TAB, and a row or link the view marked (see `KuiSpec.selected`).
939pub const KUI_ACCESS_SELECTED_SET: u32 = 1 << 12;
940pub const KUI_ACCESS_SELECTED: u32 = 1 << 13;
941/// The node expands, and whether it is open (see `KuiSpec.expanded`).
942pub const KUI_ACCESS_EXPANDED_SET: u32 = 1 << 14;
943pub const KUI_ACCESS_EXPANDED: u32 = 1 << 15;
944/// `pos_in_set` holds (on an item), `set_size` holds (on its container).
945pub const KUI_ACCESS_HAS_POS_IN_SET: u32 = 1 << 16;
946pub const KUI_ACCESS_HAS_SET_SIZE: u32 = 1 << 17;
947/// A checkbox that is neither on nor off (`KuiSpec.mixed`); set beside
948/// `KUI_ACCESS_CHECKED_SET`, whose `KUI_ACCESS_CHECKED` it outranks.
949pub const KUI_ACCESS_MIXED: u32 = 1 << 20;
950/// The node declared `live` (see `KuiSpec.live`), and which politeness.
951/// Two bits rather than a field, because `KuiAccessNode` is an array the
952/// host allocates and appending to it would be an ABI break.
953pub const KUI_ACCESS_LIVE_POLITE: u32 = 1 << 18;
954pub const KUI_ACCESS_LIVE_ASSERTIVE: u32 = 1 << 19;
955
956/// KUI_ORIENTATION_* is the position in `Orientation::ALL` plus one
957/// (0 = unset: the node is not a composite container).
958pub const KUI_ORIENTATION_HORIZONTAL: u32 = 1;
959pub const KUI_ORIENTATION_VERTICAL: u32 = 2;
960
961pub(crate) fn orientation_code(o: Option<kui_core::Orientation>) -> u32 {
962    o.and_then(|o| kui_core::Orientation::ALL.iter().position(|x| *x == o))
963        .map_or(0, |i| i as u32 + 1)
964}
965
966/// KUI_EXPANDED_* is the position in `schema::EXPANDED` plus one (0 = unset:
967/// the node does not expand).
968pub const KUI_EXPANDED_COLLAPSED: u32 = 1;
969pub const KUI_EXPANDED_EXPANDED: u32 = 2;
970
971/// KUI_SCROLLBAR_* is the position in `schema::SCROLLBARS` plus one (0 =
972/// unset, which is the stock visible bar).
973pub const KUI_SCROLLBAR_VISIBLE: u32 = 1;
974pub const KUI_SCROLLBAR_HIDDEN: u32 = 2;
975pub const KUI_SCROLLBAR_AUTO: u32 = 3;
976
977/// KUI_OVERSCROLL_* is the position in `schema::OVERSCROLLS` plus one (0 =
978/// unset, which is `auto`).
979pub const KUI_OVERSCROLL_AUTO: u32 = 1;
980pub const KUI_OVERSCROLL_CONTAIN: u32 = 2;
981
982/// KUI_SCROLL_AXES_* is the position in `schema::SCROLL_AXES` plus one (0
983/// = unset, which is both).
984pub const KUI_SCROLL_AXES_BOTH: u32 = 1;
985pub const KUI_SCROLL_AXES_X: u32 = 2;
986pub const KUI_SCROLL_AXES_Y: u32 = 3;
987
988/// KUI_LIVE_* is the position in `schema::LIVE` itself, not the position
989/// plus one: unlike a disclosure, a live region's zero *is* a value —
990/// "not a live region" is what an unset field already means, so there is
991/// no unset state to reserve zero for.
992pub const KUI_LIVE_OFF: u32 = 0;
993pub const KUI_LIVE_POLITE: u32 = 1;
994pub const KUI_LIVE_ASSERTIVE: u32 = 2;
995
996/// One queued announcement (`kui_take_announcements`): something to say
997/// once, with no node behind it. `live` is KUI_LIVE_POLITE or
998/// KUI_LIVE_ASSERTIVE — never KUI_LIVE_OFF, which `kui_announce` drops.
999/// `text` borrows the context's buffer and stays valid until the next
1000/// `kui_take_announcements` on the same context.
1001#[repr(C)]
1002#[derive(Clone, Copy)]
1003pub struct KuiAnnouncement {
1004    pub text: KuiStr,
1005    pub live: u32,
1006}
1007
1008pub const KUI_VALUE_NOW: u32 = 1 << 0;
1009pub const KUI_VALUE_MIN: u32 = 1 << 1;
1010pub const KUI_VALUE_MAX: u32 = 1 << 2;
1011/// `KuiSpec.value_step` holds.
1012pub const KUI_VALUE_STEP: u32 = 1 << 6;
1013
1014/// KUI_ROLE_* is the position in `Role::ALL` plus one (0 = unset).
1015pub(crate) fn role_code(role: kui_core::Role) -> u32 {
1016    kui_core::Role::ALL
1017        .iter()
1018        .position(|r| *r == role)
1019        .map_or(0, |i| i as u32 + 1)
1020}
1021
1022pub(crate) fn role_of_code(code: u32) -> Option<kui_core::Role> {
1023    (code > 0)
1024        .then(|| kui_core::Role::ALL.get(code as usize - 1).copied())
1025        .flatten()
1026}
1027
1028/// What a piece of text measures (`kui_measure_text`), logical px at the
1029/// scale of the current or last frame.
1030#[repr(C)]
1031#[derive(Clone, Copy)]
1032pub struct KuiTextMetrics {
1033    /// `[out]` reservation; see `KUI_TEXT_METRICS_INIT`.
1034    pub size: u32,
1035    pub width: f32,
1036    pub height: f32,
1037    /// Lines after wrapping (capped by `max_lines`).
1038    pub lines: u32,
1039}
1040
1041// Hand-written rather than derived: a derived `Default` would zero `size`,
1042// and a zero `size` is the one value the handshake refuses.
1043impl Default for KuiTextMetrics {
1044    fn default() -> Self {
1045        Self {
1046            size: std::mem::size_of::<Self>() as u32,
1047            width: 0.0,
1048            height: 0.0,
1049            lines: 0,
1050        }
1051    }
1052}
1053
1054// SAFETY: `repr(C)` with `size: u32` first.
1055unsafe impl OutParam for KuiTextMetrics {
1056    const ABI_V1_SIZE: u32 = abi_through!(KuiTextMetrics, lines, u32);
1057    fn size_mut(&mut self) -> &mut u32 {
1058        &mut self.size
1059    }
1060}
1061
1062/// The palette a frame paints with: one `0xRRGGBBAA` per role, derived
1063/// from what the host reported through `kui_env_set_system` unless it
1064/// pinned something else. Written by `kui_theme` (`[out]`, so start from
1065/// `KUI_THEME_INIT`) and read whole by `kui_theme_set` (`[in]`).
1066///
1067/// The roles are `kui_core::schema::THEME_ROLES` field for field, in that
1068/// order; a test pins the two together, and an append here bumps
1069/// `KUI_ABI_VERSION`.
1070#[repr(C)]
1071#[derive(Clone, Copy)]
1072pub struct KuiTheme {
1073    /// `[out]` reservation; see `KUI_THEME_INIT`. Ignored by
1074    /// `kui_theme_set`, which reads the struct the host filled.
1075    pub size: u32,
1076    /// Which base this came from: `KUI_APPEARANCE_UNKNOWN` (0, the dark
1077    /// base without claiming the user chose it) / `_LIGHT` / `_DARK`.
1078    pub appearance: u32,
1079    /// What a disabled control's opacity is multiplied by.
1080    pub disabled_opacity: f32,
1081    pub bg: u32,
1082    pub surface: u32,
1083    pub raised: u32,
1084    pub sunken: u32,
1085    pub border: u32,
1086    pub border_strong: u32,
1087    pub fg: u32,
1088    pub muted: u32,
1089    pub faint: u32,
1090    pub accent: u32,
1091    pub accent_hover: u32,
1092    pub accent_pressed: u32,
1093    pub on_accent: u32,
1094    pub accent_soft: u32,
1095    pub selection: u32,
1096    pub focus_ring: u32,
1097    pub hover: u32,
1098    pub pressed: u32,
1099    pub success: u32,
1100    pub warning: u32,
1101    pub danger: u32,
1102    pub scrollbar: u32,
1103    pub scrollbar_active: u32,
1104}
1105
1106// Hand-written for the same reason `KuiTextMetrics`'s is: a zeroed `size`
1107// is the one value the handshake refuses.
1108impl Default for KuiTheme {
1109    fn default() -> Self {
1110        Self {
1111            size: std::mem::size_of::<Self>() as u32,
1112            appearance: 0,
1113            disabled_opacity: 0.0,
1114            bg: 0,
1115            surface: 0,
1116            raised: 0,
1117            sunken: 0,
1118            border: 0,
1119            border_strong: 0,
1120            fg: 0,
1121            muted: 0,
1122            faint: 0,
1123            accent: 0,
1124            accent_hover: 0,
1125            accent_pressed: 0,
1126            on_accent: 0,
1127            accent_soft: 0,
1128            selection: 0,
1129            focus_ring: 0,
1130            hover: 0,
1131            pressed: 0,
1132            success: 0,
1133            warning: 0,
1134            danger: 0,
1135            scrollbar: 0,
1136            scrollbar_active: 0,
1137        }
1138    }
1139}
1140
1141// SAFETY: `repr(C)` with `size: u32` first.
1142unsafe impl OutParam for KuiTheme {
1143    const ABI_V1_SIZE: u32 = abi_through!(KuiTheme, scrollbar_active, u32);
1144    fn size_mut(&mut self) -> &mut u32 {
1145        &mut self.size
1146    }
1147}
1148
1149/// The sizes the stock widgets are built from (`kui_metrics`,
1150/// `kui_metrics_set`), in logical px before the scale factor:
1151/// `kui_core::schema::METRIC_ROLES` field for field, in that order. An
1152/// append here bumps `KUI_ABI_VERSION`.
1153#[repr(C)]
1154#[derive(Clone, Copy)]
1155pub struct KuiMetrics {
1156    /// `[out]` reservation; see `KUI_METRICS_INIT`. Ignored by
1157    /// `kui_metrics_set`, which reads the struct the host filled.
1158    pub size: u32,
1159    pub control_text: f32,
1160    pub chrome_text: f32,
1161    pub hint_text: f32,
1162    pub radius: f32,
1163    pub radius_inner: f32,
1164    pub control_pad_x: f32,
1165    pub control_pad_y: f32,
1166    pub field_pad_x: f32,
1167    pub field_pad_y: f32,
1168    pub hint_pad_x: f32,
1169    pub hint_pad_y: f32,
1170    pub menu_pad_x: f32,
1171    pub menu_pad_y: f32,
1172    pub menu_width: f32,
1173    pub menu_bar_h: f32,
1174    pub titlebar_h: f32,
1175}
1176
1177impl Default for KuiMetrics {
1178    fn default() -> Self {
1179        Self::of(&kui_core::Metrics::default())
1180    }
1181}
1182
1183impl KuiMetrics {
1184    /// A core `Metrics` as the host reads it, through the role table so
1185    /// the two cannot disagree on a field.
1186    pub(crate) fn of(m: &kui_core::Metrics) -> Self {
1187        let mut out = Self {
1188            size: std::mem::size_of::<Self>() as u32,
1189            control_text: 0.0,
1190            chrome_text: 0.0,
1191            hint_text: 0.0,
1192            radius: 0.0,
1193            radius_inner: 0.0,
1194            control_pad_x: 0.0,
1195            control_pad_y: 0.0,
1196            field_pad_x: 0.0,
1197            field_pad_y: 0.0,
1198            hint_pad_x: 0.0,
1199            hint_pad_y: 0.0,
1200            menu_pad_x: 0.0,
1201            menu_pad_y: 0.0,
1202            menu_width: 0.0,
1203            menu_bar_h: 0.0,
1204            titlebar_h: 0.0,
1205        };
1206        for role in kui_core::schema::METRIC_ROLES {
1207            *out.field_mut(role.name) = (role.get)(m);
1208        }
1209        out
1210    }
1211
1212    /// The host's struct as a core `Metrics`, through the same table.
1213    pub(crate) fn to_core(self) -> kui_core::Metrics {
1214        let mut m = kui_core::Metrics::default();
1215        for role in kui_core::schema::METRIC_ROLES {
1216            (role.set)(&mut m, *self.field(role.name));
1217        }
1218        m
1219    }
1220
1221    fn field(&self, name: &str) -> &f32 {
1222        match name {
1223            "control_text" => &self.control_text,
1224            "chrome_text" => &self.chrome_text,
1225            "hint_text" => &self.hint_text,
1226            "radius" => &self.radius,
1227            "radius_inner" => &self.radius_inner,
1228            "control_pad_x" => &self.control_pad_x,
1229            "control_pad_y" => &self.control_pad_y,
1230            "field_pad_x" => &self.field_pad_x,
1231            "field_pad_y" => &self.field_pad_y,
1232            "hint_pad_x" => &self.hint_pad_x,
1233            "hint_pad_y" => &self.hint_pad_y,
1234            "menu_pad_x" => &self.menu_pad_x,
1235            "menu_pad_y" => &self.menu_pad_y,
1236            "menu_width" => &self.menu_width,
1237            "menu_bar_h" => &self.menu_bar_h,
1238            "titlebar_h" => &self.titlebar_h,
1239            other => panic!("METRIC_ROLES names a metric KuiMetrics lacks: {other}"),
1240        }
1241    }
1242
1243    fn field_mut(&mut self, name: &str) -> &mut f32 {
1244        match name {
1245            "control_text" => &mut self.control_text,
1246            "chrome_text" => &mut self.chrome_text,
1247            "hint_text" => &mut self.hint_text,
1248            "radius" => &mut self.radius,
1249            "radius_inner" => &mut self.radius_inner,
1250            "control_pad_x" => &mut self.control_pad_x,
1251            "control_pad_y" => &mut self.control_pad_y,
1252            "field_pad_x" => &mut self.field_pad_x,
1253            "field_pad_y" => &mut self.field_pad_y,
1254            "hint_pad_x" => &mut self.hint_pad_x,
1255            "hint_pad_y" => &mut self.hint_pad_y,
1256            "menu_pad_x" => &mut self.menu_pad_x,
1257            "menu_pad_y" => &mut self.menu_pad_y,
1258            "menu_width" => &mut self.menu_width,
1259            "menu_bar_h" => &mut self.menu_bar_h,
1260            "titlebar_h" => &mut self.titlebar_h,
1261            other => panic!("METRIC_ROLES names a metric KuiMetrics lacks: {other}"),
1262        }
1263    }
1264}
1265
1266// SAFETY: `repr(C)` with `size: u32` first.
1267unsafe impl OutParam for KuiMetrics {
1268    const ABI_V1_SIZE: u32 = abi_through!(KuiMetrics, titlebar_h, f32);
1269    fn size_mut(&mut self) -> &mut u32 {
1270        &mut self.size
1271    }
1272}
1273
1274/// One diagnostic (`kui_take_warnings`): a silent misconfiguration the
1275/// core noticed. `code` is stable (`grow-weight-ignored`,
1276/// `transition-auto-key`, `duplicate-key`); the strings are borrowed until
1277/// the next `kui_take_warnings` on the same context.
1278#[repr(C)]
1279#[derive(Clone, Copy)]
1280pub struct KuiWarning {
1281    pub code: KuiStr,
1282    pub key: u64,
1283    pub message: KuiStr,
1284}
1285
1286/// One installed or loaded font family (`kui_system_fonts`), as the font
1287/// database read its faces. `family` and `weights` are borrowed until the
1288/// next `kui_system_fonts` on the same context.
1289#[repr(C)]
1290#[derive(Clone, Copy)]
1291pub struct KuiSystemFont {
1292    /// The name `kui_font_add_system` takes.
1293    pub family: KuiStr,
1294    /// The weights its faces come in (400 regular, 700 bold), sorted,
1295    /// each once; `weight_count` of them.
1296    pub weights: *const u16,
1297    pub weight_count: u32,
1298    /// 1 when every face says it is fixed-pitch.
1299    pub monospaced: u32,
1300    /// 1 when it has an italic or an oblique face.
1301    pub italic: u32,
1302}
1303
1304/// Options for `kui_play`. NULL means defaults; a given struct is read
1305/// literally (so `volume` must be set — `KUI_PLAY_INIT` in kui.h does).
1306#[repr(C)]
1307#[derive(Clone, Copy)]
1308pub struct KuiPlay {
1309    /// Linear amplitude, 0..1.
1310    pub volume: f32,
1311    pub looped: u32,
1312    pub fade_in_ms: f32,
1313}
1314
1315/// What a `kui_audio` node declares; read literally (`KUI_AUDIO_INIT`).
1316#[repr(C)]
1317#[derive(Clone, Copy)]
1318pub struct KuiAudio {
1319    /// A registered sound (`kui_sound_add`).
1320    pub src: u64,
1321    /// Linear amplitude, 0..1.
1322    pub volume: f32,
1323    pub looped: u32,
1324    pub paused: u32,
1325    /// Removal releases the playback instead of stopping it: it plays to
1326    /// its end. Zero stops it.
1327    pub finish: u32,
1328}
1329
1330/// One audio command for a host that drives its own device
1331/// (`kui_take_audio_commands`); `kind` is `KUI_AUDIO_*` and says which of
1332/// the other fields mean anything.
1333#[repr(C)]
1334#[derive(Clone, Copy, Default)]
1335pub struct KuiAudioCommand {
1336    pub kind: u32,
1337    pub playback: u64,
1338    pub sound: u64,
1339    /// Linear amplitude (play, set_volume, master_volume).
1340    pub volume: f32,
1341    /// Fade / tween duration in ms (fade-in for play).
1342    pub ms: f32,
1343    pub looped: u32,
1344}
1345
1346/// How a run of text is set: size, line height, colour, family or font,
1347/// wrapping and decoration. A zeroed struct is the default style at size
1348/// 0, so set at least `size`; NULL where a style pointer is taken is the
1349/// default style.
1350#[repr(C)]
1351#[derive(Clone, Copy)]
1352pub struct KuiTextStyle {
1353    pub size: f32,
1354    /// <= 0 picks the default (size * 1.35).
1355    pub line_height: f32,
1356    /// 0xRRGGBBAA; 0 = default foreground
1357    pub color: u32,
1358    /// KUI_FONT_SANS (0, default) / KUI_FONT_SERIF / KUI_FONT_MONO.
1359    pub family: u32,
1360    /// A registered font handle (kui_font_add / kui_font_add_system);
1361    /// non-zero overrides `family`.
1362    pub font: u64,
1363    /// KUI_WRAP_WORD (0, default) / KUI_WRAP_GLYPH / KUI_WRAP_NONE /
1364    /// KUI_WRAP_BREAK_SPACES.
1365    pub wrap: u32,
1366    /// Lay out at most this many lines; 0 = unlimited.
1367    pub max_lines: u32,
1368    /// Non-zero: end the last line with an ellipsis when the text is cut
1369    /// off (a single line unless `max_lines` says otherwise).
1370    pub ellipsis: u32,
1371    /// OpenType features for the shaper, in the spelling every binding
1372    /// shares: `tag=value` pairs separated by spaces or commas, a bare
1373    /// tag meaning 1 and `-tag` 0 (`"liga=0 calt=0"`, `"tnum"`). Empty
1374    /// (a zeroed `KuiStr`) is the font's defaults.
1375    pub features: KuiStr,
1376    /// `KUI_DECO_UNDERLINE` | `KUI_DECO_STRIKETHROUGH`: lines where the
1377    /// face puts them, over every glyph. Paint only.
1378    pub decoration: u32,
1379    /// The underline's own colour as `0xRRGGBBAA`, 0 for the text's;
1380    /// non-zero implies `KUI_DECO_UNDERLINE`. ABI 17.
1381    pub underline_color: u32,
1382    /// `KUI_UNDERLINE_SOLID` / `_WAVY` / `_DOTTED`; a non-solid style
1383    /// implies `KUI_DECO_UNDERLINE`. ABI 17.
1384    pub underline_style: u32,
1385}
1386
1387/// One styled run of a rich-text paragraph (`kui_rich_text`,
1388/// `kui_measure_rich_text`): its text and what differs from the base
1389/// style. Travels as an array, so an append here is an ABI bump.
1390#[repr(C)]
1391#[derive(Clone, Copy)]
1392pub struct KuiSpan {
1393    pub text: KuiStr,
1394    /// 0xRRGGBBAA; 0 = inherit the paragraph color
1395    pub color: u32,
1396    /// `KUI_SPAN_BOLD` | `KUI_SPAN_ITALIC` | `KUI_SPAN_UNDERLINE` |
1397    /// `KUI_SPAN_STRIKETHROUGH`
1398    pub flags: u32,
1399    /// 0xRRGGBBAA behind the span's glyphs alone, one rect per line the
1400    /// span covers; 0 = none. ABI 8.
1401    pub bg: u32,
1402    /// The underline's own colour, 0 for the span's; non-zero implies
1403    /// `KUI_SPAN_UNDERLINE`. ABI 17.
1404    pub underline_color: u32,
1405    /// `KUI_UNDERLINE_*`; non-solid implies `KUI_SPAN_UNDERLINE`. ABI 17.
1406    pub underline_style: u32,
1407    /// The background's corner radius, logical px; 0 is the square
1408    /// background. Above zero, `bg` is joined into one shape with
1409    /// every rounded background of the same colour and radius it meets —
1410    /// on the line above or below, or end to end on its own line, in this
1411    /// text or another — rounded outside where a line reaches past its
1412    /// neighbour and filleted inside where it falls short: a selection
1413    /// over rows is one outline. ABI 20.
1414    pub bg_radius: f32,
1415    /// With `KUI_SPAN_FAMILY` in `flags`, the span's own face, a
1416    /// `KUI_FONT_*`; without it the paragraph's. ABI 26.
1417    pub family: u32,
1418    /// The span's own size, logical px; 0 is the paragraph's. ABI 26.
1419    pub size: f32,
1420    /// A registered font handle, the span's face; non-zero overrides
1421    /// `family`. ABI 26.
1422    pub font: u64,
1423}
1424
1425/// One colour token as `kui_tokens_set` reads it: a name and a value per
1426/// base, `0xRRGGBBAA` each (the same value twice for a colour that does
1427/// not follow the appearance). Travels as an array, so an append here is
1428/// an ABI bump.
1429#[repr(C)]
1430#[derive(Clone, Copy)]
1431pub struct KuiColorToken {
1432    pub name: KuiStr,
1433    pub light: u32,
1434    pub dark: u32,
1435}
1436
1437/// One length token: a name and logical px, before the scale factor.
1438/// Array-carried like `KuiColorToken`.
1439#[repr(C)]
1440#[derive(Clone, Copy)]
1441pub struct KuiLengthToken {
1442    pub name: KuiStr,
1443    pub value: f32,
1444}
1445
1446/// One step of a derived colour token's recipe as `kui_tokens_derive`
1447/// reads it: the verb as one of the `KUI_OP_*` numbers, the number it
1448/// takes, and (for `KUI_OP_MIX` and `KUI_OP_READABLE` only) the colour
1449/// token or role the verb names, empty otherwise. Array-carried, so an
1450/// append here is an ABI bump.
1451#[repr(C)]
1452#[derive(Clone, Copy)]
1453pub struct KuiColorOp {
1454    pub op: u8,
1455    pub t: f32,
1456    pub other: KuiStr,
1457}
1458
1459/// One derived colour token: a name, the colour token or theme role it
1460/// derives from, and its chain of ops in order (none for an alias).
1461/// Array-carried like `KuiColorToken`.
1462#[repr(C)]
1463#[derive(Clone, Copy)]
1464pub struct KuiDerivedToken {
1465    pub name: KuiStr,
1466    pub from: KuiStr,
1467    pub ops: *const KuiColorOp,
1468    pub op_count: usize,
1469}
1470
1471/// The verbs of `KuiColorOp.op`, numbered as `kui_core::ColorOp::VERBS`
1472/// lists them.
1473pub const KUI_OP_LIFT: u8 = 0;
1474pub const KUI_OP_DARKEN: u8 = 1;
1475pub const KUI_OP_RAISE: u8 = 2;
1476pub const KUI_OP_ALPHA: u8 = 3;
1477pub const KUI_OP_MIX: u8 = 4;
1478pub const KUI_OP_READABLE: u8 = 5;
1479
1480/// Where a `kui_reply` from inside `kui_ext_on_event` sends its value: an
1481/// opaque handle the library puts on the event for the callback and takes
1482/// back when it returns. Never allocate, dereference or store one.
1483///
1484/// It carries a function pointer into the host's copy of this library, so
1485/// a plugin linked against a different copy (which a Windows DLL must be)
1486/// still reaches the host. `push` is cleared when the callback returns, so
1487/// a plugin that stored the event and replies later gets `false`.
1488#[repr(C)]
1489pub struct KuiReplySink {
1490    /// Called with the sink and the value; false if the sink is closed.
1491    pub(crate) push: Option<unsafe extern "C" fn(*mut KuiReplySink, *const KuiValue) -> bool>,
1492}
1493
1494/// One event from the UI, as `kui_poll_event` writes it and `kui_run`'s
1495/// `on_event` receives it.
1496///
1497/// `key` is the node that emitted it, `payload` the tag the node was
1498/// declared with plus what the event adds (its `kind`, a pointer
1499/// position, a key's `code`), `origin` 0 for the host's own nodes and an
1500/// extension's index otherwise, `window` which window it came from. An
1501/// `[out]` struct: start from `KUI_EVENT_INIT` so `size` is set.
1502#[repr(C)]
1503pub struct KuiEvent {
1504    /// Set to `sizeof(KuiEvent)` before the call (`KUI_EVENT_INIT` does);
1505    /// comes back as the number of bytes the library filled.
1506    pub size: u32,
1507    pub origin: u16,
1508    pub key: u64,
1509    /// Borrowed until the next `kui_poll_event`/`kui_ctx_free`; NULL if none.
1510    pub payload: *const KuiValue,
1511    /// Which window the event came from; 0 (`KUI_WINDOW_MAIN`) until a
1512    /// second one is opened. Appended in ABI 4; a host reserving the older
1513    /// layout gets a shorter write and never sees it.
1514    pub window: u32,
1515    /// Where `kui_reply` sends a reply to this event, or NULL when there is
1516    /// nowhere to send one (every event a host polls itself). A host has no
1517    /// use for it: it is the library's channel to a plugin, which passes
1518    /// the event straight back. See [`KuiReplySink`]. Appended in ABI 10.
1519    pub reply_sink: *mut KuiReplySink,
1520    /// The key of the slot whose fill drew the node (what `kui_key_of`
1521    /// answers for the slot's full name), or 0 for a node the host drew
1522    /// itself. Appended after `reply_sink` without a bump: a host
1523    /// reserving the older layout never sees it.
1524    pub slot: u64,
1525}
1526
1527impl Default for KuiEvent {
1528    fn default() -> Self {
1529        Self {
1530            size: std::mem::size_of::<Self>() as u32,
1531            origin: 0,
1532            key: 0,
1533            payload: std::ptr::null(),
1534            window: 0,
1535            reply_sink: std::ptr::null_mut(),
1536            slot: 0,
1537        }
1538    }
1539}
1540
1541// SAFETY: `repr(C)` with `size: u32` first.
1542unsafe impl OutParam for KuiEvent {
1543    /// Through `payload`: the last field ABI 1 shipped, and so still the
1544    /// floor now that `window` follows it.
1545    const ABI_V1_SIZE: u32 = abi_through!(KuiEvent, payload, *const KuiValue);
1546    fn size_mut(&mut self) -> &mut u32 {
1547        &mut self.size
1548    }
1549}
1550
1551/// What a declared window is (`kui_window_declare`), and what an `Open`
1552/// command carries back out inside [`KuiWindowCommand`]. Read literally,
1553/// so start from `KUI_WINDOW_CONFIG_INIT` (a normal, activating 640x480
1554/// window) or pass NULL for exactly that.
1555#[repr(C)]
1556#[derive(Clone, Copy, Default)]
1557pub struct KuiWindowConfig {
1558    /// `KUI_WINDOW_KIND_*`; 0 is a normal window.
1559    pub kind: u32,
1560    /// Initial inner size, logical px. Zero means the default.
1561    pub width: f32,
1562    pub height: f32,
1563    /// Whether opening it takes OS focus. Start a popup from
1564    /// `KUI_WINDOW_POPUP_INIT`, which clears it: a popup that takes focus
1565    /// blurs the field that opened it.
1566    pub activates: u32,
1567    /// `KUI_WINDOW_KIND_POPUP` only: the rect the popup is placed against,
1568    /// in the **declaring window's** logical coordinates — the `x`/`y`/`w`/
1569    /// `h` an `onLayout` event already reports for the field or button the
1570    /// menu belongs to. The host resolves it to screen coordinates against
1571    /// that window's own position. Ignored by a normal window, and read on
1572    /// the opening edge with the rest of the config.
1573    pub anchor_x: f32,
1574    pub anchor_y: f32,
1575    pub anchor_w: f32,
1576    pub anchor_h: f32,
1577}
1578
1579/// `KUI_WINDOW_KIND_NORMAL`: a regular top-level window.
1580pub const KUI_WINDOW_KIND_NORMAL: u32 = 0;
1581/// `KUI_WINDOW_KIND_POPUP`: a borderless, taskbar-less menu surface owned
1582/// by the window that declared it, placed against `anchor_*` in screen
1583/// coordinates and closed when its owner closes.
1584pub const KUI_WINDOW_KIND_POPUP: u32 = 1;
1585
1586pub(crate) fn window_config_of(c: Option<&KuiWindowConfig>) -> WindowConfig {
1587    let Some(c) = c else {
1588        return WindowConfig::default();
1589    };
1590    let size = if c.width > 0.0 && c.height > 0.0 {
1591        Size::new(c.width, c.height)
1592    } else {
1593        WindowConfig::DEFAULT_SIZE
1594    };
1595    WindowConfig {
1596        // A kind this build does not have reads as a normal window, so a
1597        // host built against a later header degrades to a window rather
1598        // than to nothing. `kui_window_declare` raises `unknown-window-kind`
1599        // on the way past, so the degradation is reported and not silent.
1600        kind: match c.kind {
1601            KUI_WINDOW_KIND_POPUP => WindowKind::Popup,
1602            _ => WindowKind::Normal,
1603        },
1604        size,
1605        activates: c.activates != 0,
1606        anchor: Rect::new(c.anchor_x, c.anchor_y, c.anchor_w, c.anchor_h),
1607    }
1608}
1609
1610/// How `kui_run_with` opens its window: the options a Rust host's
1611/// `Launcher` has, as one struct. Read literally, so start from
1612/// `KUI_RUN_CONFIG_INIT` (every zero) or pass NULL for exactly that: a
1613/// 960x640 native window, unbounded, antialiasing chosen by the GPU,
1614/// diagnostics as the build has them.
1615#[repr(C)]
1616#[derive(Clone, Copy, Default, Debug, PartialEq)]
1617pub struct KuiRunConfig {
1618    /// Initial inner size, logical px; zero is the default. Clamped into
1619    /// the bounds below, as the OS would. `KUI_WINDOW=WxH` in the
1620    /// environment still overrides.
1621    pub width: f32,
1622    pub height: f32,
1623    /// Smallest and largest inner size the user may resize to, logical
1624    /// px; a zero side is unbounded, so a lone `min_h` stands. A max
1625    /// below its min loses to it, as on the OS side.
1626    pub min_w: f32,
1627    pub min_h: f32,
1628    pub max_w: f32,
1629    pub max_h: f32,
1630    /// `KUI_CHROME_*`: native decorations, the custom titlebar a
1631    /// `kui_titlebar` draws, or none.
1632    pub chrome: u32,
1633    /// `KUI_TEXT_AA_*`; `KUI_TEXT_AA=gray|subpixel` in the environment
1634    /// still overrides, for an A/B by hand.
1635    pub text_aa: u32,
1636    /// `KUI_DIAG_*`: whether the core runs its checks and the runner
1637    /// prints them to stderr. The window's setting, over whatever
1638    /// `kui_set_diagnostics` set on the context handed in; the default is
1639    /// the build's — on in a debug build, off in release — as `kui_run`
1640    /// always had it.
1641    pub diagnostics: u32,
1642    /// Frames queued ahead of the one on screen; zero is the default.
1643    /// `KUI_FRAME_LATENCY` in the environment still overrides. ABI 19.
1644    pub frame_latency: u32,
1645    /// `KUI_BACKDROP_*`: what shows through the window's transparent
1646    /// pixels where the platform can (`Launcher::backdrop`); 0 is opaque.
1647    /// What the window got is `kui_ctx_backdrop`. ABI 26.
1648    pub backdrop: u32,
1649}
1650
1651/// `KUI_CHROME_NATIVE`: the OS's decorations.
1652pub const KUI_CHROME_NATIVE: u32 = 0;
1653/// `KUI_CHROME_CUSTOM`: undecorated; the view draws a `kui_titlebar` and
1654/// the runner synthesizes edge resizing and double-click maximize.
1655pub const KUI_CHROME_CUSTOM: u32 = 1;
1656/// `KUI_CHROME_BORDERLESS`: no decorations and no chrome expectations.
1657pub const KUI_CHROME_BORDERLESS: u32 = 2;
1658/// `KUI_TEXT_AA_AUTO`: LCD subpixel coverage when the GPU can blend per
1659/// channel, grayscale otherwise.
1660pub const KUI_TEXT_AA_AUTO: u32 = 0;
1661/// `KUI_TEXT_AA_GRAYSCALE`.
1662pub const KUI_TEXT_AA_GRAYSCALE: u32 = 1;
1663/// `KUI_TEXT_AA_SUBPIXEL`.
1664pub const KUI_TEXT_AA_SUBPIXEL: u32 = 2;
1665/// `KUI_DIAG_DEFAULT`: the build's — on in debug, off in release.
1666pub const KUI_DIAG_DEFAULT: u32 = 0;
1667/// `KUI_DIAG_ON`.
1668pub const KUI_DIAG_ON: u32 = 1;
1669/// `KUI_DIAG_OFF`.
1670pub const KUI_DIAG_OFF: u32 = 2;
1671
1672/// A `KuiRunConfig` read: what `kui_run_with` tells the launcher, in the
1673/// launcher's own terms. Separate from the launcher so the reading is a
1674/// test without a window.
1675#[cfg(any(feature = "runner", test))]
1676#[derive(Debug, PartialEq, Default)]
1677pub(crate) struct RunOptions {
1678    pub size: Option<(f64, f64)>,
1679    pub min_size: Option<(f64, f64)>,
1680    pub max_size: Option<(f64, f64)>,
1681    /// `KUI_CHROME_*`, checked.
1682    pub chrome: u32,
1683    /// `KUI_TEXT_AA_*`, checked.
1684    pub text_aa: u32,
1685    /// `None` is the build's default.
1686    pub diagnostics: Option<bool>,
1687    /// `None` is the launcher's default.
1688    pub frame_latency: Option<u32>,
1689    /// The backdrop asked for, checked.
1690    pub backdrop: kui_core::Backdrop,
1691}
1692
1693/// A max side left at zero is unbounded: a bound no display reaches, as
1694/// Node's `maxWidth` alone is.
1695#[cfg(any(feature = "runner", test))]
1696pub(crate) const UNBOUNDED_SIZE: f64 = 65_535.0;
1697
1698/// Reads a config the way `kui_window_declare` reads its own — literally,
1699/// NULL for the defaults — except that a word this build does not have is
1700/// refused with its reason rather than degraded: a window that opened
1701/// native when asked for the custom chrome would draw its titlebar under
1702/// the OS's.
1703#[cfg(any(feature = "runner", test))]
1704pub(crate) fn run_options_of(c: Option<&KuiRunConfig>) -> Result<RunOptions, String> {
1705    let Some(c) = c else {
1706        return Ok(RunOptions::default());
1707    };
1708    let side = |v: f32, what: &str| -> Result<f64, String> {
1709        if v.is_finite() && v >= 0.0 {
1710            Ok(f64::from(v))
1711        } else {
1712            Err(format!(
1713                "KuiRunConfig.{what} must be a finite, non-negative size, not {v}"
1714            ))
1715        }
1716    };
1717    let (w, h) = (side(c.width, "width")?, side(c.height, "height")?);
1718    let size =
1719        match (w > 0.0, h > 0.0) {
1720            (true, true) => Some((w, h)),
1721            (false, false) => None,
1722            _ => return Err(
1723                "KuiRunConfig: width and height go together (a min or max side may stand alone)"
1724                    .into(),
1725            ),
1726        };
1727    let (min_w, min_h) = (side(c.min_w, "min_w")?, side(c.min_h, "min_h")?);
1728    let min_size = (min_w > 0.0 || min_h > 0.0).then_some((min_w, min_h));
1729    let (max_w, max_h) = (side(c.max_w, "max_w")?, side(c.max_h, "max_h")?);
1730    let unbounded = |v: f64| if v > 0.0 { v } else { UNBOUNDED_SIZE };
1731    let max_size = (max_w > 0.0 || max_h > 0.0).then_some((unbounded(max_w), unbounded(max_h)));
1732    if c.chrome > KUI_CHROME_BORDERLESS {
1733        return Err(format!(
1734            "KuiRunConfig.chrome must be KUI_CHROME_NATIVE, KUI_CHROME_CUSTOM or KUI_CHROME_BORDERLESS, not {}",
1735            c.chrome
1736        ));
1737    }
1738    if c.text_aa > KUI_TEXT_AA_SUBPIXEL {
1739        return Err(format!(
1740            "KuiRunConfig.text_aa must be KUI_TEXT_AA_AUTO, KUI_TEXT_AA_GRAYSCALE or KUI_TEXT_AA_SUBPIXEL, not {}",
1741            c.text_aa
1742        ));
1743    }
1744    let diagnostics = match c.diagnostics {
1745        KUI_DIAG_DEFAULT => None,
1746        KUI_DIAG_ON => Some(true),
1747        KUI_DIAG_OFF => Some(false),
1748        other => {
1749            return Err(format!(
1750                "KuiRunConfig.diagnostics must be KUI_DIAG_DEFAULT, KUI_DIAG_ON or KUI_DIAG_OFF, not {other}"
1751            ));
1752        }
1753    };
1754    let Some(backdrop) = kui_core::Backdrop::from_code(c.backdrop) else {
1755        return Err(format!(
1756            "KuiRunConfig.backdrop must be KUI_BACKDROP_OPAQUE, KUI_BACKDROP_TRANSPARENT, KUI_BACKDROP_BLUR or KUI_BACKDROP_TINTED, not {}",
1757            c.backdrop
1758        ));
1759    };
1760    Ok(RunOptions {
1761        size,
1762        min_size,
1763        max_size,
1764        chrome: c.chrome,
1765        text_aa: c.text_aa,
1766        diagnostics,
1767        frame_latency: (c.frame_latency > 0).then_some(c.frame_latency),
1768        backdrop,
1769    })
1770}
1771
1772fn window_config_to_c(c: WindowConfig) -> KuiWindowConfig {
1773    KuiWindowConfig {
1774        kind: match c.kind {
1775            WindowKind::Normal => KUI_WINDOW_KIND_NORMAL,
1776            WindowKind::Popup => KUI_WINDOW_KIND_POPUP,
1777        },
1778        width: c.size.w,
1779        height: c.size.h,
1780        activates: c.activates as u32,
1781        anchor_x: c.anchor.x,
1782        anchor_y: c.anchor.y,
1783        anchor_w: c.anchor.w,
1784        anchor_h: c.anchor.h,
1785    }
1786}
1787
1788/// `KUI_DISMISS_OUTSIDE`, `KUI_DISMISS_ESCAPE`: why a window was asked to
1789/// go away (`kui_window_dismissed`).
1790pub const KUI_DISMISS_OUTSIDE: u32 = 0;
1791pub const KUI_DISMISS_ESCAPE: u32 = 1;
1792
1793/// `KUI_OPTION_AS_ALT_NONE`, `_LEFT`, `_RIGHT`, `_BOTH`: which Option
1794/// keys act as Alt on macOS (`kui_set_option_as_alt`).
1795pub const KUI_OPTION_AS_ALT_NONE: u32 = 0;
1796pub const KUI_OPTION_AS_ALT_LEFT: u32 = 1;
1797pub const KUI_OPTION_AS_ALT_RIGHT: u32 = 2;
1798pub const KUI_OPTION_AS_ALT_BOTH: u32 = 3;
1799
1800/// `KUI_CMD_START_DRAG`, `KUI_CMD_CLOSE`, `KUI_CMD_MINIMIZE`,
1801/// `KUI_CMD_TOGGLE_MAXIMIZE`: the verbs chrome nodes issue.
1802pub const KUI_CMD_START_DRAG: u32 = 1;
1803pub const KUI_CMD_CLOSE: u32 = 2;
1804pub const KUI_CMD_MINIMIZE: u32 = 3;
1805pub const KUI_CMD_TOGGLE_MAXIMIZE: u32 = 4;
1806/// `KUI_CMD_OPEN`: the declared set gained a window; `config` says what.
1807pub const KUI_CMD_OPEN: u32 = 5;
1808/// `KUI_CMD_SET_SIZE`: the app asked for a size (`kui_set_window_size`);
1809/// `width`/`height` carry it. `KUI_CMD_FOCUS`: it asked for focus.
1810pub const KUI_CMD_SET_SIZE: u32 = 6;
1811pub const KUI_CMD_FOCUS: u32 = 7;
1812/// `KUI_CMD_REDRAW`: draw `window` again, because another window's input
1813/// changed what it shows. A host that redraws every window on every event
1814/// may ignore it.
1815pub const KUI_CMD_REDRAW: u32 = 8;
1816
1817/// One window command (`kui_take_window_command`): what a chrome node
1818/// asked for, or what the declared window set's diff decided. Plain data
1819/// (an `Open` carries no title; the window's first frame declares one
1820/// through `kui_window_title`), so nothing borrowed enters a host's drain
1821/// loop. An `[out]` struct: start from `KUI_WINDOW_COMMAND_INIT`.
1822#[repr(C)]
1823pub struct KuiWindowCommand {
1824    /// Set to `sizeof(KuiWindowCommand)` before the call
1825    /// (`KUI_WINDOW_COMMAND_INIT` does); comes back as the bytes filled.
1826    pub size: u32,
1827    /// `KUI_CMD_*`.
1828    pub kind: u32,
1829    /// Which window: the one the chrome node was drawn in, or for
1830    /// `KUI_CMD_OPEN` the id the core assigned the new window — what its
1831    /// events will carry in `KuiEvent.window`.
1832    pub window: u32,
1833    /// `KUI_CMD_OPEN` only: which frontend's declaration won (0 = the host,
1834    /// 1+ = an extension), so a host can refuse an extension's window.
1835    pub origin: u16,
1836    /// `KUI_CMD_OPEN` only: the config from the declaration that opened it.
1837    pub config: KuiWindowConfig,
1838    /// `KUI_CMD_SET_SIZE` only: the size asked for, logical px. Appended in
1839    /// ABI 6; only the host's own `kui_set_window_size` produces the verb
1840    /// that fills them.
1841    pub width: f32,
1842    pub height: f32,
1843    /// `KUI_CMD_OPEN` only: the window whose frame declared this one. For a
1844    /// `KUI_WINDOW_KIND_POPUP` it is the owner — the surface `config`'s
1845    /// `anchor_*` is measured against, the one to parent it to, and the one
1846    /// whose closing closes it. (The core closes it either way: an owner's
1847    /// declarations leave the declared set with it, so the same drain
1848    /// carries the popup's `KUI_CMD_CLOSE`.)
1849    pub owner: u32,
1850}
1851
1852impl Default for KuiWindowCommand {
1853    fn default() -> Self {
1854        Self {
1855            size: std::mem::size_of::<Self>() as u32,
1856            kind: 0,
1857            window: 0,
1858            origin: 0,
1859            config: KuiWindowConfig::default(),
1860            width: 0.0,
1861            height: 0.0,
1862            owner: 0,
1863        }
1864    }
1865}
1866
1867// SAFETY: `repr(C)` with `size: u32` first.
1868unsafe impl OutParam for KuiWindowCommand {
1869    /// Through `config`: the whole struct as it first shipped, in ABI 5.
1870    /// ABI 6's `width`/`height` and ABI 7's `owner` sit past it, so the
1871    /// floor never moved for those — but ABI 7 also grew `KuiWindowConfig`
1872    /// itself, by the four anchor floats a popup is placed against, and a
1873    /// field appended *inside* an embedded struct moves everything after
1874    /// it. So the floor is 16 bytes higher than the whole ABI-6 struct was,
1875    /// an ABI-6 host's reservation is refused rather than short-written
1876    /// (`out_accepts`), and `kui_abi_version()` is what catches that before
1877    /// it looks like an empty queue. The size handshake bounds the damage;
1878    /// only the version check prevents it.
1879    const ABI_V1_SIZE: u32 = abi_through!(KuiWindowCommand, config, KuiWindowConfig);
1880    fn size_mut(&mut self) -> &mut u32 {
1881        &mut self.size
1882    }
1883}
1884
1885pub(crate) fn window_command_to_c(cmd: WindowCommand) -> KuiWindowCommand {
1886    let mut size = Size::new(0.0, 0.0);
1887    let mut owner = 0;
1888    let (kind, origin, config) = match cmd {
1889        WindowCommand::StartDrag(_) => (KUI_CMD_START_DRAG, 0, KuiWindowConfig::default()),
1890        WindowCommand::Close(_) => (KUI_CMD_CLOSE, 0, KuiWindowConfig::default()),
1891        WindowCommand::Minimize(_) => (KUI_CMD_MINIMIZE, 0, KuiWindowConfig::default()),
1892        WindowCommand::ToggleMaximize(_) => {
1893            (KUI_CMD_TOGGLE_MAXIMIZE, 0, KuiWindowConfig::default())
1894        }
1895        WindowCommand::Open {
1896            owner: o,
1897            origin,
1898            config,
1899            ..
1900        } => {
1901            owner = o.0;
1902            (KUI_CMD_OPEN, origin.0, window_config_to_c(config))
1903        }
1904        WindowCommand::SetSize { size: s, .. } => {
1905            size = s;
1906            (KUI_CMD_SET_SIZE, 0, KuiWindowConfig::default())
1907        }
1908        WindowCommand::Focus(_) => (KUI_CMD_FOCUS, 0, KuiWindowConfig::default()),
1909        WindowCommand::Redraw(_) => (KUI_CMD_REDRAW, 0, KuiWindowConfig::default()),
1910    };
1911    KuiWindowCommand {
1912        kind,
1913        window: cmd.window().0,
1914        origin,
1915        config,
1916        width: size.w,
1917        height: size.h,
1918        owner,
1919        ..Default::default()
1920    }
1921}
1922
1923/// One quad of a frame's draw list, in physical pixels: a rounded
1924/// rectangle, border, shadow, glyph, image or segment, told apart by
1925/// `kind`. A renderer draws them in order, every one with the same
1926/// instanced pipeline; `KuiDrawData` hands out the array.
1927#[repr(C)]
1928#[derive(Clone, Copy)]
1929pub struct KuiQuad {
1930    pub x: f32,
1931    pub y: f32,
1932    pub w: f32,
1933    pub h: f32,
1934    pub color: [f32; 4],
1935    pub border_color: [f32; 4],
1936    /// Corner radii (physical px), clockwise from the top-left.
1937    pub radius: [f32; 4],
1938    pub border_w: f32,
1939    /// KUI_QUAD_SHADOW only: blur radius (physical px), which is also how
1940    /// far `x`/`y`/`w`/`h` is inflated past the shape being blurred.
1941    pub blur: f32,
1942    /// KUI_QUAD_*
1943    pub kind: u32,
1944    /// Which entry of `KuiDrawData::clips` clips this quad; entry zero
1945    /// clips nothing.
1946    pub clip: u32,
1947    /// Atlas texels: x, y, w, h. `KUI_QUAD_SEGMENT`: the endpoints as
1948    /// float bits (see `kui_core::Quad::segment_ends`).
1949    pub uv: [u32; 4],
1950}
1951
1952/// One clip a frame's quads name, in physical pixels. Mirrors
1953/// `kui_core::Clip`, so `KuiDrawData::clips` is a cast and not a copy.
1954#[repr(C)]
1955#[derive(Clone, Copy)]
1956pub struct KuiClip {
1957    /// Clip rect: x, y, w, h. Pixels outside are transparent.
1958    pub rect: [f32; 4],
1959    /// Corner radii (physical px), clockwise from the top-left: pixels
1960    /// outside the rounded clip are transparent too. All zero — every clip
1961    /// of a frame with no rounded clipper — is the plain rect clip.
1962    pub radius: [f32; 4],
1963    /// The turn every quad naming this entry is drawn through (ADR
1964    /// 0043): angle in radians (clockwise, y down), scale, tx, ty —
1965    /// `pixel = R(angle) · scale · p + (tx, ty)` for `p` a point of the
1966    /// quad's own rect. `{0, 1, 0, 0}`, the identity, on every entry of a
1967    /// frame that turns nothing. ABI 27.
1968    pub transform: [f32; 4],
1969    /// A second clip in the quad's own space, before the turn: x, y, w,
1970    /// h, from clipping nodes inside a turned subtree. The clip that clips
1971    /// nothing (a rect past any pixel) when nothing inside a turn clips.
1972    pub inner: [f32; 4],
1973    /// `inner`'s corner radii, as `radius` is `rect`'s.
1974    pub inner_radius: [f32; 4],
1975}
1976
1977/// One `KUI_QUAD_FRAGMENT`'s draw, addressed by that quad's `uv[0]`.
1978#[repr(C)]
1979#[derive(Clone, Copy)]
1980pub struct KuiFragmentDraw {
1981    /// The registered handle, for `kui_fragment_source` and for keying a
1982    /// renderer's pipeline cache.
1983    pub fragment: u64,
1984    /// What the node declared, zero-padded to sixteen.
1985    pub params: [f32; 16],
1986    /// Where the draw's `image` is: `KUI_FRAGMENT_IMAGE_NONE`,
1987    /// `_ATLAS` (bind the atlas, as for any fragment) or `_TEXTURE` (bind
1988    /// the texture `image_texture` names, as for a `KUI_QUAD_TEXTURE`
1989    /// quad). Added in ABI 15.
1990    pub image_source: u32,
1991    /// The `textures` index the draw reads, when `image_source` is
1992    /// `KUI_FRAGMENT_IMAGE_TEXTURE`; 0 otherwise.
1993    pub image_texture: u32,
1994    /// The texel rect the shader is given as `FragmentIn::image`, in the
1995    /// atlas or in that texture; zero with no image.
1996    pub image_uv: [u32; 4],
1997}
1998
1999/// `KuiFragmentDraw::image_source`: no image.
2000pub const KUI_FRAGMENT_IMAGE_NONE: u32 = 0;
2001/// `KuiFragmentDraw::image_source`: the image is in the atlas.
2002pub const KUI_FRAGMENT_IMAGE_ATLAS: u32 = 1;
2003/// `KuiFragmentDraw::image_source`: the image has a texture of its own.
2004pub const KUI_FRAGMENT_IMAGE_TEXTURE: u32 = 2;
2005
2006/// One `KUI_QUAD_TEXTURE`'s draw, addressed by that quad's `uv[0]`.
2007#[repr(C)]
2008#[derive(Clone, Copy)]
2009pub struct KuiTextureDraw {
2010    /// The image handle, for `kui_image_pixels` and for keying a
2011    /// renderer's texture cache.
2012    pub image: u64,
2013    /// Moves with every `kui_image_update`; a renderer that uploaded this
2014    /// revision has nothing to do.
2015    pub rev: u32,
2016    pub width: u32,
2017    pub height: u32,
2018    /// The texel rect to show, `[x, y, w, h]` in the image's own texels —
2019    /// the whole image, or the crop a `fit = cover` made.
2020    pub uv: [u32; 4],
2021}
2022
2023/// The finished frame's draw list, as `kui_draw_data` writes it: the
2024/// quads, the clips they index, the glyph atlas to mirror as a texture,
2025/// and the side lists for fragments and texture-backed images.
2026///
2027/// Everything is in physical pixels. The pointers are valid until the
2028/// next `kui_frame_begin` on the context. An `[out]` struct: start from
2029/// `KUI_DRAW_DATA_INIT`.
2030#[repr(C)]
2031pub struct KuiDrawData {
2032    /// `[out]` reservation; see `KUI_DRAW_DATA_INIT`.
2033    pub size: u32,
2034    pub quads: *const KuiQuad,
2035    pub quad_count: usize,
2036    pub viewport_w: f32,
2037    pub viewport_h: f32,
2038    pub scale: f32,
2039    /// RGBA, atlas_size * atlas_size * 4 bytes.
2040    pub atlas_pixels: *const u8,
2041    /// Can grow or shrink between frames: a page extended for one frame
2042    /// goes back to its size at the next. Size the texture to it, not to
2043    /// the largest seen.
2044    pub atlas_size: u32,
2045    /// Re-upload the atlas texture when either of these changes/sets.
2046    pub atlas_dirty: bool,
2047    pub atlas_epoch: u64,
2048    /// One per `KUI_QUAD_FRAGMENT` quad, indexed by its `uv[0]`; null and
2049    /// zero on a frame that draws none. Added in ABI 9.
2050    pub fragments: *const KuiFragmentDraw,
2051    pub fragment_count: usize,
2052    /// The frame clock in seconds, for a fragment's `time`.
2053    pub time: f32,
2054    /// The clips the quads index through `KuiQuad::clip`. Never empty on a
2055    /// frame that drew anything: entry zero clips nothing. Added in
2056    /// ABI 11.
2057    pub clips: *const KuiClip,
2058    pub clip_count: usize,
2059    /// One per `KUI_QUAD_TEXTURE` quad, indexed by its `uv[0]`; null and
2060    /// zero on a frame that draws none. Added in ABI 14.
2061    pub textures: *const KuiTextureDraw,
2062    pub texture_count: usize,
2063}
2064
2065impl Default for KuiDrawData {
2066    fn default() -> Self {
2067        Self {
2068            size: std::mem::size_of::<Self>() as u32,
2069            quads: std::ptr::null(),
2070            quad_count: 0,
2071            viewport_w: 0.0,
2072            viewport_h: 0.0,
2073            scale: 1.0,
2074            atlas_pixels: std::ptr::null(),
2075            atlas_size: 0,
2076            atlas_dirty: false,
2077            atlas_epoch: 0,
2078            fragments: std::ptr::null(),
2079            fragment_count: 0,
2080            time: 0.0,
2081            clips: std::ptr::null(),
2082            clip_count: 0,
2083            textures: std::ptr::null(),
2084            texture_count: 0,
2085        }
2086    }
2087}
2088
2089// SAFETY: `repr(C)` with `size: u32` first.
2090unsafe impl OutParam for KuiDrawData {
2091    const ABI_V1_SIZE: u32 = abi_through!(KuiDrawData, atlas_epoch, u64);
2092    fn size_mut(&mut self) -> &mut u32 {
2093        &mut self.size
2094    }
2095}
2096
2097/// What the last layout resolved for a scroll container (`kui_scroll_geometry`):
2098/// its own box, its content size and the clamped offset, all logical px in
2099/// viewport coordinates.
2100#[repr(C)]
2101#[derive(Clone, Copy)]
2102pub struct KuiScrollGeometry {
2103    /// `[out]` reservation; see `KUI_SCROLL_GEOMETRY_INIT`.
2104    pub size: u32,
2105    /// The container's box, as the last layout placed and sized it.
2106    pub x: f32,
2107    pub y: f32,
2108    pub w: f32,
2109    pub h: f32,
2110    /// Its laid-out content, padding included.
2111    pub content_w: f32,
2112    pub content_h: f32,
2113    /// Where it is scrolled to: the retained offset clamped to the travel
2114    /// below, so it is always a position within the content.
2115    pub offset_x: f32,
2116    pub offset_y: f32,
2117    /// How far the offset can travel; zero on an axis that does not scroll.
2118    pub max_offset_x: f32,
2119    pub max_offset_y: f32,
2120}
2121
2122impl Default for KuiScrollGeometry {
2123    fn default() -> Self {
2124        Self {
2125            size: std::mem::size_of::<Self>() as u32,
2126            x: 0.0,
2127            y: 0.0,
2128            w: 0.0,
2129            h: 0.0,
2130            content_w: 0.0,
2131            content_h: 0.0,
2132            offset_x: 0.0,
2133            offset_y: 0.0,
2134            max_offset_x: 0.0,
2135            max_offset_y: 0.0,
2136        }
2137    }
2138}
2139
2140/// One cell of a `kui_cells` grid: a Unicode scalar, colours as
2141/// `0xRRGGBBAA` (a `bg` of 0 is none), `KUI_CELL_*` attribute bits.
2142/// Travels as an array, so a change here is an ABI bump.
2143#[repr(C)]
2144#[derive(Clone, Copy, Default)]
2145pub struct KuiCell {
2146    pub ch: u32,
2147    pub fg: u32,
2148    pub bg: u32,
2149    pub flags: u32,
2150    /// The underline's own colour (SGR 58), 0 for `fg`. ABI 17.
2151    pub ul: u32,
2152}
2153
2154/// What choosing a context-menu row left for the host
2155/// (`kui_take_menu_action`): the clipboard, which is the host's in this
2156/// library. `KUI_MENU_ACTION_SET_CLIPBOARD` carries the text to put there;
2157/// `KUI_MENU_ACTION_PASTE` carries nothing and asks for what is there,
2158/// which the host delivers back with `kui_input_paste` (or
2159/// `kui_input_commit`); `KUI_MENU_ACTION_SET_CLIPBOARD_SECRET` carries a
2160/// secret to put there marked concealed and transient.
2161#[repr(C)]
2162#[derive(Clone, Copy)]
2163pub struct KuiMenuAction {
2164    /// `[out]` reservation; see `KUI_MENU_ACTION_INIT`.
2165    pub size: u32,
2166    /// A `KUI_MENU_ACTION_*` kind.
2167    pub kind: u32,
2168    /// Borrowed until the next `kui_take_menu_action` on this context.
2169    pub text: KuiStr,
2170    /// The same selection with the formatting the core knows about, for a
2171    /// host offering a second clipboard flavour.
2172    /// Empty when there is none to carry — and never a *replacement* for
2173    /// `text`: a clipboard whose only flavour is HTML pastes markup into
2174    /// every plain-text field on the machine.
2175    pub html: KuiStr,
2176    /// `KUI_MENU_ACTION_LOOK_UP` only: where to anchor the panel — the
2177    /// baseline origin of the selection's first line, logical viewport
2178    /// px. Zero for every other kind.
2179    pub x: f32,
2180    pub y: f32,
2181}
2182
2183impl Default for KuiMenuAction {
2184    fn default() -> Self {
2185        Self {
2186            size: std::mem::size_of::<Self>() as u32,
2187            kind: 0,
2188            text: KuiStr {
2189                ptr: std::ptr::null(),
2190                len: 0,
2191            },
2192            html: KuiStr {
2193                ptr: std::ptr::null(),
2194                len: 0,
2195            },
2196            x: 0.0,
2197            y: 0.0,
2198        }
2199    }
2200}
2201
2202// SAFETY: `repr(C)` with `size: u32` first.
2203unsafe impl OutParam for KuiMenuAction {
2204    const ABI_V1_SIZE: u32 = abi_through!(KuiMenuAction, y, f32);
2205    fn size_mut(&mut self) -> &mut u32 {
2206        &mut self.size
2207    }
2208}
2209
2210/// Where a point landed in the text a keyed node drew (`kui_text_hit`):
2211/// a byte offset into that text, across the node's text runs in order,
2212/// and the visual (wrapped) line it is on.
2213#[repr(C)]
2214#[derive(Clone, Copy)]
2215pub struct KuiTextHit {
2216    /// `[out]` reservation; see `KUI_TEXT_HIT_INIT`.
2217    pub size: u32,
2218    pub line: u32,
2219    pub byte: u64,
2220}
2221
2222impl Default for KuiTextHit {
2223    fn default() -> Self {
2224        Self {
2225            size: std::mem::size_of::<Self>() as u32,
2226            line: 0,
2227            byte: 0,
2228        }
2229    }
2230}
2231
2232// SAFETY: `repr(C)` with `size: u32` first.
2233unsafe impl OutParam for KuiTextHit {
2234    const ABI_V1_SIZE: u32 = abi_through!(KuiTextHit, byte, u64);
2235    fn size_mut(&mut self) -> &mut u32 {
2236        &mut self.size
2237    }
2238}
2239
2240/// The rect a node was laid out at (`kui_layout_of`): logical px in
2241/// viewport coordinates, the `layout` event's numbers without the event.
2242#[repr(C)]
2243#[derive(Clone, Copy)]
2244pub struct KuiLayoutRect {
2245    /// `[out]` reservation; see `KUI_LAYOUT_RECT_INIT`.
2246    pub size: u32,
2247    pub x: f32,
2248    pub y: f32,
2249    pub w: f32,
2250    pub h: f32,
2251}
2252
2253impl Default for KuiLayoutRect {
2254    fn default() -> Self {
2255        Self {
2256            size: std::mem::size_of::<Self>() as u32,
2257            x: 0.0,
2258            y: 0.0,
2259            w: 0.0,
2260            h: 0.0,
2261        }
2262    }
2263}
2264
2265// SAFETY: `repr(C)` with `size: u32` first.
2266unsafe impl OutParam for KuiLayoutRect {
2267    const ABI_V1_SIZE: u32 = abi_through!(KuiLayoutRect, h, f32);
2268    fn size_mut(&mut self) -> &mut u32 {
2269        &mut self.size
2270    }
2271}
2272
2273/// A caret rect (`kui_caret_rect`): logical px in viewport coordinates,
2274/// zero wide, one line tall.
2275#[repr(C)]
2276#[derive(Clone, Copy)]
2277pub struct KuiCaretRect {
2278    /// `[out]` reservation; see `KUI_CARET_RECT_INIT`.
2279    pub size: u32,
2280    pub x: f32,
2281    pub y: f32,
2282    pub w: f32,
2283    pub h: f32,
2284}
2285
2286impl Default for KuiCaretRect {
2287    fn default() -> Self {
2288        Self {
2289            size: std::mem::size_of::<Self>() as u32,
2290            x: 0.0,
2291            y: 0.0,
2292            w: 0.0,
2293            h: 0.0,
2294        }
2295    }
2296}
2297
2298// SAFETY: `repr(C)` with `size: u32` first.
2299unsafe impl OutParam for KuiCaretRect {
2300    const ABI_V1_SIZE: u32 = abi_through!(KuiCaretRect, h, f32);
2301    fn size_mut(&mut self) -> &mut u32 {
2302        &mut self.size
2303    }
2304}
2305
2306// SAFETY: `repr(C)` with `size: u32` first.
2307unsafe impl OutParam for KuiScrollGeometry {
2308    const ABI_V1_SIZE: u32 = abi_through!(KuiScrollGeometry, max_offset_y, f32);
2309    fn size_mut(&mut self) -> &mut u32 {
2310        &mut self.size
2311    }
2312}
2313
2314// ---------------------------------------------------------------------------
2315// The plain-constant enums the header spells and the entry points read.
2316//
2317// Each of these used to be a literal at the one site that read it (`kind =
2318// 3`, `flags & 8`), with the header the only place the number had a name.
2319// Named here so `mod abi_parity` can pin every one of them to the header
2320// by name, the way it pins `KUI_CMD_*`: a value renumbered on either side
2321// fails the C build instead of meaning something else at runtime.
2322
2323/// `KUI_SPAN_*`: the flags on a `KuiSpan`.
2324pub const KUI_SPAN_BOLD: u32 = 1 << 0;
2325pub const KUI_SPAN_ITALIC: u32 = 1 << 1;
2326pub const KUI_SPAN_UNDERLINE: u32 = 1 << 2;
2327pub const KUI_SPAN_STRIKETHROUGH: u32 = 1 << 3;
2328/// `KUI_SPAN_FAMILY`: `KuiSpan.family` names the span's face (ABI 26).
2329pub const KUI_SPAN_FAMILY: u32 = 1 << 4;
2330
2331/// `KUI_UNDERLINE_*`: an underline's shape, `KuiTextStyle.underline_style`
2332/// and `KuiSpan.underline_style`.
2333pub const KUI_UNDERLINE_SOLID: u32 = 0;
2334pub const KUI_UNDERLINE_WAVY: u32 = 1;
2335pub const KUI_UNDERLINE_DOTTED: u32 = 2;
2336
2337/// `KUI_KMOD_*`: the modifier bits `kui_input_key_down` and its siblings
2338/// take, and `kui_input_modifiers` reports — the core's own
2339/// `KeyMods::bits`, which is also what the conformance corpus's
2340/// `modifiers` step spells, so the header and the corpus cannot drift.
2341pub const KUI_KMOD_SHIFT: u32 = kui_core::KeyMods::SHIFT;
2342pub const KUI_KMOD_CTRL: u32 = kui_core::KeyMods::CTRL;
2343pub const KUI_KMOD_ALT: u32 = kui_core::KeyMods::ALT;
2344pub const KUI_KMOD_SUPER: u32 = kui_core::KeyMods::SUPER;
2345
2346/// `KUI_KLOCK_*` and `KUI_KLOC_*`: which lock keys were on and which of a
2347/// key's twins it was, in the same word as the `KUI_KMOD_*` bits
2348/// `kui_input_key_down` and its siblings take. Zero is no lock on and the
2349/// standard key.
2350pub const KUI_KLOCK_CAPS: u32 = kui_core::KeyLocks::CAPS;
2351pub const KUI_KLOCK_NUM: u32 = kui_core::KeyLocks::NUM;
2352pub const KUI_KLOC_LEFT: u32 = 1 << kui_core::KeyLocation::SHIFT;
2353pub const KUI_KLOC_RIGHT: u32 = 2 << kui_core::KeyLocation::SHIFT;
2354pub const KUI_KLOC_NUMPAD: u32 = 3 << kui_core::KeyLocation::SHIFT;
2355/// `KUI_KLAYOUT_NONLATIN`: the press was typed on a layout that writes no
2356/// Latin, so the US key stands in for its ASCII too. Zero judges each key
2357/// by itself.
2358pub const KUI_KLAYOUT_NONLATIN: u32 = kui_core::LayoutScript::NON_LATIN;
2359
2360/// `KUI_EDIT_*`: the flags `kui_text_edit` takes. `WRAP` is the `wrap`
2361/// row declared on a field (the mode is `KuiTextStyle.wrap`, whose zero
2362/// is `KUI_WRAP_WORD`, so the style alone cannot say): the field folds to
2363/// its width the way a document does and keeps a field's keyboard.
2364pub const KUI_EDIT_MULTILINE: u32 = 1 << 0;
2365pub const KUI_EDIT_AUTOFOCUS: u32 = 1 << 1;
2366pub const KUI_EDIT_WRAP: u32 = 1 << 2;
2367
2368/// `KUI_MOD_*`: the editing modifiers `kui_input_key` takes — extend the
2369/// selection, move by word, move by document.
2370pub const KUI_MOD_SHIFT: u32 = 1 << 0;
2371pub const KUI_MOD_WORD: u32 = 1 << 1;
2372pub const KUI_MOD_DOC: u32 = 1 << 2;
2373
2374/// `KUI_KEY_*`: the editing keys `kui_input_key` takes, in the header's
2375/// order — which is not `EditKey`'s declaration order, so the table is
2376/// the pin rather than a cast. `edit_key_of` reads it and `mod
2377/// abi_parity` emits it.
2378pub const KUI_EDIT_KEYS: [(&str, EditKey); 16] = [
2379    ("KUI_KEY_LEFT", EditKey::Left),
2380    ("KUI_KEY_RIGHT", EditKey::Right),
2381    ("KUI_KEY_UP", EditKey::Up),
2382    ("KUI_KEY_DOWN", EditKey::Down),
2383    ("KUI_KEY_HOME", EditKey::Home),
2384    ("KUI_KEY_END", EditKey::End),
2385    ("KUI_KEY_PAGE_UP", EditKey::PageUp),
2386    ("KUI_KEY_PAGE_DOWN", EditKey::PageDown),
2387    ("KUI_KEY_BACKSPACE", EditKey::Backspace),
2388    ("KUI_KEY_DELETE", EditKey::Delete),
2389    ("KUI_KEY_ENTER", EditKey::Enter),
2390    ("KUI_KEY_TAB", EditKey::Tab),
2391    ("KUI_KEY_SELECT_ALL", EditKey::SelectAll),
2392    ("KUI_KEY_ESCAPE", EditKey::Escape),
2393    ("KUI_KEY_UNDO", EditKey::Undo),
2394    ("KUI_KEY_REDO", EditKey::Redo),
2395];
2396
2397/// `KUI_MOUSE_*`: `kui_input_mouse_button`'s button, which is
2398/// `MouseButton::code` — the core owns the numbering, this is its name.
2399pub const KUI_MOUSE_PRIMARY: u32 = 0;
2400pub const KUI_MOUSE_SECONDARY: u32 = 1;
2401pub const KUI_MOUSE_MIDDLE: u32 = 2;
2402pub const KUI_MOUSE_OTHER: u32 = 3;
2403
2404/// `KUI_MENU_*`: a `KuiMenuItem.role`, the position in `MenuRole::ALL`.
2405pub const KUI_MENU_CUSTOM: u32 = 0;
2406pub const KUI_MENU_SEPARATOR: u32 = 1;
2407pub const KUI_MENU_CUT: u32 = 2;
2408pub const KUI_MENU_COPY: u32 = 3;
2409pub const KUI_MENU_PASTE: u32 = 4;
2410pub const KUI_MENU_SELECT_ALL: u32 = 5;
2411pub const KUI_MENU_LOOK_UP: u32 = 6;
2412
2413/// `KUI_MENU_ITEM_*`: the flags `kui_menu_bar_item` and `kui_menu_item`
2414/// report on a row.
2415pub const KUI_MENU_ITEM_ENABLED: u32 = 1 << 0;
2416pub const KUI_MENU_ITEM_CHECKED: u32 = 1 << 1;
2417/// `KUI_MENU_ITEM_SUBMENU`: the row opens rows of its own (backlog F128).
2418pub const KUI_MENU_ITEM_SUBMENU: u32 = 1 << 2;
2419
2420/// `KUI_MENU_ACTION_*`: a `KuiMenuAction.kind`.
2421pub const KUI_MENU_ACTION_SET_CLIPBOARD: u32 = 0;
2422pub const KUI_MENU_ACTION_PASTE: u32 = 1;
2423pub const KUI_MENU_ACTION_LOOK_UP: u32 = 2;
2424/// A secret for the clipboard, to write marked concealed and transient
2425/// (`kui_set_clipboard_secret`). A host that does not know the kind drops
2426/// the copy, which for a secret is the safe way to fail.
2427pub const KUI_MENU_ACTION_SET_CLIPBOARD_SECRET: u32 = 3;
2428
2429/// `KUI_PASTE_*`: the pasteboard's markers on a paste's answer
2430/// (`kui_input_paste`).
2431pub const KUI_PASTE_CONCEALED: u32 = 1 << 0;
2432pub const KUI_PASTE_TRANSIENT: u32 = 1 << 1;
2433
2434/// `KUI_BUTTONS_*`: the buttons `KuiSpec.on_button` claims
2435/// (`KuiSpec.buttons`); none set is all three.
2436pub const KUI_BUTTONS_SECONDARY: u32 = kui_core::Buttons::SECONDARY.bits();
2437pub const KUI_BUTTONS_MIDDLE: u32 = kui_core::Buttons::MIDDLE.bits();
2438pub const KUI_BUTTONS_OTHER: u32 = kui_core::Buttons::OTHER.bits();
2439
2440/// `KUI_OWED_*`: the bits of what `kui_owed` returns, `kui_animating` by
2441/// kind.
2442pub const KUI_OWED_TRANSITION: u32 = 1 << 0;
2443pub const KUI_OWED_CYCLE: u32 = 1 << 1;
2444pub const KUI_OWED_DEPART: u32 = 1 << 2;
2445pub const KUI_OWED_REQUESTED: u32 = 1 << 3;
2446pub const KUI_OWED_AUTOSCROLL: u32 = 1 << 4;
2447pub const KUI_OWED_SCROLL: u32 = 1 << 5;
2448
2449/// `KUI_FRAME_CAUSE_*`: the bits of what `kui_frame_cause` returns and
2450/// `kui_note_frame_cause` takes.
2451pub const KUI_FRAME_CAUSE_POINTER_MOVE: u32 = kui_core::FrameCause::POINTER_MOVE.bits();
2452pub const KUI_FRAME_CAUSE_POINTER_LEAVE: u32 = kui_core::FrameCause::POINTER_LEAVE.bits();
2453pub const KUI_FRAME_CAUSE_BUTTON: u32 = kui_core::FrameCause::BUTTON.bits();
2454pub const KUI_FRAME_CAUSE_WHEEL: u32 = kui_core::FrameCause::WHEEL.bits();
2455pub const KUI_FRAME_CAUSE_KEY: u32 = kui_core::FrameCause::KEY.bits();
2456pub const KUI_FRAME_CAUSE_MODIFIERS: u32 = kui_core::FrameCause::MODIFIERS.bits();
2457pub const KUI_FRAME_CAUSE_TEXT: u32 = kui_core::FrameCause::TEXT.bits();
2458pub const KUI_FRAME_CAUSE_PREEDIT: u32 = kui_core::FrameCause::PREEDIT.bits();
2459pub const KUI_FRAME_CAUSE_ACCESS: u32 = kui_core::FrameCause::ACCESS.bits();
2460pub const KUI_FRAME_CAUSE_FILE_DRAG: u32 = kui_core::FrameCause::FILE_DRAG.bits();
2461pub const KUI_FRAME_CAUSE_FILES: u32 = kui_core::FrameCause::FILES.bits();
2462pub const KUI_FRAME_CAUSE_FIRST: u32 = kui_core::FrameCause::FIRST.bits();
2463pub const KUI_FRAME_CAUSE_WAKE: u32 = kui_core::FrameCause::WAKE.bits();
2464pub const KUI_FRAME_CAUSE_HOST: u32 = kui_core::FrameCause::HOST.bits();
2465pub const KUI_FRAME_CAUSE_RESIZE: u32 = kui_core::FrameCause::RESIZE.bits();
2466pub const KUI_FRAME_CAUSE_SCALE: u32 = kui_core::FrameCause::SCALE.bits();
2467pub const KUI_FRAME_CAUSE_FOCUS: u32 = kui_core::FrameCause::FOCUS.bits();
2468pub const KUI_FRAME_CAUSE_OCCLUSION: u32 = kui_core::FrameCause::OCCLUSION.bits();
2469pub const KUI_FRAME_CAUSE_APPEARANCE: u32 = kui_core::FrameCause::APPEARANCE.bits();
2470pub const KUI_FRAME_CAUSE_CARET: u32 = kui_core::FrameCause::CARET.bits();
2471pub const KUI_FRAME_CAUSE_RETRY: u32 = kui_core::FrameCause::RETRY.bits();
2472pub const KUI_FRAME_CAUSE_OVERDUE: u32 = kui_core::FrameCause::OVERDUE.bits();
2473pub const KUI_FRAME_CAUSE_DEVICE: u32 = kui_core::FrameCause::DEVICE.bits();
2474pub const KUI_FRAME_CAUSE_AFTER_FRAME: u32 = kui_core::FrameCause::AFTER_FRAME.bits();
2475pub const KUI_FRAME_CAUSE_ELSEWHERE: u32 = kui_core::FrameCause::ELSEWHERE.bits();
2476pub const KUI_FRAME_CAUSE_MENU: u32 = kui_core::FrameCause::MENU.bits();
2477pub const KUI_FRAME_CAUSE_AUDIO: u32 = kui_core::FrameCause::AUDIO.bits();
2478pub const KUI_FRAME_CAUSE_SMOKE: u32 = kui_core::FrameCause::SMOKE.bits();
2479pub const KUI_FRAME_CAUSE_OWED: u32 = kui_core::FrameCause::OWED.bits();
2480
2481/// `KUI_COPY_*`: what `kui_request_copy` returns.
2482pub const KUI_COPY_READY: u32 = 0;
2483pub const KUI_COPY_ASKED: u32 = 1;
2484pub const KUI_COPY_NOTHING: u32 = 2;
2485
2486/// `KUI_AUDIO_*`: a `KuiAudioCommand.kind`.
2487pub const KUI_AUDIO_PLAY: u32 = 1;
2488pub const KUI_AUDIO_STOP: u32 = 2;
2489pub const KUI_AUDIO_SET_VOLUME: u32 = 3;
2490pub const KUI_AUDIO_PAUSE: u32 = 4;
2491pub const KUI_AUDIO_RESUME: u32 = 5;
2492pub const KUI_AUDIO_MASTER_VOLUME: u32 = 6;
2493pub const KUI_AUDIO_UNLOAD: u32 = 7;
2494
2495/// `KUI_WINDOW_NONE`: a `KuiSpec.window_role` that is no chrome role.
2496pub const KUI_WINDOW_NONE: u32 = 0;