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.
This commit is contained in:
Mitchell Hashimoto
2026-04-06 09:31:05 -07:00
parent 46a69ea63d
commit 9ff4bb2df5
3 changed files with 51 additions and 62 deletions

View File

@@ -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);

View File

@@ -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 = .{};

View File

@@ -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) {