- commit
- 66c8e0e
- parent
- bdbf795
- author
- Eric Bower
- date
- 2026-04-08 20:22:43 -0400 EDT
feat: change pty window size when new leader is set When we set a new client leader we send a Resize event request from the daemon to the client. So now whenever a user types into their stdin we set the new leader and then resize the window.
2 files changed,
+31,
-26
+1,
-0
1@@ -80,6 +80,7 @@ pub fn appendMessage(
2 tag: Tag,
3 data: []const u8,
4 ) !void {
5+ std.log.info("sending ipc message tag={s}", .{@tagName(tag)});
6 const header = Header{
7 .tag = tag,
8 .len = @intCast(data.len),
+30,
-26
1@@ -319,11 +319,6 @@ const Cfg = struct {
2 }
3 };
4
5-const EnsureSessionResult = struct {
6- created: bool,
7- is_daemon: bool,
8-};
9-
10 /// Daemon is responsible for managing a zmx session.
11 ///
12 /// It holds all the state for a running session. Instead of a single daemon for all sessions, we
13@@ -354,6 +349,11 @@ const Daemon = struct {
14 task_command: ?[]const []const u8 = null,
15 pty_write_buf: std.ArrayList(u8) = .empty,
16
17+ const EnsureSessionResult = struct {
18+ created: bool,
19+ is_daemon: bool,
20+ };
21+
22 pub fn deinit(self: *Daemon) void {
23 self.clients.deinit(self.alloc);
24 self.pty_write_buf.deinit(self.alloc);
25@@ -384,6 +384,15 @@ const Daemon = struct {
26 return false;
27 }
28
29+ fn setLeader(self: *Daemon, client: *Client) !void {
30+ std.log.info("setting new leader client_fd={d}", .{client.socket_fd});
31+ self.leader_client_fd = client.socket_fd;
32+ // Send a resize message to the client so it can send us back their window size
33+ // so we can resize the pty and ghostty state.
34+ try ipc.appendMessage(self.alloc, &client.write_buf, .Resize, "");
35+ client.has_pending_output = true;
36+ }
37+
38 /// Runs in the forked child. Either execs or returns an error (caller
39 /// must exit on error -- returning would fall through to parent code).
40 fn execChild(self: *Daemon) !noreturn {
41@@ -598,11 +607,7 @@ const Daemon = struct {
42 const isNewline = std.mem.indexOfScalar(u8, payload, '\r') != null;
43 const isUpArrow = std.mem.eql(u8, payload, "\x1b[A") or util.isUpArrow(payload);
44 if (isNewline or isUpArrow) {
45- std.log.info(
46- "setting new leader session={s} client_fd={d}",
47- .{ self.session_name, client.socket_fd },
48- );
49- self.leader_client_fd = client.socket_fd;
50+ try self.setLeader(client);
51 self.queuePtyInput(payload);
52 return;
53 }
54@@ -623,11 +628,7 @@ const Daemon = struct {
55 defer client.alloc.free(output);
56 // if there's no text output then this client is effectively read-only until they type
57 if (output.len > 0) {
58- std.log.info(
59- "setting new leader session={s} client_fd={d}",
60- .{ self.session_name, client.socket_fd },
61- );
62- self.leader_client_fd = client.socket_fd;
63+ try self.setLeader(client);
64 // new leader is set to this client so send *entire* payload
65 self.queuePtyInput(payload);
66 }
67@@ -669,11 +670,7 @@ const Daemon = struct {
68
69 // no leader is set so set one
70 if (self.leader_client_fd == null) {
71- std.log.info(
72- "setting new leader session={s} client_fd={d}",
73- .{ self.session_name, client.socket_fd },
74- );
75- self.leader_client_fd = client.socket_fd;
76+ try self.setLeader(client);
77 }
78
79 // only resize if leader
80@@ -704,11 +701,7 @@ const Daemon = struct {
81 ) !void {
82 if (payload.len != @sizeOf(ipc.Resize)) return;
83 if (self.leader_client_fd == null) {
84- std.log.info(
85- "setting new leader session={s} client_fd={d}",
86- .{ self.session_name, client.socket_fd },
87- );
88- self.leader_client_fd = client.socket_fd;
89+ try self.setLeader(client);
90 }
91 // only leader can resize
92 if (self.leader_client_fd != client.socket_fd) return;
93@@ -1020,7 +1013,7 @@ fn wait(cfg: *Cfg, session_names: std.ArrayList([]const u8)) !void {
94 }
95 }
96
97- std.Thread.sleep(3000 * std.time.ns_per_ms);
98+ std.Thread.sleep(1000 * std.time.ns_per_ms);
99 }
100 }
101
102@@ -1529,6 +1522,17 @@ fn clientLoop(client_sock_fd: i32) !void {
103 try stdout_buf.appendSlice(alloc, msg.payload);
104 }
105 },
106+ .Resize => {
107+ // daemon is asking for the client's window size usually in response
108+ // to this client being set as leader.
109+ const next_size = ipc.getTerminalSize(posix.STDOUT_FILENO);
110+ try ipc.appendMessage(
111+ alloc,
112+ &sock_write_buf,
113+ .Resize,
114+ std.mem.asBytes(&next_size),
115+ );
116+ },
117 else => {},
118 }
119 }