In C++

On this page

xpui-cpp

The C++ side of the boundary: an application that already owns its screen stack, hosting xpui screens over a C ABI. This is the repository to read if you have a firmware written in C++ and want one screen of it in Rust. Not a rewrite — a screen at a time, called through six lifecycle entry points and an opaque void*.

Which crate you want

cpp_hostThe worked example, and the only thing any gate in this organisation builds, links and runs the FreeInkUI shim through. Design an ABI change here
firmwareThe same C++ through PlatformIO, for a real ESP32. No gate invokes it, so a break surfaces when somebody builds a firmware
abiTwo of the five ABI boundaries, checked — the two that cross into the application: xpui_host.h against the Rust that calls it, and xpui_app.h against the macro that defines it. The other three are the backend's and are checked there

Using it

Nothing depends on this repository; it is the far end. Using it is following the tutorial with a firmware of your own, or building the worked example:

cmake -S cpp_host -B target/cpp_host -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build target/cpp_host
./target/cpp_host/xpui-host --headless --frames 30 --selftest

What it builds on is xpui and the FreeInkUI backend from xpui-backends, whose fui/cpp/ this compiles and links. That one is a sibling checkout, not a package path: nothing is published, and a git dependency lands in a cargo checkout directory with no path a CMakeLists.txt can name.

Requirements

  • xpui-backends, cloned beside this repository — or named by XPUI_BACKENDS_DIR.
  • The FreeInk SDK, the same arrangement: beside the checkout, or named by FREEINK_SDK_INCLUDE, or fetched by CMake at the revision cpp_host/freeink-sdk.rev pins, which CI reads too.
  • SDL2, for the desktop host's window.
  • clang-format 21 or newer, for the C++ format stage; an older binary ignores options it does not know and formats differently, silently.
  • CMake and Ninja, for all.

Checking it

./build-and-test.sh          # format, lint, test, and every snippet
./build-and-test.sh all      # plus cmake, the link, and nine ctest cases

The checks themselves are in xtask/ — this repository's own list, in Rust, holding nothing it does not run. ./build-and-test.sh fix formats in place first. all is the only place in the organisation where the C ABI is compiled, linked and executed rather than syntax-checked. How a change is reviewed is in docs/contributing.md.

Where next

docs/tutorial.mdStart here. Eleven steps: write a screen, get its words from your string table, export it, drive it, open it from the menu you already have, answer what the framework asks, teach the firmware a new symbol, start it, build it, test it with no device, and flash it
docs/boundary.mdthe four symbol sets and who defines each, the double-boxed handle, the firmware this host was modelled on and the three deliberate differences, what the nine ctest cases prove, and what porting to a device cost
docs/contributing.mdthe requirements, the gate in both modes, the two-place rule for a C symbol, the five review steps, and how a commit is written

Where it sits

Every arrow is a dependency in a Cargo.toml, and they all point inward toward xpui, which depends on nothing at all. That is the rule the organisation is arranged around: a backend can be written without the framework knowing it exists, and a firmware reaches whatever it needs directly rather than through whoever happens to sit above it.

xpuithe frameworkxpui-chromecomponentsxpui-boardsseven devicesxpui-backendstwo backendsxpui-simulatora windowxpui-gallerythe appxpui-rp2040firmwarexpui-esp32firmwarexpui-cppa C++ hostxpui-devthe umbrella

Edit this page on GitHubIt lives in xpui-cpp; a correction goes there.