From b3a3ca11823200b974d7f3aa0c564bab50026194 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 20 Aug 2023 16:56:44 -0700 Subject: [PATCH] terminal/kitty-gfx: placement now works properly --- src/terminal/kitty/graphics_exec.zig | 43 +++++++++++++++++++++++-- src/terminal/kitty/graphics_storage.zig | 25 ++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/terminal/kitty/graphics_exec.zig b/src/terminal/kitty/graphics_exec.zig index c0ac58f2c..e7fcac5f5 100644 --- a/src/terminal/kitty/graphics_exec.zig +++ b/src/terminal/kitty/graphics_exec.zig @@ -2,6 +2,7 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; +const point = @import("../point.zig"); const Terminal = @import("../Terminal.zig"); const command = @import("graphics_command.zig"); const image = @import("graphics_image.zig"); @@ -9,6 +10,11 @@ const Command = command.Command; const Response = command.Response; const Image = image.Image; +// TODO: +// - image ids need to be assigned, can't just be zero +// - delete +// (not exhaustive, almost every op is ignoring additional config) + /// Execute a Kitty graphics command against the given terminal. This /// will never fail, but the response may indicate an error and the /// terminal state may not be updated to reflect the command. This will @@ -112,16 +118,47 @@ fn display( cmd: *Command, ) Response { const d = cmd.display().?; + + // Display requires image ID or number. + if (d.image_id == 0 and d.image_number == 0) { + return .{ .message = "EINVAL: image ID or number required" }; + } + + // Build up our response var result: Response = .{ .id = d.image_id, .image_number = d.image_number, .placement_id = d.placement_id, }; - // TODO + // Verify the requested image exists if we have an ID + const storage = &terminal.screen.kitty_images; + const img_: ?Image = if (d.image_id != 0) + storage.imageById(d.image_id) + else + storage.imageByNumber(d.image_number); + const img = img_ orelse { + result.message = "EINVAL: image not found"; + return result; + }; + + // Make sure our response has the image id in case we looked up by number + result.id = img.id; + + // Determine the screen point for the placement. + const placement_point = (point.Viewport{ + .x = terminal.screen.cursor.x, + .y = terminal.screen.cursor.y, + }).toScreen(&terminal.screen); + + // Add the placement + storage.addPlacement(alloc, img.id, d.placement_id, .{ + .point = placement_point, + }) catch |err| { + encodeError(&result, err); + return result; + }; - _ = alloc; - _ = terminal; return result; } diff --git a/src/terminal/kitty/graphics_storage.zig b/src/terminal/kitty/graphics_storage.zig index 7355bbacf..6ea9fed19 100644 --- a/src/terminal/kitty/graphics_storage.zig +++ b/src/terminal/kitty/graphics_storage.zig @@ -33,7 +33,22 @@ pub const ImageStorage = struct { /// Add an already-loaded image to the storage. This will automatically /// free any existing image with the same ID. pub fn addImage(self: *ImageStorage, alloc: Allocator, img: Image) Allocator.Error!void { + // Do the gop op first so if it fails we don't get a partial state const gop = try self.images.getOrPut(alloc, img.id); + + // If the image has an image number, we need to invalidate the last + // image with that same number. + if (img.number > 0) { + var it = self.images.iterator(); + while (it.next()) |kv| { + if (kv.value_ptr.number == img.number) { + kv.value_ptr.number = 0; + break; + } + } + } + + // Write our new image if (gop.found_existing) gop.value_ptr.deinit(alloc); gop.value_ptr.* = img; } @@ -59,6 +74,16 @@ pub const ImageStorage = struct { return self.images.get(image_id); } + /// Get an image by its number. If the image doesn't exist, return null. + pub fn imageByNumber(self: *ImageStorage, image_number: u32) ?Image { + var it = self.images.iterator(); + while (it.next()) |kv| { + if (kv.value_ptr.number == image_number) return kv.value_ptr.*; + } + + return null; + } + /// Every placement is uniquely identified by the image ID and the /// placement ID. If an image ID isn't specified it is assumed to be 0. /// Likewise, if a placement ID isn't specified it is assumed to be 0.