From b830a0ee1d2d14f559a08191aa46ecb0ea1b524e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 18 Mar 2026 12:22:32 -0700 Subject: [PATCH] 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. --- include/ghostty/vt/render.h | 18 +++++++++++++ src/lib_vt.zig | 1 + src/terminal/c/main.zig | 1 + src/terminal/c/render.zig | 52 +++++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/include/ghostty/vt/render.h b/include/ghostty/vt/render.h index 705194c70..684da696f 100644 --- a/include/ghostty/vt/render.h +++ b/include/ghostty/vt/render.h @@ -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. * diff --git a/src/lib_vt.zig b/src/lib_vt.zig index f132189c8..250635d4b 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -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" }); diff --git a/src/terminal/c/main.zig b/src/terminal/c/main.zig index ebac8bd73..c1999ccea 100644 --- a/src/terminal/c/main.zig +++ b/src/terminal/c/main.zig @@ -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; diff --git a/src/terminal/c/render.zig b/src/terminal/c/render.zig index c1198caea..9104b4ac7 100644 --- a/src/terminal/c/render.zig +++ b/src/terminal/c/render.zig @@ -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); } -- 2.51.2