From 9ff4bb2df5d2542f7f4e189aebe309d907e3449e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 6 Apr 2026 09:31:05 -0700 Subject: [PATCH] terminal/kitty: convert Format, Medium, Compression to lib.Enum Convert the Transmission.Format, Transmission.Medium, and Transmission.Compression types from plain Zig enums to lib.Enum so they get a C-compatible backing type when building with c_abi. This lets the C API layer reuse the types directly instead of maintaining separate mirror enums. Move Format.bpp() to a standalone Transmission.formatBpp() function since lib.Enum types cannot have decls. In the C API layer, rename kitty_gfx to kitty_storage and command to kitty_cmd for clarity, and simplify the format/compression getters to direct assignment now that the types are shared. --- src/terminal/c/kitty_graphics.zig | 53 ++++++++++-------------- src/terminal/kitty/graphics_command.zig | 54 ++++++++++++------------- src/terminal/kitty/graphics_image.zig | 6 +-- 3 files changed, 51 insertions(+), 62 deletions(-) diff --git a/src/terminal/c/kitty_graphics.zig b/src/terminal/c/kitty_graphics.zig index bad8b6130..ecfc574c2 100644 --- a/src/terminal/c/kitty_graphics.zig +++ b/src/terminal/c/kitty_graphics.zig @@ -2,13 +2,14 @@ const std = @import("std"); const build_options = @import("terminal_options"); const lib = @import("../lib.zig"); const CAllocator = lib.alloc.Allocator; -const kitty_gfx = @import("../kitty/graphics_storage.zig"); +const kitty_storage = @import("../kitty/graphics_storage.zig"); +const kitty_cmd = @import("../kitty/graphics_command.zig"); const Image = @import("../kitty/graphics_image.zig").Image; const Result = @import("result.zig").Result; /// C: GhosttyKittyGraphics pub const KittyGraphics = if (build_options.kitty_graphics) - *kitty_gfx.ImageStorage + *kitty_storage.ImageStorage else *anyopaque; @@ -22,8 +23,8 @@ else pub const PlacementIterator = ?*PlacementIteratorWrapper; const PlacementMap = std.AutoHashMapUnmanaged( - kitty_gfx.ImageStorage.PlacementKey, - kitty_gfx.ImageStorage.Placement, + kitty_storage.ImageStorage.PlacementKey, + kitty_storage.ImageStorage.Placement, ); const PlacementIteratorWrapper = struct { @@ -117,19 +118,10 @@ fn getTyped( } /// C: GhosttyKittyImageFormat -pub const ImageFormat = enum(c_int) { - rgb = 0, - rgba = 1, - png = 2, - gray_alpha = 3, - gray = 4, -}; +pub const ImageFormat = kitty_cmd.Transmission.Format; /// C: GhosttyKittyImageCompression -pub const ImageCompression = enum(c_int) { - none = 0, - zlib_deflate = 1, -}; +pub const ImageCompression = kitty_cmd.Transmission.Compression; /// C: GhosttyKittyGraphicsImageData pub const ImageData = enum(c_int) { @@ -195,17 +187,8 @@ fn imageGetTyped( .number => out.* = image.number, .width => out.* = image.width, .height => out.* = image.height, - .format => out.* = switch (image.format) { - .rgb => .rgb, - .rgba => .rgba, - .png => .png, - .gray_alpha => .gray_alpha, - .gray => .gray, - }, - .compression => out.* = switch (image.compression) { - .none => .none, - .zlib_deflate => .zlib_deflate, - }, + .format => out.* = image.format, + .compression => out.* = image.compression, .data_ptr => out.* = image.data.ptr, .data_len => out.* = image.data.len, } @@ -306,7 +289,8 @@ test "placement_iterator next on empty storage" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); @@ -334,7 +318,8 @@ test "placement_iterator get before next returns invalid" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); @@ -364,7 +349,8 @@ test "placement_iterator with transmit and display" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); @@ -416,7 +402,8 @@ test "placement_iterator with multiple placements" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); @@ -472,7 +459,8 @@ test "image_get_handle returns null for missing id" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); @@ -492,7 +480,8 @@ test "image_get_handle and image_get with transmitted image" { var t: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( - &lib.alloc.test_allocator, &t, + &lib.alloc.test_allocator, + &t, .{ .cols = 80, .rows = 24, .max_scrollback = 0 }, )); defer terminal_c.free(t); diff --git a/src/terminal/kitty/graphics_command.zig b/src/terminal/kitty/graphics_command.zig index dfce56e35..d1f0e6b63 100644 --- a/src/terminal/kitty/graphics_command.zig +++ b/src/terminal/kitty/graphics_command.zig @@ -3,6 +3,7 @@ const assert = @import("../../quirks.zig").inlineAssert; const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const simd = @import("../../simd/main.zig"); +const lib = @import("../lib.zig"); const log = std.log.scoped(.kitty_gfx); @@ -394,39 +395,38 @@ pub const Transmission = struct { compression: Compression = .none, // o more_chunks: bool = false, // m - pub const Format = enum { - rgb, // 24 - rgba, // 32 - png, // 100 - + pub const Format = lib.Enum(lib.target, &.{ + "rgb", // 24 + "rgba", // 32 + "png", // 100 // The following are not supported directly via the protocol // but they are formats that a png may decode to that we // support. - gray_alpha, - gray, + "gray_alpha", + "gray", + }); - pub fn bpp(self: Format) u8 { - return switch (self) { - .gray => 1, - .gray_alpha => 2, - .rgb => 3, - .rgba => 4, - .png => unreachable, // Must be validated before - }; - } - }; + pub const Medium = lib.Enum(lib.target, &.{ + "direct", // d + "file", // f + "temporary_file", // t + "shared_memory", // s + }); - pub const Medium = enum { - direct, // d - file, // f - temporary_file, // t - shared_memory, // s - }; + pub const Compression = lib.Enum(lib.target, &.{ + "none", + "zlib_deflate", // z + }); - pub const Compression = enum { - none, - zlib_deflate, // z - }; + pub fn formatBpp(format: Format) u8 { + return switch (format) { + .gray => 1, + .gray_alpha => 2, + .rgb => 3, + .rgba => 4, + .png => unreachable, // Must be validated before + }; + } fn parse(kv: KV) !Transmission { var result: Transmission = .{}; diff --git a/src/terminal/kitty/graphics_image.zig b/src/terminal/kitty/graphics_image.zig index f1f055fa0..bddc5c5b2 100644 --- a/src/terminal/kitty/graphics_image.zig +++ b/src/terminal/kitty/graphics_image.zig @@ -202,8 +202,8 @@ pub const LoadingImage = struct { .png => stat_size, // For these formats we have a size we must have. - .gray, .gray_alpha, .rgb, .rgba => |f| size: { - const bpp = f.bpp(); + .gray, .gray_alpha, .rgb, .rgba => size: { + const bpp = command.Transmission.formatBpp(self.image.format); break :size self.image.width * self.image.height * bpp; }, }; @@ -390,7 +390,7 @@ pub const LoadingImage = struct { if (img.width > max_dimension or img.height > max_dimension) return error.DimensionsTooLarge; // Data length must be what we expect - const bpp = img.format.bpp(); + const bpp = command.Transmission.formatBpp(img.format); const expected_len = img.width * img.height * bpp; const actual_len = self.data.items.len; if (actual_len != expected_len) {