1/**
2 * @file render.h
3 *
4 * Render state for creating high performance renderers.
5 */
6
7#ifndef GHOSTTY_VT_RENDER_H
8#define GHOSTTY_VT_RENDER_H
9
10#include <stdbool.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <ghostty/vt/allocator.h>
14#include <ghostty/vt/color.h>
15#include <ghostty/vt/terminal.h>
16#include <ghostty/vt/types.h>
17
18#ifdef __cplusplus
19extern "C" {
20#endif
21
22/** @defgroup render Render State
23 *
24 * Represents the state required to render a visible screen (a viewport)
25 * of a terminal instance. This is stateful and optimized for repeated
26 * updates from a single terminal instance and only updating dirty regions
27 * of the screen.
28 *
29 * The key design principle of this API is that it only needs read/write
30 * access to the terminal instance during the update call. This allows
31 * the render state to minimally impact terminal IO performance and also
32 * allows the renderer to be safely multi-threaded (as long as a lock is
33 * held during the update call to ensure exclusive access to the terminal
34 * instance).
35 *
36 * The basic usage of this API is:
37 *
38 * 1. Create an empty render state
39 * 2. Update it from a terminal instance whenever you need.
40 * 3. Read from the render state to get the data needed to draw your frame.
41 *
42 * ## Two-Phase Updates
43 *
44 * For callers that synchronize terminal access (e.g. a renderer thread
45 * sharing a lock with an IO thread), the update can be split into two
46 * phases to minimize the time the terminal must be held exclusively:
47 * ghostty_render_state_begin_update requires terminal access, while
48 * ghostty_render_state_end_update completes any deferred work using only
49 * memory owned by the render state. A typical renderer would lock, begin
50 * the update, unlock, and then end the update while the IO thread is free
51 * to continue modifying the terminal. ghostty_render_state_update is a
52 * convenience that performs both phases in one call.
53 *
54 * ## Dirty Tracking
55 *
56 * Dirty tracking is a key feature of the render state that allows renderers
57 * to efficiently determine what parts of the screen have changed and only
58 * redraw changed regions.
59 *
60 * The render state API keeps track of dirty state at two independent layers:
61 * a global dirty state that indicates whether the entire frame is clean,
62 * partially dirty, or fully dirty, and a per-row dirty state that allows
63 * tracking which rows in a partially dirty frame have changed.
64 *
65 * The user of the render state API is expected to unset both of these.
66 * The `update` call does not unset dirty state, it only updates it.
67 *
68 * An extremely important detail: setting one dirty state doesn't unset
69 * the other. For example, setting the global dirty state to false does not
70 * reset the row-level dirty flags. So, the caller of the render state API must
71 * be careful to manage both layers of dirty state correctly.
72 *
73 * ## Examples
74 *
75 * ### Creating and updating render state
76 * @snippet c-vt-render/src/main.c render-state-update
77 *
78 * ### Checking dirty state
79 * @snippet c-vt-render/src/main.c render-dirty-check
80 *
81 * ### Reading colors
82 * @snippet c-vt-render/src/main.c render-colors
83 *
84 * ### Reading cursor state
85 * @snippet c-vt-render/src/main.c render-cursor
86 *
87 * ### Iterating rows and cells
88 * @snippet c-vt-render/src/main.c render-row-iterate
89 *
90 * ### Resetting dirty state after rendering
91 * @snippet c-vt-render/src/main.c render-dirty-reset
92 *
93 * @{
94 */
95
96/**
97 * Dirty state of a render state after update.
98 *
99 * @ingroup render
100 */
101typedef enum GHOSTTY_ENUM_TYPED {
102 /** Not dirty at all; rendering can be skipped. */
103 GHOSTTY_RENDER_STATE_DIRTY_FALSE = 0,
104
105 /** Some rows changed; renderer can redraw incrementally. */
106 GHOSTTY_RENDER_STATE_DIRTY_PARTIAL = 1,
107
108 /** Global state changed; renderer should redraw everything. */
109 GHOSTTY_RENDER_STATE_DIRTY_FULL = 2,
110 GHOSTTY_RENDER_STATE_DIRTY_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
111} GhosttyRenderStateDirty;
112
113/**
114 * Visual style of the cursor.
115 *
116 * @ingroup render
117 */
118typedef enum GHOSTTY_ENUM_TYPED {
119 /** Bar cursor (DECSCUSR 5, 6). */
120 GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BAR = 0,
121
122 /** Block cursor (DECSCUSR 1, 2). */
123 GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK = 1,
124
125 /** Underline cursor (DECSCUSR 3, 4). */
126 GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_UNDERLINE = 2,
127
128 /** Hollow block cursor. */
129 GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK_HOLLOW = 3,
130 GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
131} GhosttyRenderStateCursorVisualStyle;
132
133/**
134 * Queryable data kinds for ghostty_render_state_get().
135 *
136 * @ingroup render
137 */
138typedef enum GHOSTTY_ENUM_TYPED {
139 /** Invalid / sentinel value. */
140 GHOSTTY_RENDER_STATE_DATA_INVALID = 0,
141
142 /** Viewport width in cells (uint16_t). */
143 GHOSTTY_RENDER_STATE_DATA_COLS = 1,
144
145 /** Viewport height in cells (uint16_t). */
146 GHOSTTY_RENDER_STATE_DATA_ROWS = 2,
147
148 /** Current dirty state (GhosttyRenderStateDirty). */
149 GHOSTTY_RENDER_STATE_DATA_DIRTY = 3,
150
151 /** Populate a pre-allocated GhosttyRenderStateRowIterator with row data
152 * from the render state (GhosttyRenderStateRowIterator). Row data is
153 * only valid as long as the underlying render state is not updated.
154 * It is unsafe to use row data after updating the render state.
155 * */
156 GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR = 4,
157
158 /** Default/current background color (GhosttyColorRgb). */
159 GHOSTTY_RENDER_STATE_DATA_COLOR_BACKGROUND = 5,
160
161 /** Default/current foreground color (GhosttyColorRgb). */
162 GHOSTTY_RENDER_STATE_DATA_COLOR_FOREGROUND = 6,
163
164 /** Cursor color when explicitly set by terminal state (GhosttyColorRgb).
165 * Returns GHOSTTY_INVALID_VALUE if no explicit cursor color is set;
166 * use COLOR_CURSOR_HAS_VALUE to check first. */
167 GHOSTTY_RENDER_STATE_DATA_COLOR_CURSOR = 7,
168
169 /** Whether an explicit cursor color is set (bool). */
170 GHOSTTY_RENDER_STATE_DATA_COLOR_CURSOR_HAS_VALUE = 8,
171
172 /** The active 256-color palette (GhosttyColorRgb[256]). */
173 GHOSTTY_RENDER_STATE_DATA_COLOR_PALETTE = 9,
174
175 /** The visual style of the cursor (GhosttyRenderStateCursorVisualStyle). */
176 GHOSTTY_RENDER_STATE_DATA_CURSOR_VISUAL_STYLE = 10,
177
178 /** Whether the cursor is visible based on terminal modes (bool). */
179 GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE = 11,
180
181 /** Whether the cursor should blink based on terminal modes (bool). */
182 GHOSTTY_RENDER_STATE_DATA_CURSOR_BLINKING = 12,
183
184 /** Whether the cursor is at a password input field (bool). */
185 GHOSTTY_RENDER_STATE_DATA_CURSOR_PASSWORD_INPUT = 13,
186
187 /** Whether the cursor is visible within the viewport (bool).
188 * If false, the cursor viewport position values are undefined. */
189 GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE = 14,
190
191 /** Cursor viewport x position in cells (uint16_t).
192 * Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
193 GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X = 15,
194
195 /** Cursor viewport y position in cells (uint16_t).
196 * Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
197 GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y = 16,
198
199 /** Whether the cursor is on the tail of a wide character (bool).
200 * Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
201 GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_WIDE_TAIL = 17,
202 GHOSTTY_RENDER_STATE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
203} GhosttyRenderStateData;
204
205/**
206 * Settable options for ghostty_render_state_set().
207 *
208 * @ingroup render
209 */
210typedef enum GHOSTTY_ENUM_TYPED {
211 /** Set dirty state (GhosttyRenderStateDirty). */
212 GHOSTTY_RENDER_STATE_OPTION_DIRTY = 0,
213 GHOSTTY_RENDER_STATE_OPTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
214} GhosttyRenderStateOption;
215
216/**
217 * Queryable data kinds for ghostty_render_state_row_get().
218 *
219 * @ingroup render
220 */
221typedef enum GHOSTTY_ENUM_TYPED {
222 /** Invalid / sentinel value. */
223 GHOSTTY_RENDER_STATE_ROW_DATA_INVALID = 0,
224
225 /** Whether the current row is dirty (bool). */
226 GHOSTTY_RENDER_STATE_ROW_DATA_DIRTY = 1,
227
228 /** The raw row value (GhosttyRow). */
229 GHOSTTY_RENDER_STATE_ROW_DATA_RAW = 2,
230
231 /** Populate a pre-allocated GhosttyRenderStateRowCells with cell data for
232 * the current row (GhosttyRenderStateRowCells). Cell data is only
233 * valid as long as the underlying render state is not updated.
234 * It is unsafe to use cell data after updating the render state. */
235 GHOSTTY_RENDER_STATE_ROW_DATA_CELLS = 3,
236
237 /** Row-local selected cell range (GhosttyRenderStateRowSelection). */
238 GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION = 4,
239 GHOSTTY_RENDER_STATE_ROW_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
240} GhosttyRenderStateRowData;
241
242/**
243 * Settable options for ghostty_render_state_row_set().
244 *
245 * @ingroup render
246 */
247typedef enum GHOSTTY_ENUM_TYPED {
248 /** Set dirty state for the current row (bool). */
249 GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY = 0,
250 GHOSTTY_RENDER_STATE_ROW_OPTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
251} GhosttyRenderStateRowOption;
252
253/**
254 * Row-local selection range.
255 *
256 * This struct uses the sized-struct ABI pattern. Initialize with
257 * GHOSTTY_INIT_SIZED(GhosttyRenderStateRowSelection) before querying
258 * GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION.
259 *
260 * Querying GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION returns GHOSTTY_NO_VALUE
261 * if the current row does not intersect the current selection.
262 *
263 * @ingroup render
264 */
265typedef struct {
266 /** Size of this struct in bytes. Must be set to sizeof(GhosttyRenderStateRowSelection). */
267 size_t size;
268
269 /** Start column of the row-local selection range, inclusive. */
270 uint16_t start_x;
271
272 /** End column of the row-local selection range, inclusive. */
273 uint16_t end_x;
274} GhosttyRenderStateRowSelection;
275
276/** Narrow ASCII cell returned by the optional whole-row render fast path. */
277typedef struct {
278 uint32_t codepoint;
279 uint32_t foreground;
280} GhosttyRenderStateAsciiCell;
281
282/**
283 * Render-state color information.
284 *
285 * This struct uses the sized-struct ABI pattern. Initialize with
286 * GHOSTTY_INIT_SIZED(GhosttyRenderStateColors) before calling
287 * ghostty_render_state_colors_get().
288 *
289 * Example:
290 * @code
291 * GhosttyRenderStateColors colors = GHOSTTY_INIT_SIZED(GhosttyRenderStateColors);
292 * GhosttyResult result = ghostty_render_state_colors_get(state, &colors);
293 * @endcode
294 *
295 * @ingroup render
296 */
297typedef struct {
298 /** Size of this struct in bytes. Must be set to sizeof(GhosttyRenderStateColors). */
299 size_t size;
300
301 /** The default/current background color for the render state. */
302 GhosttyColorRgb background;
303
304 /** The default/current foreground color for the render state. */
305 GhosttyColorRgb foreground;
306
307 /** The cursor color when explicitly set by terminal state. */
308 GhosttyColorRgb cursor;
309
310 /**
311 * True when cursor contains a valid explicit cursor color value.
312 * If this is false, the cursor color should be ignored; it will
313 * contain undefined data.
314 * */
315 bool cursor_has_value;
316
317 /** The active 256-color palette for this render state. */
318 GhosttyColorRgb palette[256];
319} GhosttyRenderStateColors;
320
321/**
322 * Create a new render state instance.
323 *
324 * @param allocator Pointer to allocator, or NULL to use the default allocator
325 * @param state Pointer to store the created render state handle
326 * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY on allocation
327 * failure
328 *
329 * @ingroup render
330 */
331GHOSTTY_API GhosttyResult ghostty_render_state_new(const GhosttyAllocator* allocator,
332 GhosttyRenderState* state);
333
334/**
335 * Free a render state instance.
336 *
337 * Releases all resources associated with the render state. After this call,
338 * the render state handle becomes invalid.
339 *
340 * @param state The render state handle to free (may be NULL)
341 *
342 * @ingroup render
343 */
344GHOSTTY_API void ghostty_render_state_free(GhosttyRenderState state);
345
346/**
347 * Update a render state instance from a terminal.
348 *
349 * This consumes terminal/screen dirty state in the same way as the internal
350 * render state update path.
351 *
352 * This is a convenience function that performs a full update in one call,
353 * equivalent to ghostty_render_state_begin_update immediately followed by
354 * ghostty_render_state_end_update. Callers that hold a lock over the
355 * terminal state should prefer calling the two phases directly so that the
356 * lock is only held for the begin phase.
357 *
358 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
359 * @param terminal The terminal handle to read from (NULL returns GHOSTTY_INVALID_VALUE)
360 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or
361 * `terminal` is NULL, GHOSTTY_OUT_OF_MEMORY if updating the state requires
362 * allocation and that allocation fails
363 *
364 * @ingroup render
365 */
366GHOSTTY_API GhosttyResult ghostty_render_state_update(GhosttyRenderState state,
367 GhosttyTerminal terminal);
368
369/**
370 * Begin an update of a render state instance from a terminal.
371 *
372 * Every begin must be completed with a ghostty_render_state_end_update call
373 * before the render state is read.
374 *
375 * This two-phase structure exists for callers that synchronize access to the
376 * terminal state (e.g. with a lock shared with an IO thread): only this
377 * function requires terminal access, so a caller can hold its lock for this
378 * call only and then call ghostty_render_state_end_update after releasing
379 * it. The end phase exclusively reads and writes memory owned by the render
380 * state, so it is safe to call while the terminal is being modified.
381 *
382 * Work that doesn't require terminal access may be deferred to the end phase
383 * to keep this call (and therefore lock hold time) as short as possible.
384 * Callers must treat the render state as incomplete until
385 * ghostty_render_state_end_update is called.
386 *
387 * This consumes terminal/screen dirty state in the same way as the internal
388 * render state update path.
389 *
390 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
391 * @param terminal The terminal handle to read from (NULL returns GHOSTTY_INVALID_VALUE)
392 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or
393 * `terminal` is NULL, GHOSTTY_OUT_OF_MEMORY if updating the state requires
394 * allocation and that allocation fails
395 *
396 * @ingroup render
397 */
398GHOSTTY_API GhosttyResult ghostty_render_state_begin_update(GhosttyRenderState state,
399 GhosttyTerminal terminal);
400
401/**
402 * Complete a prior ghostty_render_state_begin_update call by performing any
403 * deferred work.
404 *
405 * This only reads and writes memory owned by the render state, so it is safe
406 * to call while the terminal is being modified (no terminal synchronization
407 * is required). Calling this without a prior begin is a safe no-op.
408 *
409 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
410 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is
411 * NULL
412 *
413 * @ingroup render
414 */
415GHOSTTY_API GhosttyResult ghostty_render_state_end_update(GhosttyRenderState state);
416
417/**
418 * Get a value from a render state.
419 *
420 * The `out` pointer must point to a value of the type corresponding to the
421 * requested data kind (see GhosttyRenderStateData).
422 *
423 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
424 * @param data The data kind to query
425 * @param[out] out Pointer to receive the queried value
426 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is
427 * NULL or `data` is not a recognized enum value
428 *
429 * @ingroup render
430 */
431GHOSTTY_API GhosttyResult ghostty_render_state_get(GhosttyRenderState state,
432 GhosttyRenderStateData data,
433 void* out);
434
435/**
436 * Get multiple data fields from a render state in a single call.
437 *
438 * Each element in the keys array specifies a data kind, and the
439 * corresponding element in the values array receives the result.
440 *
441 * Processing stops at the first error; on success out_written
442 * is set to count, on error it is set to the index of the
443 * failing key (i.e. the number of values successfully written).
444 *
445 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
446 * @param count Number of key/value pairs
447 * @param keys Array of data kinds to query
448 * @param values Array of output pointers (types must match each key's
449 * documented output type)
450 * @param[out] out_written On return, receives the number of values
451 * successfully written (may be NULL)
452 * @return GHOSTTY_SUCCESS if all queries succeed
453 *
454 * @ingroup render
455 */
456GHOSTTY_API GhosttyResult ghostty_render_state_get_multi(
457 GhosttyRenderState state,
458 size_t count,
459 const GhosttyRenderStateData* keys,
460 void** values,
461 size_t* out_written);
462
463/**
464 * Set an option on a render state.
465 *
466 * The `value` pointer must point to a value of the type corresponding to the
467 * requested option kind (see GhosttyRenderStateOption).
468 *
469 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
470 * @param option The option to set
471 * @param[in] value Pointer to the value to set (NULL returns
472 * GHOSTTY_INVALID_VALUE)
473 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or
474 * `value` is NULL
475 *
476 * @ingroup render
477 */
478GHOSTTY_API GhosttyResult ghostty_render_state_set(GhosttyRenderState state,
479 GhosttyRenderStateOption option,
480 const void* value);
481
482/**
483 * Get the current color information from a render state.
484 *
485 * This writes as many fields as fit in the caller-provided sized struct.
486 * `out_colors->size` must be set by the caller (typically via
487 * GHOSTTY_INIT_SIZED(GhosttyRenderStateColors)).
488 *
489 * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
490 * @param[out] out_colors Sized output struct to receive render-state colors
491 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or
492 * `out_colors` is NULL, or if `out_colors->size` is smaller than
493 * `sizeof(size_t)`
494 *
495 * @ingroup render
496 */
497GHOSTTY_API GhosttyResult ghostty_render_state_colors_get(GhosttyRenderState state,
498 GhosttyRenderStateColors* out_colors);
499
500/**
501 * Create a new row iterator instance.
502 *
503 * All fields except the allocator are left undefined until populated
504 * via ghostty_render_state_get() with
505 * GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR.
506 *
507 * @param allocator Pointer to allocator, or NULL to use the default allocator
508 * @param[out] out_iterator On success, receives the created iterator handle
509 * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY on allocation
510 * failure
511 *
512 * @ingroup render
513 */
514GHOSTTY_API GhosttyResult ghostty_render_state_row_iterator_new(
515 const GhosttyAllocator* allocator,
516 GhosttyRenderStateRowIterator* out_iterator);
517
518/**
519 * Free a render-state row iterator.
520 *
521 * @param iterator The iterator handle to free (may be NULL)
522 *
523 * @ingroup render
524 */
525GHOSTTY_API void ghostty_render_state_row_iterator_free(GhosttyRenderStateRowIterator iterator);
526
527/**
528 * Move a render-state row iterator to the next row.
529 *
530 * Returns true if the iterator moved successfully and row data is
531 * available to read at the new position.
532 *
533 * @param iterator The iterator handle to advance (may be NULL)
534 * @return true if advanced to the next row, false if `iterator` is
535 * NULL or if the iterator has reached the end
536 *
537 * @ingroup render
538 */
539GHOSTTY_API bool ghostty_render_state_row_iterator_next(GhosttyRenderStateRowIterator iterator);
540
541/**
542 * Get a value from the current row in a render-state row iterator.
543 *
544 * The `out` pointer must point to a value of the type corresponding to the
545 * requested data kind (see GhosttyRenderStateRowData).
546 * Call ghostty_render_state_row_iterator_next() at least once before
547 * calling this function.
548 *
549 * @param iterator The iterator handle to query (NULL returns GHOSTTY_INVALID_VALUE)
550 * @param data The data kind to query
551 * @param[out] out Pointer to receive the queried value
552 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if
553 * `iterator` is NULL or the iterator is not positioned on a row
554 *
555 * @ingroup render
556 */
557GHOSTTY_API GhosttyResult ghostty_render_state_row_get(
558 GhosttyRenderStateRowIterator iterator,
559 GhosttyRenderStateRowData data,
560 void* out);
561
562/**
563 * Get multiple data fields from the current row in a single call.
564 *
565 * Each element in the keys array specifies a data kind, and the
566 * corresponding element in the values array receives the result.
567 *
568 * Processing stops at the first error; on success out_written
569 * is set to count, on error it is set to the index of the
570 * failing key (i.e. the number of values successfully written).
571 *
572 * @param iterator The iterator handle (NULL returns GHOSTTY_INVALID_VALUE)
573 * @param count Number of key/value pairs
574 * @param keys Array of data kinds to query
575 * @param values Array of output pointers (types must match each key's
576 * documented output type)
577 * @param[out] out_written On return, receives the number of values
578 * successfully written (may be NULL)
579 * @return GHOSTTY_SUCCESS if all queries succeed
580 *
581 * @ingroup render
582 */
583GHOSTTY_API GhosttyResult ghostty_render_state_row_get_multi(
584 GhosttyRenderStateRowIterator iterator,
585 size_t count,
586 const GhosttyRenderStateRowData* keys,
587 void** values,
588 size_t* out_written);
589
590/**
591 * Set an option on the current row in a render-state row iterator.
592 *
593 * The `value` pointer must point to a value of the type corresponding to the
594 * requested option kind (see GhosttyRenderStateRowOption).
595 * Call ghostty_render_state_row_iterator_next() at least once before
596 * calling this function.
597 *
598 * @param iterator The iterator handle to update (NULL returns GHOSTTY_INVALID_VALUE)
599 * @param option The option to set
600 * @param[in] value Pointer to the value to set (NULL returns
601 * GHOSTTY_INVALID_VALUE)
602 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if
603 * `iterator` is NULL or the iterator is not positioned on a row
604 *
605 * @ingroup render
606 */
607GHOSTTY_API GhosttyResult ghostty_render_state_row_set(
608 GhosttyRenderStateRowIterator iterator,
609 GhosttyRenderStateRowOption option,
610 const void* value);
611
612/**
613 * Create a new row cells instance.
614 *
615 * All fields except the allocator are left undefined until populated
616 * via ghostty_render_state_row_get() with
617 * GHOSTTY_RENDER_STATE_ROW_DATA_CELLS.
618 *
619 * You can reuse this value repeatedly with ghostty_render_state_row_get() to
620 * avoid allocating a new cells container for every row.
621 *
622 * @param allocator Pointer to allocator, or NULL to use the default allocator
623 * @param[out] out_cells On success, receives the created row cells handle
624 * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY on allocation
625 * failure
626 *
627 * @ingroup render
628 */
629GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_new(
630 const GhosttyAllocator* allocator,
631 GhosttyRenderStateRowCells* out_cells);
632
633/**
634 * Queryable data kinds for ghostty_render_state_row_cells_get().
635 *
636 * @ingroup render
637 */
638typedef enum GHOSTTY_ENUM_TYPED {
639 /** Invalid / sentinel value. */
640 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_INVALID = 0,
641
642 /** The raw cell value (GhosttyCell). */
643 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_RAW = 1,
644
645 /** The style for the current cell (GhosttyStyle). */
646 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_STYLE = 2,
647
648 /** The total number of grapheme codepoints including the base codepoint
649 * (uint32_t). Returns 0 if the cell has no text. */
650 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_LEN = 3,
651
652 /** Write grapheme codepoints into a caller-provided buffer (uint32_t*).
653 * The buffer must be at least graphemes_len elements. The base codepoint
654 * is written first, followed by any extra codepoints. */
655 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF = 4,
656
657 /** The resolved background color of the cell (GhosttyColorRgb).
658 * Flattens the three possible sources: content-tag bg_color_rgb,
659 * content-tag bg_color_palette (looked up in the palette), or the
660 * style's bg_color. Returns GHOSTTY_INVALID_VALUE if the cell has
661 * no background color, in which case the caller should use whatever
662 * default background color it wants (e.g. the terminal background). */
663 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_BG_COLOR = 5,
664
665 /** The resolved foreground color of the cell (GhosttyColorRgb).
666 * Resolves palette indices through the palette. Bold color handling
667 * is not applied; the caller should handle bold styling separately.
668 * Returns GHOSTTY_INVALID_VALUE if the cell has no explicit foreground
669 * color, in which case the caller should use whatever default foreground
670 * color it wants (e.g. the terminal foreground). */
671 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_FG_COLOR = 6,
672
673 /** Whether the cell is contained within the current selection (bool).
674 * This returns true when the cell's column is within the current row's
675 * row-local selection range, and false otherwise. Rendering policy for
676 * selected cells (colors, inversion, etc.) is left to the caller.
677 *
678 * Renderers that can draw cells in spans may be more efficient querying
679 * GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION once per row and applying that
680 * range directly, avoiding one C API call per cell for selection state. */
681 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_SELECTED = 7,
682
683 /** Whether the cell has any explicit styling (bool).
684 * This is equivalent to querying the raw cell's
685 * GHOSTTY_CELL_DATA_HAS_STYLING value, but avoids materializing the raw
686 * GhosttyCell for renderers that only need to know whether fetching the
687 * full style is necessary. */
688 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_HAS_STYLING = 8,
689
690 /**
691 * Encode the current cell's full grapheme cluster as UTF-8 into a
692 * caller-provided buffer (GhosttyBuffer).
693 *
694 * The base codepoint is encoded first, followed by any extra grapheme
695 * codepoints. Returns GHOSTTY_SUCCESS with len=0 when the cell has no text.
696 *
697 * If ptr is NULL or cap is too small for a non-empty cell, returns
698 * GHOSTTY_OUT_OF_SPACE without writing any bytes and sets len to the required
699 * buffer size in bytes.
700 */
701 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_UTF8 = 9,
702 GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
703} GhosttyRenderStateRowCellsData;
704
705/**
706 * Move a render-state row cells iterator to the next cell.
707 *
708 * Returns true if the iterator moved successfully and cell data is
709 * available to read at the new position.
710 *
711 * @param cells The row cells handle to advance (may be NULL)
712 * @return true if advanced to the next cell, false if `cells` is
713 * NULL or if the iterator has reached the end
714 *
715 * @ingroup render
716 */
717GHOSTTY_API bool ghostty_render_state_row_cells_next(GhosttyRenderStateRowCells cells);
718
719/**
720 * Move a render-state row cells iterator to a specific column.
721 *
722 * Positions the iterator at the given x (column) index so that
723 * subsequent reads return data for that cell.
724 *
725 * @param cells The row cells handle to reposition (NULL returns
726 * GHOSTTY_INVALID_VALUE)
727 * @param x The zero-based column index to select
728 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `cells`
729 * is NULL or `x` is out of range
730 *
731 * @ingroup render
732 */
733GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_select(
734 GhosttyRenderStateRowCells cells, uint16_t x);
735
736/**
737 * Get a value from the current cell in a render-state row cells iterator.
738 *
739 * The `out` pointer must point to a value of the type corresponding to the
740 * requested data kind (see GhosttyRenderStateRowCellsData).
741 * Call ghostty_render_state_row_cells_next() or
742 * ghostty_render_state_row_cells_select() at least once before
743 * calling this function.
744 *
745 * @param cells The row cells handle to query (NULL returns GHOSTTY_INVALID_VALUE)
746 * @param data The data kind to query
747 * @param[out] out Pointer to receive the queried value
748 * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if
749 * `cells` is NULL or the iterator is not positioned on a cell
750 *
751 * @ingroup render
752 */
753GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_get(
754 GhosttyRenderStateRowCells cells,
755 GhosttyRenderStateRowCellsData data,
756 void* out);
757
758/**
759 * Get multiple data fields from the current cell in a single call.
760 *
761 * Each element in the keys array specifies a data kind, and the
762 * corresponding element in the values array receives the result.
763 *
764 * Processing stops at the first error; on success out_written
765 * is set to count, on error it is set to the index of the
766 * failing key (i.e. the number of values successfully written).
767 *
768 * @param cells The row cells handle (NULL returns GHOSTTY_INVALID_VALUE)
769 * @param count Number of key/value pairs
770 * @param keys Array of data kinds to query
771 * @param values Array of output pointers (types must match each key's
772 * documented output type)
773 * @param[out] out_written On return, receives the number of values
774 * successfully written (may be NULL)
775 * @return GHOSTTY_SUCCESS if all queries succeed
776 *
777 * @ingroup render
778 */
779GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_get_multi(
780 GhosttyRenderStateRowCells cells,
781 size_t count,
782 const GhosttyRenderStateRowCellsData* keys,
783 void** values,
784 size_t* out_written);
785
786/**
787 * Export the row when every retained cell is narrow printable ASCII with only
788 * an optional foreground color. GHOSTTY_NO_VALUE selects the canonical API.
789 */
790GHOSTTY_API GhosttyResult ghostty_render_state_row_cells_ascii(
791 GhosttyRenderStateRowCells cells,
792 GhosttyRenderStateAsciiCell* out,
793 size_t capacity,
794 size_t* out_len);
795
796/**
797 * Free a row cells instance.
798 *
799 * @param cells The row cells handle to free (may be NULL)
800 *
801 * @ingroup render
802 */
803GHOSTTY_API void ghostty_render_state_row_cells_free(GhosttyRenderStateRowCells cells);
804
805/** @} */
806
807#ifdef __cplusplus
808}
809#endif
810
811#endif /* GHOSTTY_VT_RENDER_H */