summaryrefslogtreecommitdiff
path: root/src/terminal.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal.zig')
-rw-r--r--src/terminal.zig105
1 files changed, 104 insertions, 1 deletions
diff --git a/src/terminal.zig b/src/terminal.zig
index e6304c0a..0850267e 100644
--- a/src/terminal.zig
+++ b/src/terminal.zig
@@ -9,6 +9,7 @@ const Surface = pardes.Surface;
const modal = @import("modal.zig");
const tracy = @import("tracy.zig");
const dump = @import("dump.zig");
+const tagline = @import("tagline.zig");
const Key = pardes.Key;
const Pane = panes.Pane;
const EditText = panes.EditText;
@@ -2797,6 +2798,36 @@ fn syncPixels(p: *const Pardes, pane: *Pane) void {
vt.height_px = @as(u32, vt.rows) * p.cell_pixels.h;
}
+/// The Petscii builtin on a terminal: its kitty graphics as glyph art,
+/// or back to pixels where the shell draws pixels.
+pub fn togglePetscii(pane: *Pane) void {
+ if (comptime !images_enabled) return;
+ const state = pane.terminal orelse return;
+ state.images.petscii = !state.images.petscii;
+}
+
+/// Whether the pane draws its kitty graphics as glyph art by choice; null
+/// for a pane with no emulator.
+pub fn petscii(pane: *const Pane) ?bool {
+ if (comptime !images_enabled) return null;
+ const state = pane.terminal orelse return null;
+ return state.images.petscii;
+}
+
+/// A new terminal takes TermImages: pixels, or glyph art.
+pub fn adoptSettings(p: *const Pardes, pane: *Pane) void {
+ if (comptime !images_enabled) return;
+ const state = pane.terminal orelse return;
+ state.images.petscii = p.settings.term_images == .petscii;
+}
+
+/// Whether the screen showing holds any kitty graphics image.
+pub fn hasImages(pane: *const Pane) bool {
+ if (comptime !images_enabled) return false;
+ const state = pane.terminal orelse return false;
+ return state.vt.screens.active.kitty_images.images.count() > 0;
+}
+
/// Draw the pane's kitty graphics images over its body rows (after the
/// rows and their colours: placeholder cells are blanked here).
pub fn drawImages(p: *Pardes, s: *Surface, pane: *Pane, id: usize, tx: u16, tw: u16, body_y: u16, body_h: u16) void {
@@ -2828,7 +2859,7 @@ pub fn drawImages(p: *Pardes, s: *Surface, pane: *Pane, id: usize, tx: u16, tw:
}
// Nothing to draw, no placeholder to blank and nothing cached to let
// go: the common case costs the rows' flags.
- if (!any_placeholder and screen.kitty_images.placements.count() == 0 and state.images.converted.count() == 0) return;
+ if (!any_placeholder and screen.kitty_images.placements.count() == 0 and state.images.converted.count() == 0 and state.images.glyphs.count() == 0) return;
state.images.draw(p, s, &state.vt, @intCast(id), pane.serial, tx, body_y, @min(tw, pane.cols), rows_of[0..vp_rows]);
}
@@ -2982,6 +3013,78 @@ test "a virtual kitty placement shows through its placeholder cells, which draw
try std.testing.expectEqualStrings("X", s.at(tx + 4, y).grapheme());
}
+test "without pixels, or by the Petscii builtin, a terminal's image is glyph art in its cells" {
+ if (comptime !images_enabled) return error.SkipZigTest;
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .cols = 80, .rows = 24, .shells = 1 });
+ defer p.deinit();
+ p.update(.{ .resize = .{ .cols = 80, .rows = 24, .cell_pixels = .{ .w = 10, .h = 20 } } });
+ var frame = std.heap.ArenaAllocator.init(gpa);
+ defer frame.deinit();
+ const pane = p.panes[0].?;
+ pane.body.mode = .tty;
+ p.update(.{ .output = .{ .pane = 0, .bytes = kittyTransmit("c=2,r=1") } });
+ const r = p.rects[0];
+ const tx = r.x + config.GUTTER;
+ const y = p.bodyTop(pane, r);
+ const commodore = pardes.image.GlyphArt.commodore;
+ const painted = struct {
+ fn check(cell: *const pardes.Cell) bool {
+ const bg = switch (cell.style.bg) {
+ .rgb => |rgb| rgb,
+ else => return false,
+ };
+ for (commodore) |c| if (std.mem.eql(u8, &c, &bg)) return true;
+ return false;
+ }
+ }.check;
+
+ // A shell that draws no pixels (a tty under no kitty graphics): forced.
+ p.native_images = false;
+ var s = try p.render(frame.allocator());
+ try std.testing.expectEqual(@as(usize, 0), imagePlaces(s).len);
+ try std.testing.expect(painted(s.at(tx, y)) and painted(s.at(tx + 1, y)));
+ try std.testing.expect(!painted(s.at(tx + 2, y)));
+ // The tag says so, as an image pane's does.
+ try std.testing.expect(std.mem.indexOf(u8, try tagline.tagPrefix(p, pane), " petscii:off") != null);
+
+ // Pixels where they can be drawn; the builtin flips the pane to glyphs
+ // and back.
+ p.native_images = true;
+ _ = frame.reset(.retain_capacity);
+ s = try p.render(frame.allocator());
+ try std.testing.expectEqual(@as(usize, 1), imagePlaces(s).len);
+ try std.testing.expect(p.executeBuiltinLine(0, "Petscii"));
+ _ = frame.reset(.retain_capacity);
+ s = try p.render(frame.allocator());
+ try std.testing.expectEqual(@as(usize, 0), imagePlaces(s).len);
+ try std.testing.expect(painted(s.at(tx, y)));
+ try std.testing.expect(std.mem.indexOf(u8, try tagline.tagPrefix(p, pane), " petscii:on") != null);
+ try std.testing.expect(p.executeBuiltinLine(0, "Petscii"));
+ _ = frame.reset(.retain_capacity);
+ s = try p.render(frame.allocator());
+ try std.testing.expectEqual(@as(usize, 1), imagePlaces(s).len);
+}
+
+test "TermImages is real by default, and petscii makes new terminals draw glyph art" {
+ if (comptime !images_enabled) return error.SkipZigTest;
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .cols = 80, .rows = 24, .shells = 1 });
+ defer p.deinit();
+ try std.testing.expectEqual(config.Runtime.TermImages.real, p.settings.term_images);
+ try std.testing.expectEqual(false, petscii(p.panes[0].?).?);
+ // A pane without images says nothing of them.
+ try std.testing.expect(std.mem.indexOf(u8, try tagline.tagPrefix(p, p.panes[0].?), "petscii") == null);
+ try std.testing.expect(p.executeBuiltinLine(0, "TermImages petscii"));
+ try std.testing.expectEqual(config.Runtime.TermImages.petscii, p.settings.term_images);
+ // The setting is for new panes: the one there keeps its choice.
+ try std.testing.expectEqual(false, petscii(p.panes[0].?).?);
+ const id = p.freeSlot().?;
+ const fresh = try p.newShell(id, "");
+ try std.testing.expectEqual(true, petscii(fresh).?);
+ try std.testing.expect(std.mem.indexOf(u8, try tagline.tagPrefix(p, fresh), " petscii:on") != null);
+}
+
test "a program asking the cell size in pixels (CSI 16 t) is told the shell's" {
if (comptime !images_enabled) return error.SkipZigTest;
const gpa = std.testing.allocator;