repos / zmx

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

commit
8640be5
parent
6e33c6b
author
Eric Bower
date
2025-11-26 20:09:54 -0500 EST
feat: handle resize events
1 files changed,  +41, -0
M src/main.zig
+41, -0
 1@@ -40,6 +40,29 @@ const c = switch (builtin.os.tag) {
 2     }),
 3 };
 4 
 5+var sigwinch_received: std.atomic.Value(bool) = std.atomic.Value(bool).init(false);
 6+
 7+fn handleSigwinch(_: i32, _: *const posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
 8+    sigwinch_received.store(true, .release);
 9+}
10+
11+fn setupSigwinchHandler() void {
12+    const act: posix.Sigaction = .{
13+        .handler = .{ .sigaction = handleSigwinch },
14+        .mask = posix.sigemptyset(),
15+        .flags = posix.SA.SIGINFO,
16+    };
17+    posix.sigaction(posix.SIG.WINCH, &act, null);
18+}
19+
20+fn getTerminalSize() ?ipc.Resize {
21+    var ws: c.struct_winsize = undefined;
22+    if (c.ioctl(posix.STDOUT_FILENO, c.TIOCGWINSZ, &ws) == 0) {
23+        return .{ .rows = ws.ws_row, .cols = ws.ws_col };
24+    }
25+    return null;
26+}
27+
28 const Client = struct {
29     alloc: std.mem.Allocator,
30     socket_fd: i32,
31@@ -415,6 +438,13 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
32     const alloc = std.heap.c_allocator;
33     defer posix.close(client_sock_fd);
34 
35+    setupSigwinchHandler();
36+
37+    // Send initial terminal size
38+    if (getTerminalSize()) |size| {
39+        ipc.send(client_sock_fd, .Resize, std.mem.asBytes(&size)) catch {};
40+    }
41+
42     var poll_fds = try std.ArrayList(posix.pollfd).initCapacity(alloc, 2);
43     defer poll_fds.deinit(alloc);
44 
45@@ -431,6 +461,16 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
46     _ = try posix.fcntl(stdin_fd, posix.F.SETFL, flags | posix.SOCK.NONBLOCK);
47 
48     while (true) {
49+        // Check for pending SIGWINCH
50+        if (sigwinch_received.swap(false, .acq_rel)) {
51+            if (getTerminalSize()) |size| {
52+                ipc.send(client_sock_fd, .Resize, std.mem.asBytes(&size)) catch |err| switch (err) {
53+                    error.BrokenPipe, error.ConnectionResetByPeer => return,
54+                    else => return err,
55+                };
56+            }
57+        }
58+
59         poll_fds.clearRetainingCapacity();
60 
61         try poll_fds.append(alloc, .{
62@@ -454,6 +494,7 @@ fn clientLoop(_: *Cfg, client_sock_fd: i32) !void {
63         }
64 
65         _ = posix.poll(poll_fds.items, -1) catch |err| {
66+            if (err == error.Interrupted) continue; // EINTR from signal, loop again
67             return err;
68         };
69