Skip to content

The native core

Concern Crate Notes
Windows, input, IME, event loop winit run_app on the main thread; EventLoopProxy for wake-ups from other threads
GPU wgpu Metal / D3D12 / Vulkan, with GL as a fallback; WGSL shaders
Layout taffy flexbox, grid, block. Do our own rounding to device pixels
Text layout and shaping parley + fontique Bidi, line breaking, font fallback. cosmic-text is the alternative
Glyph rasterization swash Coverage + color (emoji) atlases
Accessibility accesskit + accesskit_winit VoiceOver, Narrator/UIA, Orca/AT-SPI
Menus, tray muda, tray-icon
Dialogs, clipboard rfd, arboard
Images image (PNG/JPEG/WebP), resvg (SVG) Decoded off the main thread
core/
crates/
mantou-ffi C ABI surface (cdylib); the only crate with `extern "C"`
mantou-tree shadow tree, op decoder, styles, Taffy integration, hit-testing, focus
mantou-text parley wrapper, measure cache, glyph atlas management
mantou-scene immutable scene / display list built from the tree after layout
mantou-render wgpu renderer: instanced SDF quads, glyphs, images, clips, layers
mantou-platform winit app, windows, IME, AccessKit, menus, dialogs, clipboard

mantou-tree, mantou-text and mantou-scene have no platform dependencies, so they can be tested headless (§12).

Status: mantou-ffi, mantou-tree and mantou-render exist. For now the scene types live in mantou-tree, and the winit code in mantou-ffi; mantou-scene, mantou-text and mantou-platform split out when text and platform services arrive. The C ABI is at version 3: every per-UI function takes an instance handle, and headless instances exist for tests (§12.2).

bun:ffi can’t pass structs by value. Every call therefore takes only scalars, pointers and lengths.

uint32_t mt_abi_version(void);
// Bootstrap (main thread)
void mt_run(const uint8_t* config, uint32_t len); // blocks until quit; returns exit code via mt_exit_code()
int32_t mt_exit_code(void);
// Worker thread
void mt_set_wake(void (*wake)(void)); // threadsafe JSCallback pointer
void mt_commit(const uint8_t* ops, uint32_t len); // apply ops, layout, build scene, request redraw
uint32_t mt_take_events(const uint8_t** out); // swaps the event buffer; *out valid until the next call
uint32_t mt_measure_text(const uint8_t* req, uint32_t len, uint8_t* out, uint32_t cap); // optional, for app-side measuring
void mt_quit(int32_t code);

Both threads dlopen the same library. A commit made before mt_run has started is queued.

Revision planned (see §12.2): the core becomes multi-instance. Every function will take an instance handle: mt_run creates the app instance, and mt_headless_new creates headless instances for tests. The signatures above leave the handle out for brevity.