libghostty: expose row-local render selections

Render state already tracks the selected cell range for each viewport row,
but C renderers could only get the full terminal selection. That required
consumers to map global selection pins back into row-local spans themselves.

Add row selection data to the render-state row API. Querying the new row
data returns GHOSTTY_NO_VALUE for unselected rows and writes the inclusive
start and end columns for selected rows. The render example now demonstrates
setting a selection and reading the row-local range while iterating rows.
This commit is contained in:
Mitchell Hashimoto
2026-05-23 14:58:56 -07:00
parent ae03d3cae4
commit 24048ffd47
4 changed files with 141 additions and 2 deletions

View File

@@ -2,8 +2,8 @@
This contains an example of how to use the `ghostty-vt` render-state API
to create a render state, update it from terminal content, iterate rows
and cells, read styles and colors, inspect cursor state, and manage dirty
tracking.
and cells, read styles and colors, inspect cursor and row-local selection
state, and manage dirty tracking.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source

View File

@@ -46,6 +46,32 @@ int main(void) {
ghostty_terminal_vt_write(
terminal, (const uint8_t*)content, strlen(content));
// Select "underlined" on the second row. Render state exposes this
// later as a row-local selected cell range.
GhosttyGridRef selection_start = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint selection_start_pt = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = 0, .y = 1 } },
};
result = ghostty_terminal_grid_ref(
terminal, selection_start_pt, &selection_start);
assert(result == GHOSTTY_SUCCESS);
GhosttyGridRef selection_end = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint selection_end_pt = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = 9, .y = 1 } },
};
result = ghostty_terminal_grid_ref(terminal, selection_end_pt, &selection_end);
assert(result == GHOSTTY_SUCCESS);
GhosttySelection selection = GHOSTTY_INIT_SIZED(GhosttySelection);
selection.start = selection_start;
selection.end = selection_end;
result = ghostty_terminal_set(
terminal, GHOSTTY_TERMINAL_OPT_SELECTION, &selection);
assert(result == GHOSTTY_SUCCESS);
result = ghostty_render_state_update(render_state, terminal);
assert(result == GHOSTTY_SUCCESS);
//! [render-state-update]
@@ -154,6 +180,18 @@ int main(void) {
printf("Row %2d [%s]: ", row_index,
row_dirty ? "dirty" : "clean");
// Query the row-local selection range. Rows without a selection return
// GHOSTTY_NO_VALUE; selected rows return inclusive start/end columns.
GhosttyRenderStateRowSelection row_selection =
GHOSTTY_INIT_SIZED(GhosttyRenderStateRowSelection);
result = ghostty_render_state_row_get(
row_iter, GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION, &row_selection);
assert(result == GHOSTTY_SUCCESS || result == GHOSTTY_NO_VALUE);
if (result == GHOSTTY_SUCCESS) {
printf("selection=%u..%u ",
row_selection.start_x, row_selection.end_x);
}
// Get cells for this row (reuses the same cells handle).
result = ghostty_render_state_row_get(
row_iter, GHOSTTY_RENDER_STATE_ROW_DATA_CELLS, &cells);