diff options
Diffstat (limited to 'src/gui.zig')
| -rw-r--r-- | src/gui.zig | 89 |
1 files changed, 37 insertions, 52 deletions
diff --git a/src/gui.zig b/src/gui.zig index 516a6b79..c1881c7c 100644 --- a/src/gui.zig +++ b/src/gui.zig @@ -1,6 +1,6 @@ //! The SDL3 GPU shell: owns an SDL window + event loop, translates SDL input //! into core events, performs the core's effects (fork ptys, write them, -//! resize them — same duties as tty.zig, this is also native linux), and +//! resize them — same duties as tty.zig, this is also native), and //! rasterizes the core's Surface: one instanced quad per cell, glyphs from an //! stb_truetype R8 atlas. Test modes: PARDES_TEST_GRID=1 is headless (no SDL, //! stdin escape sequences in, text grid frames out); PARDES_TEST=1 keeps the @@ -13,10 +13,11 @@ const std = @import("std"); const builtin = @import("builtin"); const posix = std.posix; -const linux = std.os.linux; +const libc = std.c; const vaxis = @import("vaxis"); // test modes only: the stdin escape-seq parser const ghostty_vt = @import("ghostty-vt"); // 256-color palette for .index cells const pardes = @import("pardes.zig"); +const look = @import("look.zig"); const is_emscripten = builtin.os.tag == .emscripten; @@ -34,6 +35,12 @@ extern "c" fn _exit(status: c_int) noreturn; extern "c" fn emscripten_get_element_css_size(target: [*:0]const u8, width: *f64, height: *f64) c_int; const EMSCRIPTEN_RESULT_SUCCESS: c_int = 0; +// absolute shell path per OS: execv must not search PATH (no allocation +// between fork and exec) +const bash_path: [*:0]const u8 = if (builtin.os.tag == .linux) "/usr/bin/bash" else "/bin/bash"; +// TIOCSWINSZ: absent from std.c.T on darwin — _IOW('t', 103, winsize) +const TIOCSWINSZ: c_int = @bitCast(@as(u32, if (@hasDecl(posix.T, "IOCSWINSZ")) posix.T.IOCSWINSZ else 0x80087467)); + const log = std.log.scoped(.gui); const font_ttf = @embedFile("AdwaitaMono-Regular.ttf"); @@ -271,7 +278,7 @@ fn normCell(norm: f32, win: f32, cell: f32) u16 { // ---- pty plumbing: reader threads feed a mutex-protected queue ---- -const Pty = struct { fd: c_int, pid: linux.pid_t }; +const Pty = struct { fd: c_int, pid: libc.pid_t }; const Msg = union(enum) { output: struct { pane: u8, bytes: []u8 }, eof: u8 }; @@ -338,10 +345,9 @@ const Queue = struct { fn readPtyThread(gpa: std.mem.Allocator, fd: c_int, pane: u8, q: *Queue) void { var buf: [0x10000]u8 = undefined; while (true) { - const rc = linux.read(fd, &buf, buf.len); - const n: isize = @bitCast(rc); + const n = libc.read(fd, &buf, buf.len); if (n < 0) { - if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue; + if (libc.errno(n) == .INTR) continue; break; // EIO when the child exits: treat as EOF } if (n == 0) break; @@ -622,7 +628,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { var ptys: [pardes.MAX_PANES]?Pty = @splat(null); defer for (&ptys) |*slot| if (slot.*) |pt| { - _ = linux.close(pt.fd); + _ = libc.close(pt.fd); slot.* = null; }; var queue: Queue = .{ .gpa = gpa, .sdl_wake = true }; @@ -662,7 +668,7 @@ fn runNative(init: std.process.Init, opts_in: pardes.Options) !void { }, .eof => |id| { if (ptys[id]) |pt| { - _ = linux.close(pt.fd); + _ = libc.close(pt.fd); ptys[id] = null; } core.update(.{ .eof = .{ .pane = id } }); @@ -895,7 +901,7 @@ fn runGrid(init: std.process.Init, opts_in: pardes.Options) !void { var ptys: [pardes.MAX_PANES]?Pty = @splat(null); defer for (&ptys) |*slot| if (slot.*) |pt| { - _ = linux.close(pt.fd); + _ = libc.close(pt.fd); slot.* = null; }; var queue: Queue = .{ .gpa = gpa, .sdl_wake = false }; @@ -927,7 +933,7 @@ fn runGrid(init: std.process.Init, opts_in: pardes.Options) !void { }, .eof => |id| { if (ptys[id]) |pt| { - _ = linux.close(pt.fd); + _ = libc.close(pt.fd); ptys[id] = null; } core.update(.{ .eof = .{ .pane = id } }); @@ -981,8 +987,7 @@ const StdinFeed = struct { if ((fds[0].revents & (posix.POLL.HUP | posix.POLL.ERR)) != 0) out.eof = true; return out; } - const rc = linux.read(0, f.buf[f.fill..].ptr, f.buf.len - f.fill); - const rn: isize = @bitCast(rc); + const rn = libc.read(0, f.buf[f.fill..].ptr, f.buf.len - f.fill); if (rn < 0) return out; if (rn == 0) { out.eof = true; @@ -1433,12 +1438,7 @@ fn drainEffects( ptys[sp.pane] = pt; // report the pane's starting directory back to the core (tags) var lbuf: [1024]u8 = undefined; - var pbuf: [64]u8 = undefined; - if (std.fmt.bufPrintSentinel(&pbuf, "/proc/{d}/cwd", .{pt.pid}, 0) catch null) |path| { - const rc = linux.readlinkat(linux.AT.FDCWD, path, &lbuf, lbuf.len); - const n: isize = @bitCast(rc); - if (n > 0) core.setCwd(sp.pane, lbuf[0..@intCast(n)]); - } + if (look.shellCwd(pt.pid, &lbuf)) |wd| core.setCwd(sp.pane, wd); if (threads_ok) spawnReader(gpa, pt, sp.pane, queue); }, .write => |w| { @@ -1447,7 +1447,7 @@ fn drainEffects( .resize_pty => |rs| { if (ptys[rs.pane]) |pt| { const ws: posix.winsize = .{ .row = rs.rows, .col = rs.cols, .xpixel = 0, .ypixel = 0 }; - _ = posix.system.ioctl(pt.fd, posix.T.IOCSWINSZ, @intFromPtr(&ws)); + _ = posix.system.ioctl(pt.fd, TIOCSWINSZ, @intFromPtr(&ws)); } }, .open_link => {}, // native: look opens panes instead; nothing to launch @@ -1458,21 +1458,17 @@ fn drainEffects( if (f.path.len >= pathbuf.len) continue; @memcpy(pathbuf[0..f.path.len], f.path); pathbuf[f.path.len] = 0; - const rc = linux.open(pathbuf[0..f.path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644); - const sfd: isize = @bitCast(rc); - if (sfd < 0) continue; - const fd: c_int = @intCast(sfd); + const fd = libc.open(pathbuf[0..f.path.len :0], .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); + if (fd < 0) continue; writeFd(fd, f.content); - _ = linux.close(fd); + _ = libc.close(fd); }, .write_dump => { const out = core.dump_out orelse continue; - const rc = linux.open(pardes.dump.default_path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644); - const sfd: isize = @bitCast(rc); - if (sfd < 0) continue; - const fd: c_int = @intCast(sfd); + const fd = libc.open(pardes.dump.default_path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); + if (fd < 0) continue; writeFd(fd, out); - _ = linux.close(fd); + _ = libc.close(fd); }, .set_clipboard => { if (g == null) continue; @@ -1500,8 +1496,8 @@ fn forkShell(cwd: ?[*:0]const u8, rows: u16, cols: u16) Pty { const pid = forkpty(&master, null, null, &ws); if (pid == 0) { if (cwd) |cd| _ = chdir(cd); - const argv: [4:null]?[*:0]const u8 = .{ "/usr/bin/bash", "--rcfile", "/tmp/pardes-osc133.bash", null }; - _ = execv("/usr/bin/bash", &argv); + const argv: [4:null]?[*:0]const u8 = .{ bash_path, "--rcfile", "/tmp/pardes-osc133.bash", null }; + _ = execv(bash_path, &argv); _exit(127); } return .{ .fd = master, .pid = pid }; @@ -1510,11 +1506,7 @@ fn forkShell(cwd: ?[*:0]const u8, rows: u16, cols: u16) Pty { fn pollCwds(core: *pardes.Pardes, ptys: *[pardes.MAX_PANES]?Pty) void { for (ptys, 0..) |slot, id| if (slot) |pt| { var lbuf: [1024]u8 = undefined; - var pbuf: [64]u8 = undefined; - const path = std.fmt.bufPrintSentinel(&pbuf, "/proc/{d}/cwd", .{pt.pid}, 0) catch continue; - const rc = linux.readlinkat(linux.AT.FDCWD, path, &lbuf, lbuf.len); - const n: isize = @bitCast(rc); - if (n > 0) core.setCwd(id, lbuf[0..@intCast(n)]); + if (look.shellCwd(pt.pid, &lbuf)) |cwd| core.setCwd(id, cwd); }; } @@ -2073,11 +2065,9 @@ fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u const tmp_path = std.fmt.bufPrintSentinel(&tmp_buf, "{s}/latest.ppm.tmp", .{g.capture_dir}, 0) catch return error.CapturePathTooLong; const final_path = std.fmt.bufPrintSentinel(&final_buf, "{s}/latest.ppm", .{g.capture_dir}, 0) catch return error.CapturePathTooLong; - const orc = linux.open(tmp_path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644); - const sfd: isize = @bitCast(orc); - if (sfd < 0) return error.CaptureWriteFailed; - const fd: c_int = @intCast(sfd); - defer _ = linux.close(fd); + const fd = libc.open(tmp_path, .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); + if (fd < 0) return error.CaptureWriteFailed; + defer _ = libc.close(fd); var header: [64]u8 = undefined; const hdr = std.fmt.bufPrint(&header, "P6\n{d} {d}\n255\n", .{ width, height }) catch return error.CaptureWriteFailed; @@ -2096,9 +2086,7 @@ fn writeCapturePpm(g: *Gui, gpa: std.mem.Allocator, pixels: []const u8, width: u } writeFd(fd, row_rgb); } - const rrc = linux.rename(tmp_path, final_path); - const rs: isize = @bitCast(rrc); - if (rs < 0) return error.CaptureWriteFailed; + if (libc.rename(tmp_path, final_path) != 0) return error.CaptureWriteFailed; } // ===================================================================== @@ -2377,21 +2365,18 @@ fn envU16(env: *std.process.Environ.Map, name: []const u8) ?u16 { } fn writeFile(data: []const u8) void { - const rc = linux.open("/tmp/pardes-osc133.bash", .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, 0o644); - const sfd: isize = @bitCast(rc); - if (sfd < 0) return; - const fd: c_int = @intCast(sfd); - defer _ = linux.close(fd); + const fd = libc.open("/tmp/pardes-osc133.bash", .{ .ACCMODE = .WRONLY, .CREAT = true, .TRUNC = true }, @as(libc.mode_t, 0o644)); + if (fd < 0) return; + defer _ = libc.close(fd); writeFd(fd, data); } fn writeFd(fd: c_int, data: []const u8) void { var off: usize = 0; while (off < data.len) { - const rc = linux.write(fd, data[off..].ptr, data.len - off); - const n: isize = @bitCast(rc); + const n = libc.write(fd, data[off..].ptr, data.len - off); if (n < 0) { - if (n == -@as(isize, @intFromEnum(linux.E.INTR))) continue; + if (libc.errno(n) == .INTR) continue; return; } off += @intCast(n); |
