vt: add GhosttyRenderStateRowCells opaque type

Add a new opaque RowCells type that wraps per-row cell data
(raw cells, graphemes, styles) for the C API. The caller
allocates a RowCells handle via row_cells_new, then populates
it by passing it to row_get with the new .cells data kind.
This queries the current row from the iterator and slices the
underlying MultiArrayList into the RowCellsWrapper fields.

The new type and functions are wired through main.zig,
lib_vt.zig, and the render.h C header.
This commit is contained in:
Mitchell Hashimoto
2026-03-20 07:21:36 -07:00
parent 33e81ffb75
commit 75b49051a3
4 changed files with 95 additions and 2 deletions

View File

@@ -197,6 +197,8 @@ comptime {
@export(&c.render_state_row_get, .{ .name = "ghostty_render_state_row_get" });
@export(&c.render_state_row_set, .{ .name = "ghostty_render_state_row_set" });
@export(&c.render_state_row_iterator_free, .{ .name = "ghostty_render_state_row_iterator_free" });
@export(&c.render_state_row_cells_new, .{ .name = "ghostty_render_state_row_cells_new" });
@export(&c.render_state_row_cells_free, .{ .name = "ghostty_render_state_row_cells_free" });
@export(&c.render_state_free, .{ .name = "ghostty_render_state_free" });
@export(&c.terminal_new, .{ .name = "ghostty_terminal_new" });
@export(&c.terminal_free, .{ .name = "ghostty_terminal_free" });

View File

@@ -47,6 +47,8 @@ pub const render_state_row_iterator_next = render.row_iterator_next;
pub const render_state_row_get = render.row_get;
pub const render_state_row_set = render.row_set;
pub const render_state_row_iterator_free = render.row_iterator_free;
pub const render_state_row_cells_new = render.row_cells_new;
pub const render_state_row_cells_free = render.row_cells_free;
pub const sgr_new = sgr.new;
pub const sgr_free = sgr.free;

View File

@@ -32,12 +32,23 @@ const RowIteratorWrapper = struct {
dirty: []bool,
};
const RowCellsWrapper = struct {
alloc: std.mem.Allocator,
x: ?size.CellCountInt,
raws: []const page.Cell,
graphemes: []const []const u21,
styles: []const Style,
};
/// C: GhosttyRenderState
pub const RenderState = ?*RenderStateWrapper;
/// C: GhosttyRenderStateRowIterator
pub const RowIterator = ?*RowIteratorWrapper;
/// C: GhosttyRenderStateRowCells
pub const RowCells = ?*RowCellsWrapper;
/// C: GhosttyRenderStateDirty
pub const Dirty = renderpkg.RenderState.Dirty;
@@ -253,8 +264,6 @@ pub fn colors_get(
return .success;
}
pub fn row_iterator_new(
alloc_: ?*const CAllocator,
state_: RenderState,
@@ -310,11 +319,38 @@ pub fn row_iterator_next(iterator_: RowIterator) callconv(.c) bool {
return true;
}
pub fn row_cells_new(
alloc_: ?*const CAllocator,
result: *RowCells,
) callconv(.c) Result {
const alloc = lib_alloc.default(alloc_);
const ptr = alloc.create(RowCellsWrapper) catch {
result.* = null;
return .out_of_memory;
};
ptr.* = .{
.alloc = alloc,
.x = undefined,
.raws = undefined,
.graphemes = undefined,
.styles = undefined,
};
result.* = ptr;
return .success;
}
pub fn row_cells_free(cells_: RowCells) callconv(.c) void {
const cells = cells_ orelse return;
const alloc = cells.alloc;
alloc.destroy(cells);
}
/// C: GhosttyRenderStateRowData
pub const RowData = enum(c_int) {
invalid = 0,
dirty = 1,
raw = 2,
cells = 3,
/// Output type expected for querying the data of the given kind.
pub fn OutType(comptime self: RowData) type {
@@ -322,6 +358,7 @@ pub const RowData = enum(c_int) {
.invalid => void,
.dirty => bool,
.raw => row.CRow,
.cells => RowCells,
};
}
};
@@ -370,6 +407,17 @@ fn rowGetTyped(
.invalid => return .invalid_value,
.dirty => out.* = it.dirty[y],
.raw => out.* = it.raws[y].cval(),
.cells => {
const cells = out.* orelse return .invalid_value;
const cell_data = it.cells[y].slice();
cells.* = .{
.alloc = cells.alloc,
.x = null,
.raws = cell_data.items(.raw),
.graphemes = cell_data.items(.grapheme),
.styles = cell_data.items(.style),
};
},
}
return .success;