summaryrefslogtreecommitdiff
path: root/src/tty/tty.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-14 13:40:29 -0300
committerGabriel Schneider <[email protected]>2026-08-15 11:57:13 -0300
commitf67fec978a9296c651ec06bd2f43686d34ff86ee (patch)
treecbf5568883e5888398e0887093fc5afc524fd54d /src/tty/tty.zig
parent9280c597b000eed661fd98793e182fdcb640f6cd (diff)
downloadpardes-f67fec978a9296c651ec06bd2f43686d34ff86ee.tar.gz
pardes-f67fec978a9296c651ec06bd2f43686d34ff86ee.zip
look: richer path/range parsing, pdf rendering, corner-drag and stepgrain snapshots
Diffstat (limited to 'src/tty/tty.zig')
-rw-r--r--src/tty/tty.zig26
1 files changed, 24 insertions, 2 deletions
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index eadc3a9d..f6d095e5 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -456,6 +456,11 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
// per-slot spawn generation: a reused pane id ignores the old shell's
// late pty_eof (which would otherwise close the NEW pty on that slot)
var gens: [pardes.MAX_PANES]u32 = @splat(0);
+ // ...and the core's one way to ask about those ptys: is a pane's tty still
+ // the prompt we forked? Installed here rather than polled per frame (see
+ // the cwd loop) and re-installed on a replay core, which owns a fresh set
+ // of panes over this same table.
+ core.tty_query = .{ .ctx = &ptys, .taken = &ttyTakenAt };
// the single in-flight language query (see the .lsp effect)
var lsp_task: ?std.Io.Future(anyerror!void) = null;
// Selection filters may overlap: a second submit supersedes the first in
@@ -811,6 +816,7 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
while (image_iterator.next()) |handle| vx.freeImage(tty.writer(), handle.id);
kitty_handles.clearRetainingCapacity();
nc.native_images = vx.caps.kitty_graphics;
+ nc.tty_query = .{ .ctx = &ptys, .taken = &ttyTakenAt };
core.deinit();
core = nc;
if (comptime pardes.pdf_enabled) {
@@ -822,7 +828,10 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
}
}
- // live cwd for tags/look: cheap per-pane lookup, per frame
+ // live cwd for tags/look: cheap per-pane lookup, per frame. Whether the
+ // pane's tty still belongs to the prompt we forked is NOT polled here —
+ // it is a walk through /proc and nothing draws it, so the core pulls it
+ // through ttyQuery below, at the Exec that cares.
for (&ptys, 0..) |*slot, id| if (slot.*) |pt| {
var lbuf: [1024]u8 = undefined;
if (look.shellCwd(pt.pid, &lbuf)) |cwd| core.setCwd(id, cwd);
@@ -989,7 +998,9 @@ fn drainEffects(
}
const child = forkShell(core.shellBin(), cwd_z, core.screen_h, core.screen_w);
ptys[sp.pane] = .{ .file = child.file, .pid = child.pid, .reader = .{ .any_future = null, .result = {} } };
- // report the pane's starting directory back to the core (tags)
+ // report the pane's starting directory back to the core (tags). The
+ // slot needs no occupancy reset: nothing is remembered, and the next
+ // Exec asks about the shell that is there now.
var lbuf: [1024]u8 = undefined;
if (look.shellCwd(child.pid, &lbuf)) |wd| core.setCwd(sp.pane, wd);
if (threads_ok) {
@@ -1275,6 +1286,17 @@ fn winchWatch(loop: *Loop, vx: *vaxis.Vaxis, tty: *vaxis.Tty) void {
}
}
+/// The core's `tty_query`: answer for one pane out of this host's pty table.
+/// Lazy by construction — it runs only where the core is about to type a
+/// command line, so the /proc walk costs nothing on an ordinary frame. A pane
+/// with no pty of ours (a document, a slot whose shell already died) is not a
+/// terminal a program can be holding.
+fn ttyTakenAt(ctx: ?*anyopaque, pane: usize) bool {
+ const table: *const [pardes.MAX_PANES]?Pty = @ptrCast(@alignCast(ctx orelse return false));
+ const pt = table[pane] orelse return false;
+ return look.ttyTaken(pt.pid, pt.file.handle);
+}
+
fn forkShell(bin: []const u8, cwd: ?[*:0]const u8, rows: u16, cols: u16) struct { file: std.Io.File, pid: posix.pid_t } {
var master: c_int = undefined;
// resolved BEFORE the fork, into this frame, which the child inherits: