Module — Framebuffer Model and Ops¶
This module documents the in-memory framebuffer used by drawlist execution and internal renderers.
Defined by src/core/zr_framebuffer.h / src/core/zr_framebuffer.c.
Compatibility shim: src/core/zr_fb.h includes core/zr_framebuffer.h.
Data model¶
Shared structs¶
zr_rect_t { int32_t x, y, w, h; }zr_style_t { uint32_t fg_rgb, bg_rgb, attrs, reserved, underline_rgb, link_ref; }reservedlow bits carry underline variant in drawlist v1 style payloads (0keeps legacy underline behavior).underline_rgbis underline color (0x00RRGGBB;0means terminal default underline color).link_refis a framebuffer-owned 1-based hyperlink reference (0means no link).
Cells¶
Each cell (zr_cell_t) stores:
glyph[ZR_CELL_GLYPH_MAX]— UTF-8 bytes for a single graphemeglyph_len— number of valid bytes inglyphstyle— foreground/background/attributes (zr_style_t)width— one of:1for a normal cell2for a wide lead cell (spans this cell + the next cell)0for a wide continuation cell (must be empty)
Hyperlink table¶
Framebuffer hyperlink references are interned in framebuffer-owned storage:
zr_fb_link_t { uri_off, uri_len, id_off, id_len }points intofb.link_byteszr_fb_link_intern()deduplicates(uri,id)pairs and returns stable 1-based refszr_fb_link_lookup()resolves refs for OSC 8 emission in the diff rendererzr_fb_links_reset()clears per-frame link usage without reallocating bufferszr_fb_links_clone_from()copies link tables between framebuffers for deterministic model replay/tests
Limits:
- URI max bytes:
ZR_FB_LINK_URI_MAX_BYTES(2083) - ID max bytes:
ZR_FB_LINK_ID_MAX_BYTES(2083)
Grapheme size cap¶
If a grapheme’s UTF-8 encoding exceeds ZR_CELL_GLYPH_MAX, the framebuffer replaces it with U+FFFD
deterministically (UTF-8 bytes EF BF BD, width 1).
Wide glyph continuation invariant¶
Some graphemes consume 2 terminal columns (best-effort width policy; deterministic pins in src/unicode/).
When the framebuffer stores a wide glyph at (x, y):
(x, y)is the lead cell:width == 2glyph_len > 0andglyph[]contains the grapheme UTF-8 bytes(x+1, y)is the continuation cell:width == 0glyph_len == 0
Writers that overwrite cells MUST preserve these invariants. In particular, internal overlays/diff helpers MUST NOT create a state where a continuation cell exists without a valid lead cell immediately to its left.
Ops overview¶
zr_fb_init/zr_fb_release— framebuffer lifetime;zr_fb_initallocates backing cells.zr_fb_resize— resizes backing store with a no-partial-effects contract (on failure, the framebuffer is unchanged).zr_fb_cell/zr_fb_cell_const— cell lookup helpers (returnNULLif out of bounds).zr_fb_link_intern/zr_fb_link_lookup/zr_fb_links_reset/zr_fb_links_clone_from— hyperlink table helpers.zr_fb_painter_begin— starts a painter with a caller-provided clip stack.zr_fb_clip_push/zr_fb_clip_pop— deterministic clip stack; effective clip is the intersection of framebuffer bounds and all pushed clips.zr_fb_clear/zr_fb_fill_rect— style-based fills.zr_fb_draw_hline/zr_fb_draw_vline/zr_fb_draw_box— ASCII line/box primitives.zr_fb_draw_scrollbar_v/zr_fb_draw_scrollbar_h— ASCII scrollbars.zr_fb_put_grapheme— writes a pre-segmented grapheme with an explicit width (1or2):- If
len == 0, normalizes to a single ASCII space (width1). - If
len > ZR_CELL_GLYPH_MAX, writesU+FFFDwidth1. - If bytes contain invalid UTF-8 or Unicode control scalars (C0/DEL/C1), writes
U+FFFDwidth1to prevent emitting terminal control bytes. - If
width == 2but cannot write both cells within bounds+clip, writesU+FFFDwidth1(never half glyphs). - Invariant-repair exception (LOCKED): when overwriting a continuation/lead edge, it may clear exactly one adjacent
paired cell (
x-1orx+1) outside clip to prevent orphan wide-pair state. zr_fb_draw_text_bytes— draws UTF-8 text by iterating graphemes and callingzr_fb_put_grapheme.zr_fb_blit_rect— overlap-safe rectangle copy (memmove-like semantics) that preserves wide-glyph invariants.
Layout and clipping¶
Clipping MUST NOT affect cursor advancement. When a wide glyph cannot be fully written within the current clip, the
draw is replaced with U+FFFD (width 1) but the logical cursor advance remains 2. This prevents clip-dependent text
shifts where subsequent glyphs move into/out of the visible region.
If clipping begins inside a continuation cell (or covers only a wide lead), zr_fb_put_grapheme() applies a bounded
invariant-repair exception: it may clear the paired cell immediately adjacent to the target (x-1 or x+1) even when
that paired cell is outside clip. This is the only allowed out-of-clip mutation and prevents stale orphan lead/continuation
pairs on incremental frames (see tests/unit/test_clipping.c).