summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/tty.zig39
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);