macro_rules! app_entry {
($A:ty) => { ... };
}Expand description
Emits the wasm-bindgen FFI surface driving $A: App<TerminalWasm> + Default from a browser
terminal emulator (e.g. xterm.js), on wasm32 only.
examples/src/wasm_entry.rs’s __wasm_terminal_entry! does the same job for the examples
crate’s private Example trait, but that crate is publish = false, so nothing outside this
repo can reach it (retroglyph#684). This macro is the generally-usable version: generic over
App (public, stable, and already the update contract every other
driver in retroglyph-core shares), not Example.
Call it once, at the top level of a wasm32 binary crate that depends on this crate and
retroglyph-core:
#[derive(Default)]
struct MyGame { /* ... */ }
impl retroglyph_core::app::App<retroglyph_terminal_wasm::TerminalWasm> for MyGame {
fn update(
&mut self,
term: &mut retroglyph_core::terminal::Terminal<retroglyph_terminal_wasm::TerminalWasm>,
frame: &retroglyph_core::app::Frame,
) -> retroglyph_core::app::Flow {
// ...
retroglyph_core::app::Flow::Continue
}
}
retroglyph_terminal_wasm::app_entry!(MyGame);
fn main() {}Expands to nothing at all off wasm32 (a native build of the same crate just doesn’t get this
FFI surface, since nothing would call it).
Exports, all thread-local and single-instance (one $A per page; construct a fresh
handle-based session per instance instead via this crate’s wasm module, only compiled for
target_arch = "wasm32", if a page needs more than one):
wasm_app_init(width, height): builds theTerminal<TerminalWasm>at the given size (in cells) and$A::default(). Call once, before the first tick, after sizing the host terminal emulator (e.g. xterm.js’sfitAddon.fit()).wasm_app_resize(width, height): reports a new size (in cells) viaresize_terminal, so the driven$Asees the matchingEvent::Resizeon its nextupdate, not just a backend that silently changed size under it.wasm_app_push_key(code, mods)/wasm_app_push_mouse(x, y, action, button, mods): decode and queue input viadecode_key_event/decode_mouse_event.wasm_app_push_paste(text): queuestextas a singleEvent::Paste.wasm_app_push_focus(focused): queuesEvent::FocusGained/Event::FocusLost.wasm_app_tick() -> String: runs oneApp::update, presents unless it returnedFlow::Idle(or already presented itself), and returns the ANSI bytes rendered since the last call, the same contractTerminalWasm::take_outputdocuments.Frame::deltais wall-clock time since the previous tick, clamped toMAX_TICK_DELTA(250ms): a backgrounded tab can starverequestAnimationFramefor seconds or minutes, and an uncapped delta handed straight to an animation/physics step would try to simulate that entire gap in one frame (the same “spiral of death” concernFrameClockcaps steps-per-frame to avoid), just on the raw delta feeding intoFrameinstead. All FFI functions are no-ops (returning an empty string forwasm_app_tick) if called beforewasm_app_init.wasm_app_exited() -> bool:trueonce$A::updatehas returnedFlow::Exitat least once. A browser tab has no native “exit the process” the way a windowed backend’s event loop does, so this crate can’t stop JS’srequestAnimationFrameloop for it; check this afterwasm_app_tickand stop calling it once it flipstrue, e.g. to show a fixed “Game Over” frame’s own draw already put on screen.wasm_app_tickkeeps calling$A::update(and, correctly, doing nothing useful) if the caller ignores this rather than panicking or hanging.