Expand description
System integration callbacks from NsGui/IntegrationAPI.h (namespace
Noesis::GUI). These are process-global host hooks (not per-view)
that let the host react to engine requests: update the OS cursor, show or
hide the on-screen keyboard, open a URL, play a sound, and set the default
culture.
§Pattern
Each set_* registration boxes a Rust closure and hands a thin pointer to
it across the FFI as Noesis’s void* user, alongside an extern "C"
trampoline. The C++ shim (cpp/noesis_integration.cpp) keeps a single
static (user, callback) slot per hook and registers its own translating
trampoline with Noesis (converting Cursor* → CursorType, const Uri& → &str). The returned *Callback guard owns the boxed closure and
clears the registration on Drop, mirroring the Registered-guard idiom in
crate::texture_provider / crate::font_provider.
§Single-slot, last-registration-wins
These hooks are process-global with exactly one slot per hook. Both
Noesis itself and the C++ shim store a single (user, callback) pair. They
are therefore not independent, freely-stacked registrations: calling a
set_* again replaces the previous registration (last-registration-wins),
and the older guard is then logically dead even though it is still alive.
To make Drop safe under that reality, each registration is tagged with a
unique generation id (see next_reg_id) and a per-hook atomic records the
id of the currently active registration. A guard’s Drop clears the
global slot only if it is still the active registration (its id still
matches the per-hook atomic); otherwise it just frees its own boxed closure
and leaves the slot pointing at whoever overwrote it. Consequences:
- Dropping the older of two guards for the same hook is a no-op on the slot. The newer registration keeps firing (no clobber). Its box is still freed; the C++ slot never referenced it after the overwrite.
- Dropping the active guard clears the slot and unregisters the Noesis callback. A previously-overwritten registration is not restored: once replaced, an older registration is gone for good.
Each guard always frees exactly its own boxed closure, so there is no double-free or use-after-free regardless of drop order.
Registration publishes the new generation id before writing the FFI slot,
so a stale guard’s Drop can never compare-exchange its way into clobbering
a newer registration. This relies on one invariant the caller must uphold:
guard drops must be serialized with the view thread’s callback dispatch
(see “Lifetime” below). A guard freed concurrently with an in-flight callback
on the same hook is a data race regardless of the id bookkeeping.
§Triggering
open_url and play_audio invoke the registered callback
synchronously: they are genuine end-to-end round trips and are tested
as such. The cursor callback fires from input handling inside a live view’s
event pump and is reachable headlessly: a mouse-move over an element with
a non-default Cursor drives it (proved in tests/integration.rs). The
software-keyboard callback fires only when a virtual-keyboard-enabled
element gains focus on a platform that requests it; that path can’t be
synthesised headlessly, so for it we verify only that registration /
unregistration crosses the FFI cleanly.
§Lifetime
A guard must outlive every Noesis-internal reference that might call back
into the closure. Keep it alive until after crate::shutdown returns (or
until you explicitly drop it to unregister).
Structs§
- Cursor
Callback - Guard for a registered cursor callback. This is a process-global, single-slot, last-registration-wins hook (see the module docs): dropping the guard clears the slot only while it is still the active registration, and always frees its own boxed closure.
- Open
UrlCallback - Guard for a registered open-URL callback. Process-global, single-slot,
last-registration-wins; see
CursorCallbackand the module docs. - Play
Audio Callback - Guard for a registered play-audio callback. Process-global, single-slot,
last-registration-wins; see
CursorCallbackand the module docs. - Software
Keyboard Callback - Guard for a registered software-keyboard callback. Process-global,
single-slot, last-registration-wins; see
CursorCallbackand the module docs.
Enums§
- Cursor
Type - Built-in cursor types, mirroring
Noesis::CursorTypeinNsGui/Cursor.h. The discriminants match the C++ enum exactly so the value read back from a liveCursorround-trips.
Functions§
- get_
culture - Return the active default culture’s BCP-47 name. Defaults to
"en-US"before anyset_culturecall. - open_
url - Ask Noesis to open
url. Invokes the registered open-URL callback synchronously; a no-op if none is registered. - play_
audio - Ask Noesis to play the sound at
uriatvolume. Invokes the registered play-audio callback synchronously; a no-op if none is registered. - set_
culture - Set the default culture by BCP-47 name (e.g.
"en-US","fr-FR"). The name backs Noesis’s number / currency / date formatting. Round-trips throughget_culture. The C++ shim keeps the name alive in a static buffer for the process lifetime. - set_
cursor_ callback - Register
fas the global cursor-update callback.freceives the borrowedNoesis::IView*(opaque) requesting the change and the desiredCursorType. It must beFnbecause the callback fires synchronously from input dispatch and may re-enter; use interior mutability for state. Returns a guard; drop it to unregister. - set_
open_ url_ callback - Register
fas the global open-URL callback.freceives the URL string whenever the host should open it in a browser.open_urltriggers this synchronously, sofmust beFn(a re-entrant call must not alias handler state; use interior mutability). - set_
play_ audio_ callback - Register
fas the global play-audio callback.freceives the canonicalized URI string of the sound and a volume in[0.0, 1.0].play_audiotriggers this synchronously, sofmust beFn(a re-entrant call must not alias handler state; use interior mutability). - set_
software_ keyboard_ callback - Register
fas the global on-screen-keyboard callback.freceives the borrowedNoesis::UIElement*(opaque) that has focus and aboolthat istrueto open the keyboard,falseto close it. It must beFn(the callback may re-enter; use interior mutability for state).