repos / zmx

session persistence for terminal processes
git clone https://github.com/neurosnap/zmx.git

commit
b8c162e
parent
3018787
author
Eric Bower
date
2025-12-20 20:47:42 -0500 EST
fix: timing issue with first attach and sending terminal state

On first attach we don't want to send the terminal state but there
appears to be a timing issue where sometimes we already have pty output
before the Init handler can complete.
1 files changed,  +8, -1
M src/main.zig
+8, -1
 1@@ -128,6 +128,7 @@ const Daemon = struct {
 2     pid: i32,
 3     command: ?[]const []const u8 = null,
 4     has_pty_output: bool = false,
 5+    has_had_client: bool = false,
 6 
 7     pub fn deinit(self: *Daemon) void {
 8         self.clients.deinit(self.alloc);
 9@@ -179,10 +180,13 @@ const Daemon = struct {
10         // Serialize terminal state BEFORE resize to capture correct cursor position.
11         // Resizing triggers reflow which can move the cursor, and the shell's
12         // SIGWINCH-triggered redraw will run after our snapshot is sent.
13-        if (self.has_pty_output) {
14+        // Only serialize on re-attach (has_had_client), not first attach, to avoid
15+        // interfering with shell initialization (DA1 queries, etc.)
16+        if (self.has_pty_output and self.has_had_client) {
17             const cursor = &term.screens.active.cursor;
18             std.log.debug("cursor before serialize: x={d} y={d} pending_wrap={}", .{ cursor.x, cursor.y, cursor.pending_wrap });
19             if (serializeTerminalState(self.alloc, term)) |term_output| {
20+                std.log.debug("serialize terminal state", .{});
21                 defer self.alloc.free(term_output);
22                 ipc.appendMessage(self.alloc, &client.write_buf, .Output, term_output) catch |err| {
23                     std.log.warn("failed to buffer terminal state for client err={s}", .{@errorName(err)});
24@@ -200,6 +204,9 @@ const Daemon = struct {
25         _ = c.ioctl(pty_fd, c.TIOCSWINSZ, &ws);
26         try term.resize(self.alloc, resize.cols, resize.rows);
27 
28+        // Mark that we've had a client init, so subsequent clients get terminal state
29+        self.has_had_client = true;
30+
31         std.log.debug("init resize rows={d} cols={d}", .{ resize.rows, resize.cols });
32     }
33