Skip to main content

Crate denise_ffi

Crate denise_ffi 

Source
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 from denise_ui_new and goes to denise_ui_free. Nothing else may free it.
  • A node is a uint64_t, and 0 is never a valid node. Ids carry a generation, so an id kept past a denise_ui_remove fails to resolve rather than addressing whoever took the slot.
  • A message is a uint32_t, chosen by the host, and 0 means no message. A button given 0 emits nothing, and denise_ui_poll_message never 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 DeniseUi belongs 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§

pub use input::*;
pub use paint::*;
pub use tree::*;
pub use types::*;

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§

DeniseUi
An opaque user interface. Create with denise_ui_new, destroy with denise_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 DeniseUi as 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 than NULL, because a host logging an error should not have to handle an error from the logger.
denise_ui_free⚠
Destroys a user interface. NULL is accepted and does nothing, as free does.
denise_ui_new
Creates a user interface width by height pixels, in one of the built-in themes (DENISE_THEME_DARK and friends).
denise_ui_new_scaled
Creates a user interface with its theme metrics at a scale factor, given in hundredths — 150 is 1.5x, 200 a 2x display.
denise_ui_poll_message⚠
Takes the next message, or returns false if 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.