Skip to content

Rendering Model

Zireael renders by executing drawlist commands into an internal framebuffer, diffing against the previous frame, then emitting minimal terminal bytes.

Pipeline

  1. Wrapper submits drawlist bytes.
  2. Engine validates drawlist format and limits.
  3. Drawlist executes into a staging framebuffer.
  4. On success, staging framebuffer becomes next framebuffer.
  5. engine_present() diffs previous vs next framebuffer.
  6. Engine emits output bytes and performs one platform write.
  7. Framebuffers swap; metrics advance.

No-Partial-Effects Contract

  • Invalid drawlist input fails before committed frame state changes.
  • Failed present/write does not advance presented-frame metrics/state.

This makes wrapper retry and error handling deterministic.

Diff and Damage

Diff output minimizes terminal I/O using:

  • changed-cell detection between prev/next framebuffers
  • damage rectangle tracking/coalescing
  • scroll-region optimization when backend capabilities allow

Result metrics include dirty/damage counts for the last frame.

Cursor Behavior

Drawlist v1 supports explicit cursor control (ZR_DL_OP_SET_CURSOR).

  • cursor state is part of terminal emission behavior
  • it does not mutate glyph content in framebuffer cells

Drawlist v2 adds overlap-safe framebuffer rectangle copies (ZR_DL_OP_BLIT_RECT) to express scroll/move-style updates without redraw.

Screen Modes: Alt vs Inline

Zireael supports two screen buffer policies, selected at engine creation:

  • ALT (default, ZR_SCREEN_MODE_ALT): the engine switches to the alternate screen buffer and owns the full terminal surface. On exit the prior screen is restored. This fits full-screen applications.
  • INLINE (ZR_SCREEN_MODE_INLINE): the engine stays on the primary screen and renders a bounded region of inline_rows rows at the current scroll position. Scrollback above the region stays visible, output streams naturally (the region claims rows downward and the screen scrolls when it reaches the bottom), and on exit the final frame remains in scrollback with the shell prompt restored below it. This fits status UIs, progress displays, REPLs, and agent-style CLIs.

Inline mode details:

  • inline_rows must be 1..1024; the effective viewport is clamped to the live terminal height and re-clamped on resize.
  • ZR_EV_RESIZE reports the presentable surface (the clamped viewport).
  • inline_rows can be changed at runtime via engine_set_config(); the engine resizes its framebuffers and enqueues a resize event.
  • Emission never uses absolute row addressing, the alternate screen, or ESC[2J; repaints use relative cursor motion so the primary screen above the region is never touched.
  • Protocol images (Kitty/Sixel/iTerm2) and scroll-region optimization are suppressed in inline mode (both need absolute rows); DRAW_IMAGE falls back to the sub-cell blitter path and engine_get_caps() reflects the suppression.

Inline mode also supports scrollback commits: stage a drawlist with engine_commit_scrollback() and the next present emits those rows above the live region, where they become ordinary terminal history (the Ink <Static> / Bubble Tea Println pattern used by agent-style CLIs).

See examples/inline_status.c for a complete inline-mode loop including checkpoint lines committed to scrollback.

Output Buffering and Flush

  • Output is assembled into an internal bounded buffer.
  • Successful present performs a single platform write.
  • Optional sync-update wrapping can be applied when capability is detected.

Performance Guidance For Wrappers

  • avoid sending full-frame redraw drawlists when not needed
  • keep drawlist payloads compact (strings/blobs reused where possible)
  • monitor metrics (bytes_emitted_last_frame, damage fields)