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