- commit
- ed69a16
- parent
- 23203f8
- author
- Eric Bower
- date
- 2025-10-12 22:25:24 -0400 EDT
fix: vt.resize on attach fix: filter out characters ghostty cannot handle
1 files changed,
+50,
-28
+50,
-28
1@@ -304,7 +304,7 @@ fn handleMessage(client: *Client, data: []const u8) !void {
2 .attach_session_request => {
3 const parsed = try protocol.parseMessage(protocol.AttachSessionRequest, client.allocator, data);
4 defer parsed.deinit();
5- std.debug.print("Handling attach request for session: {s}\n", .{parsed.value.payload.session_name});
6+ std.debug.print("Handling attach request for session: {s} ({}x{})\n", .{ parsed.value.payload.session_name, parsed.value.payload.cols, parsed.value.payload.rows });
7 try handleAttachSession(client.server_ctx, client, parsed.value.payload.session_name, parsed.value.payload.rows, parsed.value.payload.cols);
8 },
9 .detach_session_request => {
10@@ -551,19 +551,21 @@ fn handleAttachSession(ctx: *ServerContext, client: *Client, session_name: []con
11 break :blk new_session;
12 };
13
14- // Update libghostty-vt terminal size
15- try session.vt.resize(session.allocator, cols, rows);
16+ // Update libghostty-vt terminal size and PTY window size
17+ // Only resize if we have valid dimensions
18+ if (rows > 0 and cols > 0) {
19+ try session.vt.resize(session.allocator, cols, rows);
20
21- // Update PTY window size
22- var ws = c.struct_winsize{
23- .ws_row = rows,
24- .ws_col = cols,
25- .ws_xpixel = 0,
26- .ws_ypixel = 0,
27- };
28- const result = c.ioctl(session.pty_master_fd, c.TIOCSWINSZ, &ws);
29- if (result < 0) {
30- return error.IoctlFailed;
31+ var ws = c.struct_winsize{
32+ .ws_row = rows,
33+ .ws_col = cols,
34+ .ws_xpixel = 0,
35+ .ws_ypixel = 0,
36+ };
37+ const result = c.ioctl(session.pty_master_fd, c.TIOCSWINSZ, &ws);
38+ if (result < 0) {
39+ return error.IoctlFailed;
40+ }
41 }
42
43 // Mark client as attached
44@@ -591,21 +593,28 @@ fn handleAttachSession(ctx: *ServerContext, client: *Client, session_name: []con
45 // If reattaching, send the scrollback buffer (raw PTY output with colors)
46 // Limit to last 64KB to avoid huge JSON messages
47 if (is_reattach and session.buffer.items.len > 0) {
48- std.debug.print("Sending scrollback buffer: {d} bytes total\n", .{session.buffer.items.len});
49-
50- const max_buffer_size = 64 * 1024;
51- const buffer_start = if (session.buffer.items.len > max_buffer_size)
52- session.buffer.items.len - max_buffer_size
53- else
54- 0;
55- const buffer_slice = session.buffer.items[buffer_start..];
56-
57- std.debug.print("Sending slice: {d} bytes (from offset {d})\n", .{ buffer_slice.len, buffer_start });
58-
59+ const buffer_slice = try session.vt.plainStringUnwrapped(client.allocator);
60 try protocol.writeJson(ctx.allocator, client.fd, .pty_out, protocol.PtyOutput{
61 .text = buffer_slice,
62 });
63 std.debug.print("Sent scrollback buffer to client fd={d}\n", .{client.fd});
64+ defer client.allocator.free(buffer_slice);
65+ //
66+ // std.debug.print("Sending scrollback buffer: {d} bytes total\n", .{session.buffer.items.len});
67+ //
68+ // const max_buffer_size = 64 * 1024;
69+ // const buffer_start = if (session.buffer.items.len > max_buffer_size)
70+ // session.buffer.items.len - max_buffer_size
71+ // else
72+ // 0;
73+ // const buffer_slice = session.buffer.items[buffer_start..];
74+ //
75+ // std.debug.print("Sending slice: {d} bytes (from offset {d})\n", .{ buffer_slice.len, buffer_start });
76+ //
77+ // try protocol.writeJson(ctx.allocator, client.fd, .pty_out, protocol.PtyOutput{
78+ // .text = buffer_slice,
79+ // });
80+ // std.debug.print("Sent scrollback buffer to client fd={d}\n", .{client.fd});
81 }
82 }
83
84@@ -852,10 +861,23 @@ fn readPtyCallback(
85 std.debug.print("Buffer append error: {s}\n", .{@errorName(err)});
86 };
87
88- // ALWAYS parse through libghostty-vt to maintain state
89- session.vt_stream.nextSlice(valid_data) catch |err| {
90- std.debug.print("VT parse error: {s}\n", .{@errorName(err)});
91- };
92+ // Parse through libghostty-vt byte-by-byte to handle invalid data
93+ // This is necessary because binary data (like /dev/urandom) can cause
94+ // panics in @enumFromInt when high bytes appear during escape sequences
95+ for (valid_data) |byte| {
96+ // Skip high bytes when parser is not in ground state to avoid
97+ // @enumFromInt panic in execute() which expects u7 (0-127)
98+ if (session.vt_stream.parser.state != .ground and byte > 127) {
99+ // Reset to ground state and skip this byte
100+ session.vt_stream.parser.state = .ground;
101+ continue;
102+ }
103+ session.vt_stream.next(byte) catch |err| {
104+ std.debug.print("VT parse error at byte 0x{x}: {s}\n", .{ byte, @errorName(err) });
105+ // Reset to ground state on any error
106+ session.vt_stream.parser.state = .ground;
107+ };
108+ }
109
110 // Only proxy to clients if someone is attached
111 if (session.attached_clients.count() > 0 and valid_len > 0) {