# Tribal knowledge: RmlUi bindings (`Rml::`) Hand-written C++ (`mrbgems/rmlui/src/rml_bindings.cpp`) + Ruby sugar (`mrblib/rmlui.rb`), modeled on RmlUi's Lua bindings. RmlUi is the **only** UI layer (no raygui), rendered over the game through raylib's GL context. ## At a glance - **Key files:** `mrbgems/rmlui/src/rml_bindings.cpp` (bindings + the rlgl render backend); sugar `mrblib/rmlui.rb`; `mrbgem.rake`; documents/styles `game/ui/*.{rml,rcss}`. - **Ruby API:** `Rml.init`/`load_font`, `Rml::Context` (`data_model`, `load_document`, `frame`), `Document`/`Element`/`Event`/`DataModel`. Spec `docs/API_SPEC_RMLUI.md`. - **Cross-refs:** rules `mruby-rebuild`, `link-order`. Keyboard/text input gap CLOSED (see "Known gaps" below) — unblocks the in-game console (roadmap R6). ## Build `cmake` with target `rmlui_core` ONLY. The `rmlui_debugger` module fails to compile with GCC 16 (bundled `robin_hood.h`) and we don't need it. Flags: `-DBUILD_SHARED_LIBS=OFF -DRMLUI_SAMPLES=OFF -DRMLUI_LUA_BINDINGS=OFF -DRMLUI_FONT_ENGINE=freetype`. Being a C++ gem, it flips mruby to C++-exception ABI (see rules/mruby-rebuild.md). ## The render backend (rlgl) — three fixes that were painful to find The RmlUi render interface is implemented against raylib's **rlgl** (so the same code works on desktop GL and WebGL). Three non-obvious correctness fixes: 1. **`rlSetTexture` must come AFTER `rlBegin(mode)`** — `rlBegin` resets the current draw-group texture on a mode change, so setting it before is lost. 2. **Premultiplied alpha** — render with `RL_BLEND_ALPHA_PREMULTIPLY`; RmlUi 6.x already premultiplies its font atlas. Using normal alpha gives dark fringes. 3. **Flush the batch per geometry** — call the batch flush for each geometry so textures/scissor don't bleed across draws. ## API surface `Rml::Context`, `Rml::Document < Element`, `Rml::Element` (attributes, classes, style properties, queries `query_selector`/`get_element_by_id`/`elements_by_tag`, traversal, geometry, `el.on(:click) { |event| ... }`), `Rml::Event`, and the MVC **data model** (`m.bind`/`m.value`/`m.event`, `model.dirty`). Init AFTER `Rl.init_window` (needs the GL context). `ctx.frame { }` does process_input→(block)→update+render. ## In-game REPL console (`Jamstack::Console`) - **File:** `mrbgems/rmlui/mrblib/console.rb` (Ruby sugar, compiled into the gem). - **Assets:** `game/ui/console.rml` + `game/ui/console.rcss`. - **Usage:** `console = Jamstack::Console.new(ctx, binding: binding)` — pass the game script's binding so `eval` sees local variables (`score`, `world`, etc.). - **Toggle:** backtick (`KEY_GRAVE`, 96). `console.update` (call before `ctx.process_input`) checks `IsKeyPressed` and drains both `GetKeyPressed` and `GetCharPressed` queues on the toggle frame so the backtick isn't forwarded to RmlUi as text input. - **Enter/Up/Down:** handled via `input.on(:keydown)` — `KI_RETURN` (72) evals, `KI_UP` (91)/`KI_DOWN` (93) navigate history. `event.stop_propagation` prevents RmlUi's default `LineBreak` on Enter. - **Scrollback:** `inner_rml=` with HTML-escaped text; `