Expand description
Denise’s C ABI: a cdylib for hosts that are not written in Rust.
Denise’s own backends are Rust and need none of this. This crate exists for
the other direction — a Win32 control inside an MFC application, a WinForms or
VB6 host reaching it through the ActiveX shim, an NSView in a Cocoa app, a
Python or C# panel on an embedded box. All of them speak C.
§The shape of it
The host owns the window and the pixel buffer; Denise owns the widget tree and draws into whatever it is handed:
DeniseUi *ui = denise_ui_new(800, 480, DENISE_THEME_DARK);
uint64_t root = denise_ui_root(ui);
denise_ui_add_button(ui, root, (DeniseRect){20, 20, 160, 44}, "Save", 1, DENISE_ROLE_PRIMARY);
/* per frame */
denise_ui_tick(ui, now_ms);
if (denise_ui_needs_paint(ui)) {
DeniseFrame frame = { pixels, len, w, h, stride, DENISE_FORMAT_XRGB8888, age };
denise_ui_paint(ui, &frame);
DeniseRect damage[16];
intptr_t n = denise_ui_damage(ui, damage, 16);
/* BitBlt only those rectangles */
denise_ui_presented(ui);
}
uint32_t message;
while (denise_ui_poll_message(ui, &message)) { /* ... */ }There is no Surface here and no event loop. Both belong to the host, and a
library that tried to own either would be unembeddable in exactly the places
this is for.
§Rules the whole ABI keeps
- Handles are opaque. A
DeniseUi *comes fromdenise_ui_newand goes todenise_ui_free. Nothing else may free it. - A node is a
uint64_t, and0is never a valid node. Ids carry a generation, so an id kept past adenise_ui_removefails to resolve rather than addressing whoever took the slot. - A message is a
uint32_t, chosen by the host, and0means no message. A button given0emits nothing, anddenise_ui_poll_messagenever yields it. That is what lets a widget be created without one. - Strings are NUL-terminated UTF-8 going in and coming out. Invalid UTF-8
is
DENISE_ERR_INVALID, not a replacement character: silently mangling a host’s text is worse than refusing it. - A negative return is a status, and every status has a message from
denise_status_message. - Nothing is thread-safe. One
DeniseUibelongs to one thread, which for every host this targets is the UI thread it was created on.
§Panics do not cross
Every entry point catches unwinding and returns DENISE_ERR_PANIC. A panic
is a bug in Denise, and a bug in Denise should not take down a host process
that has unsaved work in three other windows. The call did nothing; the Ui
it was called on should be treated as suspect and freed.
§The header is the contract
include/denise.h is written by hand, not generated. A generated header
follows whatever the Rust happens to say this week, which is the opposite of
what a stable ABI means — the header is the thing that must not move, and the
Rust is what gets checked against it. tests/header.rs
does the checking: every exported symbol appears in both, with the same
numbers for every key, role and constant.
Re-exports§
Modules§
- input
- Feeding host input into the tree.
- keys
- Key positions, as numbers a C caller can hold.
- paint
- Drawing into a buffer the host owns.
- tree
- Building and mutating the widget tree from C.
- types
- The
#[repr(C)]half of the ABI, and the numbers either side of it.
Structs§
- Denise
Ui - An opaque user interface. Create with
denise_ui_new, destroy withdenise_ui_free.
Constants§
- DENISE_
ABI_ VERSION - Version of this ABI.
- DENISE_
ERR_ BUFFER_ TOO_ SMALL - The buffer supplied is too small for the result.
- DENISE_
ERR_ INVALID - An argument was out of range, or a string was not valid UTF-8.
- DENISE_
ERR_ NO_ NODE - The node id does not name a live node.
- DENISE_
ERR_ NULL - A required pointer was
NULL. - DENISE_
ERR_ PANIC - A panic was caught. The call did nothing; treat the
DeniseUias suspect. - DENISE_
ERR_ WRONG_ WIDGET - The widget named is not of the kind this call needs.
- DENISE_
OK - The call succeeded.
Functions§
- denise_
abi_ version - The ABI version this library was built with. See
DENISE_ABI_VERSION. - denise_
status_ message - A short description of a status code, NUL-terminated. Never
NULL, never freed. An unrecognised code gets a generic message rather thanNULL, because a host logging an error should not have to handle an error from the logger. - denise_
ui_ ⚠free - Destroys a user interface.
NULLis accepted and does nothing, asfreedoes. - denise_
ui_ new - Creates a user interface
widthbyheightpixels, in one of the built-in themes (DENISE_THEME_DARKand friends). - denise_
ui_ new_ scaled - Creates a user interface with its theme metrics at a scale factor, given in
hundredths —
150is 1.5x,200a 2x display. - denise_
ui_ ⚠poll_ message - Takes the next message, or returns
falseif there is none. - denise_
ui_ ⚠set_ theme - Switches to one of the built-in themes and repaints everything.
- denise_
ui_ ⚠size - Writes the surface size. Either output may be
NULL. - denise_
version - Denise’s version as a NUL-terminated string. Never
NULL, never freed.