161124e4
Heap-allocate vt.Terminal
a73x 2026-04-08 13:21
diff --git a/src/main.zig b/src/main.zig index ede23cc..6082879 100644 --- a/src/main.zig +++ b/src/main.zig @@ -209,7 +209,7 @@ fn runTerminal(alloc: std.mem.Allocator) !void { } if (ev.utf8_len > 0) { _ = try p.write(ev.utf8[0..ev.utf8_len]); } else if (try encodeKeyboardEvent(&term, ev, &key_buf)) |encoded| { } else if (try encodeKeyboardEvent(term, ev, &key_buf)) |encoded| { _ = try p.write(encoded); } } @@ -525,7 +525,7 @@ test "encodeKeyboardEvent encodes left arrow" { defer term.deinit(); var buf: [32]u8 = undefined; const encoded = (try encodeKeyboardEvent(&term, .{ const encoded = (try encodeKeyboardEvent(term, .{ .keysym = c.XKB_KEY_Left, .modifiers = .{}, .action = .press, diff --git a/src/vt.zig b/src/vt.zig index f50bb2e..c216316 100644 --- a/src/vt.zig +++ b/src/vt.zig @@ -3,8 +3,8 @@ //! ## ghostty-vt API notes (as of ghostty-1.3.2-dev) //! //! ### Terminal lifecycle //! var t: Terminal = try .init(alloc, .{ .cols = N, .rows = N }); //! defer t.deinit(alloc); //! const t = try Terminal.init(alloc, .{ .cols = N, .rows = N }); //! defer t.deinit(); //! try t.printString("raw text"); // writes plain text //! try t.resize(alloc, new_cols, new_rows); //! const s = try t.plainString(alloc); // heap copy of visible text @@ -89,53 +89,43 @@ pub const Terminal = struct { max_scrollback: u32 = 1000, }; pub fn init(alloc: std.mem.Allocator, opts: InitOptions) !Terminal { var inner = try ghostty_vt.Terminal.init(alloc, .{ pub fn init(alloc: std.mem.Allocator, opts: InitOptions) !*Terminal { const self = try alloc.create(Terminal); errdefer alloc.destroy(self); const inner = try ghostty_vt.Terminal.init(alloc, .{ .cols = @intCast(opts.cols), .rows = @intCast(opts.rows), .max_scrollback = @intCast(opts.max_scrollback), }); errdefer inner.deinit(alloc); // TerminalStream.init takes a Handler value. Handler contains // a pointer to the terminal, so we store inner first then set // the pointer during return below — but we need a stable address. // We initialise with a placeholder and fix up the pointer after // the struct is placed in its final location by the caller. const stream: ghostty_vt.TerminalStream = .init(.{ .terminal = &inner, }); const render_state: ghostty_vt.RenderState = .empty; return .{ self.* = .{ .alloc = alloc, .inner = inner, .stream = stream, .render_state = render_state, .stream = .init(.{ .terminal = &self.inner, }), .render_state = .empty, .hooks = .{}, }; } /// Fix up the internal stream pointer after the Terminal has been moved /// to its final memory location. Must be called once before write(). fn fixupStreamPointer(self: *Terminal) void { self.stream.handler.terminal = &self.inner; self.stream.handler.effects.write_pty = &streamWritePty; self.stream.handler.effects.title_changed = &streamTitleChanged; self.stream.handler.effects.size = &streamSize; self.stream.handler.effects.device_attributes = &streamDeviceAttributes; self.stream.handler.effects.xtversion = &streamXtversion; self.stream.handler.effects.color_scheme = &streamColorScheme; return self; } pub fn deinit(self: *Terminal) void { self.render_state.deinit(self.alloc); self.inner.deinit(self.alloc); self.alloc.destroy(self); } pub fn write(self: *Terminal, bytes: []const u8) void { self.fixupStreamPointer(); self.stream.nextSlice(bytes); } @@ -146,7 +136,6 @@ pub const Terminal = struct { ) void { self.hooks.write_pty_ctx = ctx; self.hooks.write_pty = callback; self.fixupStreamPointer(); } pub fn setTitleChangedCallback( @@ -156,12 +145,10 @@ pub const Terminal = struct { ) void { self.hooks.title_changed_ctx = ctx; self.hooks.title_changed = callback; self.fixupStreamPointer(); } pub fn setReportedSize(self: *Terminal, size: Size) void { self.hooks.reported_size = size; self.fixupStreamPointer(); } pub fn encodeKey( @@ -280,6 +267,8 @@ test "Terminal init/deinit" { .rows = 24, }); defer term.deinit(); try std.testing.expectEqual(&term.inner, term.stream.handler.terminal); } test "Terminal.write feeds plain text" {