diff options
Diffstat (limited to 'src/terminal.zig')
| -rw-r--r-- | src/terminal.zig | 105 |
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; |
