vt: add size getter for render state

Add ghostty_render_state_size_get() to return cols and rows from the
current render state using out pointers. The C wrapper validates null
inputs, the symbol is wired through the C API export layers, and tests
cover success and invalid-value paths.
This commit is contained in:
Mitchell Hashimoto
2026-03-18 12:22:32 -07:00
parent 2876fb7a55
commit b830a0ee1d
4 changed files with 72 additions and 0 deletions

View File

@@ -95,6 +95,24 @@ GhosttyResult ghostty_render_state_new(const GhosttyAllocator* allocator,
GhosttyResult ghostty_render_state_update(GhosttyRenderState state,
GhosttyTerminal terminal);
/**
* Get the current viewport size from a render state.
*
* The returned values are the render-state dimensions in cells. These
* match the active viewport size from the most recent successful update.
*
* @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE)
* @param[out] out_cols On success, receives the viewport width in cells
* @param[out] out_rows On success, receives the viewport height in cells
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state`,
* `out_cols`, or `out_rows` is NULL
*
* @ingroup render
*/
GhosttyResult ghostty_render_state_size_get(GhosttyRenderState state,
uint16_t* out_cols,
uint16_t* out_rows);
/**
* Get the current dirty state of a render state.
*

View File

@@ -189,6 +189,7 @@ comptime {
@export(&c.formatter_free, .{ .name = "ghostty_formatter_free" });
@export(&c.render_state_new, .{ .name = "ghostty_render_state_new" });
@export(&c.render_state_update, .{ .name = "ghostty_render_state_update" });
@export(&c.render_state_size_get, .{ .name = "ghostty_render_state_size_get" });
@export(&c.render_state_dirty_get, .{ .name = "ghostty_render_state_dirty_get" });
@export(&c.render_state_dirty_set, .{ .name = "ghostty_render_state_dirty_set" });
@export(&c.render_state_free, .{ .name = "ghostty_render_state_free" });

View File

@@ -39,6 +39,7 @@ pub const formatter_free = formatter.free;
pub const render_state_new = render.new;
pub const render_state_free = render.free;
pub const render_state_update = render.update;
pub const render_state_size_get = render.size_get;
pub const render_state_dirty_get = render.dirty_get;
pub const render_state_dirty_set = render.dirty_set;

View File

@@ -2,6 +2,7 @@ const std = @import("std");
const testing = std.testing;
const lib_alloc = @import("../../lib/allocator.zig");
const CAllocator = lib_alloc.Allocator;
const size = @import("../size.zig");
const terminal_c = @import("terminal.zig");
const renderpkg = @import("../render.zig");
const Result = @import("result.zig").Result;
@@ -50,6 +51,20 @@ pub fn update(
return .success;
}
pub fn size_get(
state_: RenderState,
out_cols_: ?*size.CellCountInt,
out_rows_: ?*size.CellCountInt,
) callconv(.c) Result {
const state = state_ orelse return .invalid_value;
const out_cols = out_cols_ orelse return .invalid_value;
const out_rows = out_rows_ orelse return .invalid_value;
out_cols.* = state.state.cols;
out_rows.* = state.state.rows;
return .success;
}
pub fn dirty_get(
state_: RenderState,
out_dirty: *Dirty,
@@ -103,6 +118,33 @@ test "render: update invalid value" {
try testing.expectEqual(Result.invalid_value, update(state, null));
}
test "render: size get invalid value" {
var state: RenderState = null;
try testing.expectEqual(Result.success, new(
&lib_alloc.test_allocator,
&state,
));
defer free(state);
var cols: size.CellCountInt = 0;
var rows: size.CellCountInt = 0;
try testing.expectEqual(Result.invalid_value, size_get(
null,
&cols,
&rows,
));
try testing.expectEqual(Result.invalid_value, size_get(
state,
null,
&rows,
));
try testing.expectEqual(Result.invalid_value, size_get(
state,
&cols,
null,
));
}
test "render: dirty get/set invalid value" {
var state: RenderState = null;
try testing.expectEqual(Result.success, new(
@@ -181,4 +223,14 @@ test "render: update" {
terminal_c.vt_write(terminal, "hello", 5);
try testing.expectEqual(Result.success, update(state, terminal));
var cols: size.CellCountInt = 0;
var rows: size.CellCountInt = 0;
try testing.expectEqual(Result.success, size_get(
state,
&cols,
&rows,
));
try testing.expectEqual(@as(size.CellCountInt, 80), cols);
try testing.expectEqual(@as(size.CellCountInt, 24), rows);
}