mx_remote_ffi/lib.rs
1// Author: Lars Op den Kamp (lars@opdenkamp-it.nl)
2// Copyright (c) 2026 Op den Kamp IT Solutions
3
4#![deny(missing_docs)]
5// Every type here is named as it appears in C, because C is the only thing
6// that reads it. A header whose names were transliterated from Rust would make
7// the reader translate in both directions.
8#![allow(non_camel_case_types)]
9
10//! C ABI for [`mx_remote`].
11//!
12//! The whole of this crate is one translation: it holds every `unsafe` in the
13//! workspace, and adds no protocol logic of its own. Anything a caller can do
14//! here is something the core crate already does; if a rule is not enforced in
15//! `mx_remote`, it is not enforced at all.
16//!
17//! Three conventions run through the header, and knowing them is most of
18//! knowing the API:
19//!
20//! - **A device is addressed by value.** There is no handle for a device or a
21//! bay: a device is a [`mxr_uid_t`], a bay is a [`mxr_bay_uid_t`], and state
22//! is read by passing one in and having a struct filled out. Nothing hands
23//! back a pointer into state that a lock protects.
24//! - **Every call returns, whatever happens.** An unwind across the boundary
25//! is undefined behaviour, so each entry point catches: a panic becomes
26//! [`mxr_result_t::MXR_ERR_PANIC`] rather than a corrupted stack.
27//! - **A borrowed pointer lives for one call.** Strings and arrays handed to a
28//! callback point into memory the library owns and reuses. A caller that
29//! needs one afterwards copies it.
30//!
31//! Every failure code is negative and [`mxr_result_t::MXR_OK`] is zero, so
32//! `if (rc < 0)` is a complete test, and a code added later cannot turn a
33//! failure into a success.
34//!
35//! # From C
36//!
37//! ```c
38//! #include <mx_remote.h>
39//!
40//! /* The client cannot be its own userdata: it does not exist when the table
41//! * is handed over, so it reaches the callbacks through a struct that does. */
42//! struct app { mxr_remote_t *remote; };
43//!
44//! static void on_device_update(void *userdata, mxr_uid_t device) {
45//! mxr_device_info_t info;
46//! if (mxr_device(((struct app *)userdata)->remote, device, &info) == MXR_OK)
47//! printf("%s %s\n", info.model, info.name);
48//! }
49//!
50//! int main(void) {
51//! struct app app = {0};
52//! mxr_callbacks_t cb = {0};
53//! cb.on_device_update = on_device_update;
54//!
55//! /* Zeroing a config asks for every default; NULL does the same. */
56//! app.remote = mxr_remote_new(NULL, &cb, &app);
57//! mxr_remote_start(app.remote);
58//! /* ... */
59//! mxr_remote_free(app.remote);
60//! return 0;
61//! }
62//! ```
63//!
64//! `include/mx_remote.h` is generated from this source by cbindgen and checked
65//! into the repository. `include/mx_remote.hpp` is a hand-written header-only
66//! C++ layer over it, with a move-only `mxr::Remote` that closes and joins in
67//! its destructor and an `mxr::Handler` base class carrying a virtual method
68//! per event.
69
70mod abi;
71mod control;
72mod events;
73mod info;
74mod remote;
75mod subsystems;
76
77// Everything the header exposes, re-exported flat: a Rust caller reaching for
78// this crate wants the same surface a C caller gets, under the same names.
79pub use abi::*;
80pub use control::*;
81pub use events::*;
82pub use info::*;
83pub use remote::*;
84pub use subsystems::*;