- commit
- cfe70f6
- parent
- 2b8ec0f
- author
- Eric Bower
- date
- 2025-12-19 20:10:43 -0500 EST
fix: correct cursor position when reattaching to session Serialize terminal state BEFORE resizing the terminal in handleInit. Previously, the resize was done first, which caused cursor reflow and moved the cursor position. The shell's SIGWINCH-triggered prompt redraw would then run after our snapshot was sent, leaving the cursor at the wrong position (end of line instead of after the prompt). By capturing the terminal state before resize, we preserve the correct cursor position from when the user detached.
1 files changed,
+17,
-11
+17,
-11
1@@ -175,18 +175,13 @@ const Daemon = struct {
2 if (payload.len != @sizeOf(ipc.Resize)) return;
3
4 const resize = std.mem.bytesToValue(ipc.Resize, payload);
5- var ws: c.struct_winsize = .{
6- .ws_row = resize.rows,
7- .ws_col = resize.cols,
8- .ws_xpixel = 0,
9- .ws_ypixel = 0,
10- };
11- _ = c.ioctl(pty_fd, c.TIOCSWINSZ, &ws);
12- try term.resize(self.alloc, resize.cols, resize.rows);
13-
14- std.log.debug("init resize rows={d} cols={d}", .{ resize.rows, resize.cols });
15
16+ // Serialize terminal state BEFORE resize to capture correct cursor position.
17+ // Resizing triggers reflow which can move the cursor, and the shell's
18+ // SIGWINCH-triggered redraw will run after our snapshot is sent.
19 if (self.has_pty_output) {
20+ const cursor = &term.screens.active.cursor;
21+ std.log.debug("cursor before serialize: x={d} y={d} pending_wrap={}", .{ cursor.x, cursor.y, cursor.pending_wrap });
22 if (serializeTerminalState(self.alloc, term)) |term_output| {
23 defer self.alloc.free(term_output);
24 ipc.appendMessage(self.alloc, &client.write_buf, .Output, term_output) catch |err| {
25@@ -195,6 +190,17 @@ const Daemon = struct {
26 client.has_pending_output = true;
27 }
28 }
29+
30+ var ws: c.struct_winsize = .{
31+ .ws_row = resize.rows,
32+ .ws_col = resize.cols,
33+ .ws_xpixel = 0,
34+ .ws_ypixel = 0,
35+ };
36+ _ = c.ioctl(pty_fd, c.TIOCSWINSZ, &ws);
37+ try term.resize(self.alloc, resize.cols, resize.rows);
38+
39+ std.log.debug("init resize rows={d} cols={d}", .{ resize.rows, resize.cols });
40 }
41
42 pub fn handleResize(self: *Daemon, pty_fd: i32, term: *ghostty_vt.Terminal, payload: []const u8) !void {
43@@ -1197,7 +1203,7 @@ fn serializeTerminalState(alloc: std.mem.Allocator, term: *ghostty_vt.Terminal)
44 .palette = false,
45 .modes = true,
46 .scrolling_region = true,
47- .tabstops = true,
48+ .tabstops = false, // tabstop restoration moves cursor after CUP, corrupting position
49 .pwd = true,
50 .keyboard = true,
51 .screen = .all,