summaryrefslogtreecommitdiff
path: root/src/tty/tty.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-02 13:13:53 -0300
committerGabriel Schneider <[email protected]>2026-08-10 09:17:07 -0300
commit487434a0b55fa7cf479f39481b51da0a7eda79e1 (patch)
treece410e0c52eb030e1de76079834e6697975c9c17 /src/tty/tty.zig
parentd59397d4146ffa23adbbe6dc99c64ebb22a93b2d (diff)
downloadpardes-487434a0b55fa7cf479f39481b51da0a7eda79e1.tar.gz
pardes-487434a0b55fa7cf479f39481b51da0a7eda79e1.zip
gui: draw image surfaces as GPU quads via glsl shaders
Diffstat (limited to 'src/tty/tty.zig')
-rw-r--r--src/tty/tty.zig43
1 files changed, 36 insertions, 7 deletions
diff --git a/src/tty/tty.zig b/src/tty/tty.zig
index e2fe10e8..f0013020 100644
--- a/src/tty/tty.zig
+++ b/src/tty/tty.zig
@@ -143,7 +143,12 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
// pane unless this is in the env BEFORE bash starts (the rc is too late)
if (comptime builtin.os.tag.isDarwin()) _ = setenv("BASH_SILENCE_DEPRECATION_WARNING", "1", 1);
- var kitty_handles: [pardes.MAX_PANES]?vaxis.Image = @splat(null);
+ const KittyCached = struct { serial: u32, image: vaxis.Image };
+ var kitty_handles: [pardes.MAX_PANES]?KittyCached = @splat(null);
+ defer for (&kitty_handles) |*slot| if (slot.*) |cached| {
+ vx.freeImage(tty.writer(), cached.image.id);
+ slot.* = null;
+ };
var ptys: [pardes.MAX_PANES]?Pty = @splat(null);
// 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)
@@ -392,7 +397,11 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
// `watch off` effects go into a queue nobody drains — drop the lot
// here. The new core emits its own `on`s as it builds its panes.
for (0..watches.len) |wid| watchPane(inotify_fd, &watches, @intCast(wid), null, 0);
- kitty_handles = @splat(null);
+ for (&kitty_handles) |*slot| if (slot.*) |cached| {
+ vx.freeImage(tty.writer(), cached.image.id);
+ slot.* = null;
+ };
+ nc.native_images = vx.caps.kitty_graphics;
core.deinit();
core = nc;
}
@@ -414,6 +423,10 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
if (caps_pending and vx.queries_done.load(.unordered)) {
caps_pending = false;
vx.enableDetectedFeatures(tty.writer()) catch {};
+ // The core was constructed before the asynchronous handshake.
+ // Advertise native pixels only now, after every reply preceding
+ // DA1 has updated the capability set.
+ core.native_images = vx.caps.kitty_graphics;
vx.queueRefresh();
}
@@ -438,23 +451,39 @@ pub fn run(init: std.process.Init, opts: pardes.Options) !void {
}
}
tz_cells.end();
- // pixel attachments (kitty graphics): transmit once per pane, then
- // re-place every frame (placements aren't persistent)
+ // Pixel attachments (kitty graphics): transmit once per pane
+ // generation, then re-place every frame (placements aren't
+ // persistent). A slot can be closed and reused for an unrelated pane,
+ // so the pane number by itself is not a cache key.
+ var kitty_seen: [pardes.MAX_PANES]bool = @splat(false);
for (surface.images[0..surface.nimages]) |maybe| {
const place = maybe orelse continue;
+ kitty_seen[place.pane] = true;
+ if (kitty_handles[place.pane]) |cached| if (cached.serial != place.serial) {
+ vx.freeImage(tty.writer(), cached.image.id);
+ kitty_handles[place.pane] = null;
+ };
if (kitty_handles[place.pane] == null and vx.caps.kitty_graphics) {
const enc = std.base64.standard.Encoder;
if (gpa.alloc(u8, enc.calcSize(place.rgba.len))) |b64| {
defer gpa.free(b64);
_ = enc.encode(b64, place.rgba);
- kitty_handles[place.pane] = vx.transmitPreEncodedImage(tty.writer(), b64, @intCast(place.iw), @intCast(place.ih), .rgba) catch null;
+ if (vx.transmitPreEncodedImage(tty.writer(), b64, @intCast(place.iw), @intCast(place.ih), .rgba) catch null) |handle|
+ kitty_handles[place.pane] = .{ .serial = place.serial, .image = handle };
} else |_| {}
}
- if (kitty_handles[place.pane]) |h| {
+ if (kitty_handles[place.pane]) |cached| {
const child = win.child(.{ .x_off = place.x, .y_off = place.y, .width = place.w, .height = place.h });
- h.draw(child, .{ .scale = .contain }) catch {};
+ cached.image.draw(child, .{ .scale = .contain }) catch {};
}
}
+ // Toggling PETSCII or closing a pane removes its attachment from the
+ // Surface. Release the terminal-side image then, not merely when that
+ // numeric pane slot happens to be reused.
+ for (&kitty_handles, kitty_seen) |*slot, seen| if (!seen) if (slot.*) |cached| {
+ vx.freeImage(tty.writer(), cached.image.id);
+ slot.* = null;
+ };
if (surface.cursor) |cur| {
win.showCursor(cur.x, cur.y);
// insert = beam, everything else = the terminal's default shape