diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/tty.zig | 39 |
1 files changed, 38 insertions, 1 deletions
diff --git a/src/tty.zig b/src/tty.zig index 7de76f4d..a2ea6462 100644 --- a/src/tty.zig +++ b/src/tty.zig @@ -47,6 +47,20 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void { const io = init.io; const gpa = init.gpa; + // SIGWINCH must never run vaxis's signal handler: it posts the winsize + // event through std.Io.Mutex/Condition, and when the signal lands on a + // thread blocked inside an Io.Threaded syscall region (pty readers in + // read(2), the main thread parked in queue.pop) a contended lock re-enters + // the Io machinery and Syscall.start hits `unreachable` — panic, then the + // panic-time terminal restore used to write through the same Io and + // recurse until stack overflow. Reproduced by resizing the outer terminal + // (e.g. a font-size change) while shells run. Block it here, before any + // thread exists (threads inherit the mask, so vaxis's handler never + // fires), and take it synchronously on the sigwait thread below instead. + var winch_set = posix.sigemptyset(); + posix.sigaddset(&winch_set, posix.SIG.WINCH); + posix.sigprocmask(posix.SIG.BLOCK, &winch_set, null); + var tty_buf: [0x10000]u8 = undefined; var tty = try vaxis.Tty.init(io, &tty_buf); defer tty.deinit(); // restore cooked termios LAST, after vx flushed its resets @@ -107,7 +121,9 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void { try loop.start(); defer loop.stop(); - try loop.installResizeHandler(); + // resize watcher: plain detached thread (not io.concurrent — teardown + // joins those, and sigwait never returns); dies with the process + (try std.Thread.spawn(.{}, winchWatch, .{ &loop, &vx, &tty })).detach(); vx.queryTerminal(tty.writer(), std.Io.Duration.fromMilliseconds(2)) catch {}; // now threads are fine: start a reader task per pty @@ -354,11 +370,32 @@ fn drainEffects( }; } +/// Consume SIGWINCH synchronously (it is blocked in every thread) and post +/// the new size as a winsize event from normal thread context — the one place +/// vaxis's Io-backed queue is safe to touch on a resize. +fn winchWatch(loop: *Loop, vx: *vaxis.Vaxis, tty: *vaxis.Tty) void { + var set = posix.sigemptyset(); + posix.sigaddset(&set, posix.SIG.WINCH); + while (true) { + var sig: c_int = 0; + if (libc.sigwait(&set, &sig) != 0) continue; + if (vx.state.in_band_resize) continue; // terminal reports via CSI 48 + const ws = tty.getWinsize() catch continue; + loop.postEvent(.{ .winsize = ws }) catch {}; + } +} + fn forkShell(cwd: ?[*:0]const u8, rows: u16, cols: u16) struct { file: std.Io.File, pid: posix.pid_t } { var master: c_int = undefined; const ws = posix.winsize{ .row = rows, .col = cols, .xpixel = 0, .ypixel = 0 }; const pid = forkpty(&master, null, null, &ws); if (pid == 0) { + // the blocked-SIGWINCH mask survives fork AND exec — unblock it or + // bash/vim in the pane would never see resizes (sigprocmask is + // async-signal-safe) + var set = posix.sigemptyset(); + posix.sigaddset(&set, posix.SIG.WINCH); + posix.sigprocmask(posix.SIG.UNBLOCK, &set, null); if (cwd) |c| _ = chdir(c); const argv: [4:null]?[*:0]const u8 = .{ bash_path, "--rcfile", "/tmp/pardes-osc133.bash", null }; _ = execv(bash_path, &argv); |
