Module gui::gui_web
GUI: the WebAssembly <canvas> backend.
On a wasm32/wasm64 target std/gui's platform backend is the browser: every GuiWindow is a <canvas> element of the page (or an OffscreenCanvas where there is no document), and everything else in std/gui — the canvas, text, widgets, GuiApp with its timers and animation frames — works unchanged on top of it. The host side is std/gui/gui_web.js, an ES module shipped next to this header (see its header comment):
import { loadSafeCGui } from "./gui_web.js";
const app = await loadSafeCGui(fetch("app.wasm"), { entry: "start" });
A browser must not block, so a program doesn't call GuiApp::run(): its entry point (an export_name function the glue calls once) opens the windows, then hands the app to gui_web_run(), which returns at once; the glue then calls GuiApp::pump(0) once per requestAnimationFrame until it returns 0 (the last window closed or quit()):
static struct GuiApp gApp; // must outlive start()
__attribute__((export_name("start")))
int start() {
gApp = GuiApp::new();
?&GuiWindow w = gApp.open_window("Hello", 480, 320);
...
gui_web_run(&gApp);
return 0;
}
What maps to what:
windows windows: "canvas" (default) — each window a <canvas>: the
glue's 'canvas' option picks it (an element, a selector, or
a function (index, title) => canvas), else the next unused
canvas[data-safec-gui] of the page, else a new <canvas>
appended to <body>. Its CSS size is the window's logical
size and its backing store that times devicePixelRatio
(hiDpi), so drawing is sharp; with the 'fit' option (or a
data-safec-fit attribute) the canvas keeps the size the
page's CSS gives it and follows it (ResizeObserver) —
GUI_EVENT_RESIZE, as a window would. windows: "frames" —
each window a positioned <div class="safec-gui-window"> in
a container, with a title bar (drag it: GUI_EVENT_MOVE,
set_position()/pos_x() work; its × : GUI_EVENT_CLOSE), a
resize grip honoring the size limits, raised on a click.
presenting putImageData of the damaged rectangle, straight from the
module's memory (premultiplied pixels converted to
ImageData's straight alpha on the way) — or, with the
page's present: "webgl" option or GUI_PRESENT_GPU, WebGL2
(else WebGL1): the damaged rectangle uploaded into a
texture and drawn as one quad (gui_gpu_web_wasm.sc), back
to putImageData if there is no WebGL or the context is lost.
input pointer events (with pointer capture while a button is
down), wheel (pixel/line/page deltas to lines); keyboard
focus is real DOM focus on a hidden <textarea> per window
(a click on the canvas focuses it): KeyboardEvent.code/key
to GUI_KEY_* and the typed character, and text that comes
without a key — IME composition, virtual keyboards,
context-menu pastes — as GUI_EVENT_CHAR.
HiDPI devicePixelRatio, followed when it changes (zoom, another
display): GUI_EVENT_SCALE.
clipboard gui_clipboard_set_text() writes navigator.clipboard (and the
copy event a Ctrl/Cmd+C shortcut fires); gui_clipboard_get_text()
returns the text of the last paste event or of the last
copy — the glue holds a Ctrl/Cmd+V keydown back until its
paste event arrived, so a paste shortcut sees the pasted
text. Reading the system clipboard any other way needs a
permission browsers don't grant silently.
cursors CSS cursor on the canvas.
a11y canvases are role="img" labelled with the window title, the
hidden input carries it too, and gui_web_announce() speaks
through an aria-live region.
DOM gui_web_window_element()/gui_web_mount() trade elements with
std/wasm/dom.h, so a page mixes DOM and std/gui windows.
clock performance.now(); timers fire from the frame loop.
Files: fopen()/fread() read the glue's 'files' option (fetchFiles()), e.g. fonts at the paths std/gui looks in; the TrueType stack's embedded fallback face is there without any. 'safeguard bundle --target wasm32' writes build/<name>.web/: the module, this glue and a page.
Functions
| gui_web_run | Lets the JS glue drive 'app' from requestAnimationFrame: one app.pump(0) per frame until it returns 0. Returns at once. 'app' must stay alive (a static or heap object) after the caller returns. |
| gui_web_announce | Screen readers: says 'text' through the page's aria-live region (e.g. a status change your UI only draws). |
| gui_web_window_element | std/wasm/dom.h interop (the page loaded the module through safec_dom.js's loadSafeC, or passed its glue as the domGlue option): the window's element — its frame in "frames" mode, else its <canvas> — as a dom.h handle (-1 without the DOM glue), to style it or put DOM elements next to it; and moving the window's element into DOM element 'parent' (1 = done). |
| gui_web_mount |