Screen builds terminal frames by mutating a next buffer, diffs it against the current buffer to generate minimal ANSI escape sequences, then swaps the two. Post-processing transforms can run between building and diffing to apply animations or visual filters.
Double buffering
The screen maintains two grid buffers. During build the caller populates the next buffer. During render the next buffer is diffed against current to produce ANSI output, then the buffers swap and the (now-next) buffer is cleared. Content must be redrawn every frame.
Hit testing
A Hit_grid.t is maintained alongside each visual grid, mapping screen coordinates to integer element IDs. Call Hit_grid.add during frame building to register clickable regions. Query coordinates with query_hit after rendering.
Post-processing
Post-processors are persistent functions registered via post_process. They run after building and before diffing, receiving the grid and the delta time since the last frame. Processors execute in insertion order and persist across frames until removed.
Invariants
The Grid.t passed to build is the next buffer. After render the buffers swap and the now-next buffer is cleared.
Visual and hit buffers swap on render: regions registered via Hit_grid.add become queryable only after the following render.
Post-processors always run, even when no cells changed. Their ~delta argument is milliseconds since the last render (0. for the first frame).
Resizes buffers to width x height when both are positive, clears the hit grid, then calls f with the next grid and hit grid for in-place mutation. Returns t for chaining.
When width <= 0 or height <= 0, f is not called; only the hit grid is cleared.
Warning. The Grid.t and Hit_grid.t passed to f must not be mutated after the next render -- they become the diff baseline.
post_process f t registers f as a persistent post-processing transform and is its effect_id.
f is called during each render after frame building but before diffing. It receives the next grid and ~delta (milliseconds since last render, 0. on the first frame). Processors run in insertion order and persist across frames until removed.
add_hit_region t ~x ~y ~width ~height ~id registers a hit region on the next hit grid. Convenience wrapper around Hit_grid.add for use outside the build callback. Regions outside grid bounds are clipped. Negative dimensions are clamped to zero. Returns t for chaining.
render ~full ~height_limit t is the ANSI output for the current frame.
Applies post-processors, diffs next against current (or renders all cells when full is true), then swaps buffers. Hit regions registered during this frame become queryable via query_hit.
full renders all cells regardless of changes. Defaults to false.
height_limit limits rendering to the first height_limit rows.
Sourceval render_to_bytes : ?full:bool ->?height_limit:int ->t->Bytes.t-> int
render_to_bytes ~full ~height_limit t buf is like render but writes into buf and is the number of bytes written. buf must be large enough for the output.
set_explicit_width t b enables or disables explicit-width OSC emission for graphemes. When enabled and the terminal supports it, OSC sequences specifying the exact width of multi-width characters are emitted to prevent terminal-side width mismatch.
Sourceval set_cursor_position : t->row:int ->col:int -> unit
set_cursor_position t ~row ~col sets the desired cursor coordinates. row and col are one-based terminal coordinates where (1, 1) is the top-left corner.
cursor_info t is the current desired cursor state.
Capabilities
Sourceval apply_capabilities :
t->explicit_width:bool ->explicit_cursor_positioning:bool ->hyperlinks:bool ->
unit
apply_capabilities t ~explicit_width ~explicit_cursor_positioning ~hyperlinks applies terminal capability flags to t.
explicit_width: whether the terminal supports explicit-width OSC sequences.
explicit_cursor_positioning: whether to reposition the cursor after wide graphemes as a fallback when explicit_width is false. Prevents column drift in terminals that miscalculate grapheme display widths.
hyperlinks: whether the terminal supports OSC 8 hyperlinks.
set_width_method t m sets the grapheme width computation method on both the current and next buffers. Use this after capability changes that affect width calculation to keep buffers consistent across swaps.
Layout
Sourceval resize : t->width:int ->height:int -> unit
resize t ~width ~height resizes all internal buffers to width x height. Grid contents are preserved where dimensions overlap; hit grids are cleared.
Raises Invalid_argument if width <= 0 or height <= 0.
reset t clears the next buffer, empties hit grids, zeros statistics, and resets frame timing. The current buffer (diff baseline) is left intact so the next render can efficiently clear previously rendered content. Post-processors and configuration are preserved.
last_metrics t is the metrics for the most recent frame.
Sourceval record_runtime_metrics :
t->frame_callback_ms:float ->overall_frame_ms:float ->stdout_ms:float ->
unit
record_runtime_metrics t ~frame_callback_ms ~overall_frame_ms ~stdout_ms supplements the most recent metrics with runtime measurements. All values are in milliseconds. Intended for higher-level runtimes that measure draw-call duration, total wall-clock time, and output flush time on behalf of the screen.
Direct access
Direct access to internal buffers for advanced use cases. These functions bypass the builder API.
query_hit t ~x ~y is the element ID at (x, y) in the current hit grid (i.e. regions from the most recent render). Returns 0 if out of bounds or no region is registered.
set_row_offset t n sets the vertical origin offset applied to all subsequent renders. Negative values are clamped to zero. Useful for inline primary-screen rendering.
active_height t is the number of rows containing non-blank content in the next buffer. Background-only cells (spaces) are ignored. Useful for sizing inline rendering regions.