diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 03:44:26 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 69f8e4703f6c1204f9f0958f264d2a4c4600026f (patch) | |
| tree | 01bab7f3d7cea8a557780c0e1263962752892732 /src | |
| parent | 29ae6460961f4f781088455b72946260113a9ad9 (diff) | |
| download | pardes-69f8e4703f6c1204f9f0958f264d2a4c4600026f.tar.gz pardes-69f8e4703f6c1204f9f0958f264d2a4c4600026f.zip | |
pty/run's busy names the program holding the terminal
busy said only that something ran. Where the host can tell (Linux, the
tty's foreground group's /proc comm, a new fg_name host call), it says
busy: <program> is running; elsewhere, busy as before. The GUI's host
gains the one small callback (render agent's file: gui.zig, fgName).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/detached/server.zig | 9 | ||||
| -rw-r--r-- | src/gui/gui.zig | 8 | ||||
| -rw-r--r-- | src/host_io.zig | 15 | ||||
| -rw-r--r-- | src/ninep/pty.zig | 7 | ||||
| -rw-r--r-- | src/pardes.zig | 5 | ||||
| -rw-r--r-- | src/tty/tty.zig | 7 |
6 files changed, 50 insertions, 1 deletions
diff --git a/src/detached/server.zig b/src/detached/server.zig index af299051..bb66b841 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -373,6 +373,7 @@ pub const Session = struct { .pty_signal = ptySignal, .close_pty = closePaneShell, .tty_taken = ttyTaken, + .fg_name = fgName, .kill_job = killJob, .write_file = writeFile, .write_dump = writeDump, @@ -544,6 +545,14 @@ pub const Session = struct { return host_io.ttyTaken(pt.pid, pt.fd); } + fn fgName(ctx: ?*anyopaque, pane: u8, buf: []u8) ?[]const u8 { + const s = of(ctx); + if (pane >= s.ptys.len) return null; + const pt = s.ptys[pane]; + if (pt.fd < 0) return null; + return host_io.foregroundName(pt.pid, pt.fd, buf); + } + fn killJob(ctx: ?*anyopaque, pane: u8) bool { const s = of(ctx); if (pane >= s.ptys.len) return false; diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 491373a8..de5da1e7 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -4045,6 +4045,7 @@ const Shell = struct { .pty_signal = ptySignal, .close_pty = closePaneShell, .tty_taken = ttyTaken, + .fg_name = fgName, .kill_job = killJob, .write_file = writeFile, .write_dump = writeDump, @@ -4507,6 +4508,13 @@ fn ttyTaken(ctx: ?*anyopaque, pane: u8) bool { return host_io.ttyTaken(pt.pid, pt.fd); } +fn fgName(ctx: ?*anyopaque, pane: u8, buf: []u8) ?[]const u8 { + const s = shellOf(ctx); + const pt = s.ptys[pane] orelse return null; + if (pt.fd < 0) return null; + return host_io.foregroundName(pt.pid, pt.fd, buf); +} + fn killJob(ctx: ?*anyopaque, pane: u8) bool { const s = shellOf(ctx); const pt = s.ptys[pane] orelse return false; diff --git a/src/host_io.zig b/src/host_io.zig index 10a85d8f..1db0dac9 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -25,6 +25,9 @@ pub const Host = struct { pty_signal: ?*const fn (ctx: ?*anyopaque, pane: u8, sig: pardes.PtySignal) void = null, close_pty: ?*const fn (ctx: ?*anyopaque, pane: u8) void = null, tty_taken: ?*const fn (ctx: ?*anyopaque, pane: u8) bool = null, + /// The name of the program holding the tty, when it is not the + /// shell (host_io.foregroundName), in `buf`. + fg_name: ?*const fn (ctx: ?*anyopaque, pane: u8, buf: []u8) ?[]const u8 = null, /// Answered at once, not queued as an effect: Kill asks while the /// job it saw running is still the one in the tty (host_io.killJob). kill_job: ?*const fn (ctx: ?*anyopaque, pane: u8) bool = null, @@ -1751,6 +1754,18 @@ pub fn killJob(shell_pid: libc.pid_t, master_fd: c_int) bool { return libc.kill(-fg, libc.SIG.TERM) == 0; } +/// The command name of the tty's foreground job, when that is not the +/// shell: what `pty/run` says is running. Linux only (/proc/<pgrp>/comm). +pub fn foregroundName(shell_pid: libc.pid_t, master_fd: c_int, buf: []u8) ?[]const u8 { + if (comptime builtin.os.tag != .linux) return null; + const fg = tcgetpgrp(master_fd); + if (fg <= 0 or fg == shell_pid) return null; + var name: [64:0]u8 = undefined; + const path = std.fmt.bufPrintSentinel(&name, "/proc/{d}/comm", .{@as(u32, @intCast(fg))}, 0) catch return null; + const comm = std.mem.trimEnd(u8, readProc(path, buf) orelse return null, "\n"); + return if (comm.len > 0) comm else null; +} + fn readProc(path: [*:0]const u8, buf: []u8) ?[]const u8 { const fd = libc.open(path, .{ .ACCMODE = .RDONLY }); if (fd < 0) return null; diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index 393516ce..6b84af61 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -189,7 +189,12 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { // marks: a nested shell's prompt (ssh, a shell with its own // integration) looks like an empty prompt to ghostty but is not the // shell this run knows. - answer(p, slot, "busy", .{}); + // Naming what runs, where the host can tell (Linux). + var name: [64]u8 = undefined; + if (p.hostForegroundName(id, &name)) |program| + answer(p, slot, "busy: {s} is running", .{program}) + else + answer(p, slot, "busy", .{}); } else { // Sent now if the shell is at its prompt, or at its first prompt // when it has not drawn one yet (noteMarks). diff --git a/src/pardes.zig b/src/pardes.zig index c103b9d7..d6859f86 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -5124,6 +5124,11 @@ pub const Pardes = struct { return ev; } + pub fn hostForegroundName(p: *const Pardes, id: usize, buf: []u8) ?[]const u8 { + const f = p.host.vtable.fg_name orelse return null; + return f(p.host.ctx, @intCast(id), buf); + } + pub fn hostTtyTaken(p: *const Pardes, id: usize) bool { const f = p.host.vtable.tty_taken orelse return false; return f(p.host.ctx, @intCast(id)); diff --git a/src/tty/tty.zig b/src/tty/tty.zig index 754b4b96..e7eddec4 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -946,6 +946,7 @@ const Shell = struct { .pty_signal = ptySignal, .close_pty = closePty, .tty_taken = ttyTaken, + .fg_name = fgName, .kill_job = killJob, .write_file = writeFile, .write_dump = writeDump, @@ -1295,6 +1296,12 @@ const Shell = struct { return host_io.ttyTaken(pt.pid, pt.file.handle); } + fn fgName(ctx: ?*anyopaque, pane: u8, buf: []u8) ?[]const u8 { + const s = of(ctx); + const pt = s.ptys[pane] orelse return null; + return host_io.foregroundName(pt.pid, pt.file.handle, buf); + } + /// The command panes' exits, told once their output is in (host_io). fn takeExits(s: *@This()) void { host_io.takeExits(s.core, &s.ptys, s, closeWatched); |
