From 23a00ede5552de365f49c8ec73d6913985ecd8be Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Tue, 30 Jan 2024 20:51:54 -0600 Subject: [PATCH] images: kitty support works well We still need to handle querying for support. Signed-off-by: Tim Culverhouse --- README.md | 4 +-- examples/image.zig | 55 ++++++++------------------------ vaxis.png => examples/vaxis.png | Bin examples/zig.png | Bin 0 -> 1653 bytes src/Image.zig | 22 +++++++++---- src/main.zig | 3 +- src/vaxis.zig | 17 ++++++++-- src/widgets/align.zig | 7 ++++ src/widgets/main.zig | 1 + 9 files changed, 56 insertions(+), 53 deletions(-) rename vaxis.png => examples/vaxis.png (100%) create mode 100644 examples/zig.png create mode 100644 src/widgets/align.zig diff --git a/README.md b/README.md index 4ce26c7..e8e722c 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ Contributions are welcome. | Images (half block) | ✅ | planned | ✅ | | Images (quadrant) | ✅ | planned | ✅ | | Images (sextant) | ❌ | ❌ | ✅ | -| Images (sixel) | ✅ | planned | ✅ | -| Images (kitty) | ✅ | planned | ✅ | +| Images (sixel) | ✅ | debating | ✅ | +| Images (kitty) | ✅ | ✅ | ✅ | | Images (iterm2) | ❌ | ❌ | ✅ | | Video | ❌ | ❌ | ✅ | | Dank | 🆗 | 🆗 | ✅ | diff --git a/examples/image.zig b/examples/image.zig index 0d32e8e..aba084d 100644 --- a/examples/image.zig +++ b/examples/image.zig @@ -3,89 +3,62 @@ const vaxis = @import("vaxis"); const log = std.log.scoped(.main); -// Our EventType. This can contain internal events as well as Vaxis events. -// Internal events can be posted into the same queue as vaxis events to allow -// for a single event loop with exhaustive switching. Booya const Event = union(enum) { key_press: vaxis.Key, winsize: vaxis.Winsize, - focus_in, - focus_out, - foo: u8, }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer { const deinit_status = gpa.deinit(); - //fail test; can't try in defer as defer is executed after we return if (deinit_status == .leak) { log.err("memory leak", .{}); } } const alloc = gpa.allocator(); - // Initialize Vaxis with our event type var vx = try vaxis.init(Event, .{}); - // deinit takes an optional allocator. If your program is exiting, you can - // choose to pass a null allocator to save some exit time. defer vx.deinit(alloc); - // Start the read loop. This puts the terminal in raw mode and begins - // reading user input try vx.startReadThread(); defer vx.stopReadThread(); - // Optionally enter the alternate screen try vx.enterAltScreen(); - // Sends queries to terminal to detect certain features. This should - // _always_ be called, but is left to the application to decide when try vx.queryTerminal(); - const img = try vx.loadImage(alloc, .{ .path = "vaxis.png" }); + const imgs = [_]vaxis.Image{ + try vx.loadImage(alloc, .{ .path = "examples/zig.png" }), + try vx.loadImage(alloc, .{ .path = "examples/vaxis.png" }), + }; var n: usize = 0; - // The main event loop. Vaxis provides a thread safe, blocking, buffered - // queue which can serve as the primary event queue for an application - outer: while (true) { - // nextEvent blocks until an event is in the queue + while (true) { const event = vx.nextEvent(); - log.debug("event: {}\r\n", .{event}); - // exhaustive switching ftw. Vaxis will send events if your EventType - // enum has the fields for those events (ie "key_press", "winsize") switch (event) { .key_press => |key| { - n += 1; if (key.matches('c', .{ .ctrl = true })) { - break :outer; + return; } else if (key.matches('l', .{ .ctrl = true })) { vx.queueRefresh(); - } else if (key.matches('n', .{ .ctrl = true })) { - try vx.notify("vaxis", "hello from vaxis"); - } else {} + } }, - .winsize => |ws| try vx.resize(alloc, ws), - else => {}, } - // vx.window() returns the root window. This window is the size of the - // terminal and can spawn child windows as logical areas. Child windows - // cannot draw outside of their bounds + n = (n + 1) % imgs.len; const win = vx.window(); - - // Clear the entire space because we are drawing in immediate mode. - // vaxis double buffers the screen. This new frame will be compared to - // the old and only updated cells will be drawn win.clear(); - const child = win.initChild(n, n, .expand, .expand); + const img = imgs[n]; + const dims = try img.cellSize(win); + const center = vaxis.alignment.center(win, dims.cols, dims.rows); + const scale = false; + const z_index = 0; + img.draw(center, scale, z_index); - img.draw(child, false, 0); - - // Render the screen try vx.render(); } } diff --git a/vaxis.png b/examples/vaxis.png similarity index 100% rename from vaxis.png rename to examples/vaxis.png diff --git a/examples/zig.png b/examples/zig.png new file mode 100644 index 0000000000000000000000000000000000000000..934c23fdfb8b241e12316d20827dcd3581d1a3a0 GIT binary patch literal 1653 zcmV-*28#KKP)RJKyfIsI!j@%!rh z`9AzUKYq{ioacGc81RPcZ35AdB9preEfK7WHb+1b!Azjc;je!1{``(dXOm4rn875U zGouYgCn3yWsdD+7_vZ3T2>_M>04DZbvPmqs+XY_d1cWdnrY@hqL$-o{jJzdYJl7dV zRn1>Gewgw>(L?ktNowqDRW@g9-U@yx1VF0iyFRP4e$oDcQqO0vMFkRhOpyIImbCO= z{2fb$tn~l_d~eU!UCu^@20P&WJgNx@Va6;~4ljRiuHXOxf#oy#EH>9iJ6Z;mw&9oG zK@RN#BGAADAj~U|52P7;#-5T8hRURyB7_-{Y0Zqe3{tgqjRkj4$i$q#psIv>004OE zeNYvs(b-E$c+s3$z)+dkEJPR)fhPcAIikZilJJrgMjTp#eC7!cg87hHFxSQN-`d;A z>yrvWxe#G&4JWjxYIJs0Tjv-?&PdwQq)$N%l;-UTr~+op^_^60-7|m#pj9FTatZgH zuo8R5vbmaT+UBmCR=5`ttc*fdRYw-u9-!br#$`R-Vt#kxRH2gPMj3`XSfZ z<-Cwl(O(?LjR7~0CRQc0WB1SG^)CT}=-SlOpVQgx#AWH+l(MyPC;)&J82Jl9)H(NJ zuGZ~MgF9`*lkSpiUSU=myPV4JPZt_2)LN=NG1uLd zYHN`NcLUkqS#!(faqrRnS;}dYqv#FGm)cN^R7!;0%9dyLLkt+$s>!ILuejCY#==%#BI(fvU2I&PL+%x$5I z)7j;$XuM^=5=@MURbZo(g`D~if&vJzseH!n5q0*vcy+LGB2oczd2u2SX@ejjr1F_D z*Hy*2A$2IRB2odv@p%m*I7Uk4fw->wQkC_87JM?`wV0>EUIDEVQHtQ0-IOMR7$`h3 zw84VA9ihexYX!8$3A^NC&KVlp#KG1fh3>wiFB~z$mOzS*Y>6 z6>>=UFyOUes1CfIVc%cScOTuqV=Owi$ z<8RQx1MdX9cYWXd?5I^}uusVA+f_(O=0a|5B-jA~CTiLS6?&87VP;xv%IU3w&mjm( z^{45y1U%u*n5(_uEdFdmtyLo1G8b|+bQFMqf;ukN8ArBNUwhgi6VINDs^B{SfE0h* z`W(b&%w?3StqR*PLDUuT{vnreX_pDl$b*AFrf9^q3H%r~dRTBh5O^>>fj`Fb!~di9 ze_L?(Xy%gGc_mr#Z9yNQ(5M%4UpkE1tHARrSAbDwx(^;od32#?|GvS32HV0My>Ya|Has~IOcLRc&uR3`mbjz7;0XZ6`-aA@9BLV^)SDc?yb?khy;K3T zI5AI_!EW;!A0x&G;~#1|hfeG6j6ZtCjFg&I0hySyT$aK9^Bji&!^WPmGl$Tj22;;C z7A;7>l(~d6$Dv*DE4j3AXcssQl}Q)m;#g6%x%}3xvc&VFTG10jWm27ZF>8Lbc>>a{ z>i9JmcE#^rB4pC8J>DwJdLw`A;i=yP00960@AyAs^7Mt200000NkvXXu0mjfL=P+^ literal 0 HcmV?d00001 diff --git a/src/Image.zig b/src/Image.zig index 04d6204..ad45172 100644 --- a/src/Image.zig +++ b/src/Image.zig @@ -6,7 +6,6 @@ const base64 = std.base64.standard.Encoder; const zigimg = @import("zigimg"); const Window = @import("Window.zig"); -const Winsize = @import("Tty.zig").Winsize; const log = std.log.scoped(.image); @@ -22,7 +21,7 @@ pub const Source = union(enum) { pub const Placement = struct { img_id: u32, z_index: i32, - scale: bool, + size: ?CellSize = null, }; pub const CellSize = struct { @@ -42,15 +41,26 @@ pub fn draw(self: Image, win: Window, scale: bool, z_index: i32) void { const p = Placement{ .img_id = self.id, .z_index = z_index, - .scale = scale, + .size = sz: { + if (!scale) break :sz null; + break :sz CellSize{ + .rows = win.height, + .cols = win.width, + }; + }, }; win.writeCell(0, 0, .{ .image = p }); } -pub fn cellSize(self: Image, winsize: Winsize) !CellSize { +pub fn cellSize(self: Image, win: Window) !CellSize { // cell geometry - const pix_per_col = try std.math.divCeil(usize, winsize.x_pixel, winsize.cols); - const pix_per_row = try std.math.divCeil(usize, winsize.y_pixel, winsize.rows); + const x_pix = win.screen.width_pix; + const y_pix = win.screen.height_pix; + const w = win.screen.width; + const h = win.screen.height; + + const pix_per_col = try std.math.divCeil(usize, x_pix, w); + const pix_per_row = try std.math.divCeil(usize, y_pix, h); const cell_width = std.math.divCeil(usize, self.width, pix_per_col) catch 0; const cell_height = std.math.divCeil(usize, self.height, pix_per_row) catch 0; diff --git a/src/main.zig b/src/main.zig index e0c30df..63e2c00 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,6 +9,8 @@ pub const Key = @import("Key.zig"); pub const Winsize = @import("Tty.zig").Winsize; pub const widgets = @import("widgets/main.zig"); +pub const alignment = widgets.alignment; +pub const border = widgets.border; pub const Image = @import("Image.zig"); @@ -30,7 +32,6 @@ test { _ = @import("ctlseqs.zig"); _ = @import("event.zig"); _ = @import("gwidth.zig"); - _ = @import("image/image.zig"); _ = @import("queue.zig"); _ = @import("vaxis.zig"); } diff --git a/src/vaxis.zig b/src/vaxis.zig index 39f12d2..0ca3001 100644 --- a/src/vaxis.zig +++ b/src/vaxis.zig @@ -38,6 +38,7 @@ pub fn Vaxis(comptime T: type) type { pub const Capabilities = struct { kitty_keyboard: bool = false, + kitty_graphics: bool = false, rgb: bool = false, unicode: bool = false, }; @@ -60,8 +61,6 @@ pub fn Vaxis(comptime T: type) type { alt_screen: bool = false, /// if we have entered kitty keyboard kitty_keyboard: bool = false, - // TODO: should be false but we aren't querying yet - kitty_graphics: bool = true, bracketed_paste: bool = false, mouse: bool = false, } = .{}, @@ -337,7 +336,19 @@ pub fn Vaxis(comptime T: type) type { } if (cell.image) |img| { - try std.fmt.format(tty.buffered_writer.writer(), ctlseqs.kitty_graphics_place, .{ img.img_id, img.z_index }); + if (img.size) |size| { + try std.fmt.format( + tty.buffered_writer.writer(), + ctlseqs.kitty_graphics_scale, + .{ img.img_id, img.z_index, size.cols, size.rows }, + ); + } else { + try std.fmt.format( + tty.buffered_writer.writer(), + ctlseqs.kitty_graphics_place, + .{ img.img_id, img.z_index }, + ); + } } // something is different, so let's loop throuugh everything and diff --git a/src/widgets/align.zig b/src/widgets/align.zig new file mode 100644 index 0000000..f714479 --- /dev/null +++ b/src/widgets/align.zig @@ -0,0 +1,7 @@ +const Window = @import("../Window.zig"); + +pub fn center(parent: Window, cols: usize, rows: usize) Window { + const y_off = (parent.height / 2) - (rows / 2); + const x_off = (parent.width / 2) - (cols / 2); + return parent.initChild(x_off, y_off, .{ .limit = cols }, .{ .limit = rows }); +} diff --git a/src/widgets/main.zig b/src/widgets/main.zig index 1eabb24..714b3bd 100644 --- a/src/widgets/main.zig +++ b/src/widgets/main.zig @@ -1,2 +1,3 @@ pub const TextInput = @import("TextInput.zig"); pub const border = @import("border.zig"); +pub const alignment = @import("align.zig");