diff --git a/include/ghostty/vt/terminal.h b/include/ghostty/vt/terminal.h index 70f98f8a5..233389498 100644 --- a/include/ghostty/vt/terminal.h +++ b/include/ghostty/vt/terminal.h @@ -118,6 +118,24 @@ GhosttyResult ghostty_terminal_new(const GhosttyAllocator* allocator, */ void ghostty_terminal_free(GhosttyTerminal terminal); +/** + * Resize the terminal to the given dimensions. + * + * Changes the number of columns and rows in the terminal. The primary + * screen will reflow content if wraparound mode is enabled; the alternate + * screen does not reflow. If the dimensions are unchanged, this is a no-op. + * + * @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE) + * @param cols New width in cells (must be greater than zero) + * @param rows New height in cells (must be greater than zero) + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup terminal + */ +GhosttyResult ghostty_terminal_resize(GhosttyTerminal terminal, + uint16_t cols, + uint16_t rows); + /** * Write VT-encoded data to the terminal for processing. * diff --git a/src/lib_vt.zig b/src/lib_vt.zig index a4998f1b8..a76ed8446 100644 --- a/src/lib_vt.zig +++ b/src/lib_vt.zig @@ -145,6 +145,7 @@ comptime { @export(&c.sgr_attribute_value, .{ .name = "ghostty_sgr_attribute_value" }); @export(&c.terminal_new, .{ .name = "ghostty_terminal_new" }); @export(&c.terminal_free, .{ .name = "ghostty_terminal_free" }); + @export(&c.terminal_resize, .{ .name = "ghostty_terminal_resize" }); @export(&c.terminal_vt_write, .{ .name = "ghostty_terminal_vt_write" }); @export(&c.terminal_scroll_viewport, .{ .name = "ghostty_terminal_scroll_viewport" }); diff --git a/src/terminal/c/main.zig b/src/terminal/c/main.zig index be8da379e..f17b9065e 100644 --- a/src/terminal/c/main.zig +++ b/src/terminal/c/main.zig @@ -55,6 +55,7 @@ pub const paste_is_safe = paste.is_safe; pub const terminal_new = terminal.new; pub const terminal_free = terminal.free; +pub const terminal_resize = terminal.resize; pub const terminal_vt_write = terminal.vt_write; pub const terminal_scroll_viewport = terminal.scroll_viewport; diff --git a/src/terminal/c/terminal.zig b/src/terminal/c/terminal.zig index cf491597b..ef64e7c0e 100644 --- a/src/terminal/c/terminal.zig +++ b/src/terminal/c/terminal.zig @@ -82,6 +82,17 @@ pub fn scroll_viewport( }); } +pub fn resize( + terminal_: Terminal, + cols: size.CellCountInt, + rows: size.CellCountInt, +) callconv(.c) Result { + const t = terminal_ orelse return .invalid_value; + if (cols == 0 or rows == 0) return .invalid_value; + t.resize(t.gpa(), cols, rows) catch return .out_of_memory; + return .success; +} + pub fn free(terminal_: Terminal) callconv(.c) void { const t = terminal_ orelse return; @@ -192,6 +203,45 @@ test "scroll_viewport null" { scroll_viewport(null, .{ .tag = .top, .value = undefined }); } +test "resize" { + var t: Terminal = null; + try testing.expectEqual(Result.success, new( + &lib_alloc.test_allocator, + &t, + .{ + .cols = 80, + .rows = 24, + .max_scrollback = 10_000, + }, + )); + defer free(t); + + try testing.expectEqual(Result.success, resize(t, 40, 12)); + try testing.expectEqual(40, t.?.cols); + try testing.expectEqual(12, t.?.rows); +} + +test "resize null" { + try testing.expectEqual(Result.invalid_value, resize(null, 80, 24)); +} + +test "resize invalid value" { + var t: Terminal = null; + try testing.expectEqual(Result.success, new( + &lib_alloc.test_allocator, + &t, + .{ + .cols = 80, + .rows = 24, + .max_scrollback = 10_000, + }, + )); + defer free(t); + + try testing.expectEqual(Result.invalid_value, resize(t, 0, 24)); + try testing.expectEqual(Result.invalid_value, resize(t, 80, 0)); +} + test "vt_write" { var t: Terminal = null; try testing.expectEqual(Result.success, new(