From 3a52e0e3bdba98b5372cf0f2d5ca5f150b8c09d7 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 6 Apr 2026 07:13:48 -0700 Subject: [PATCH] libghostty: expose kitty image options via terminal set/get Add four new terminal options for configuring Kitty graphics at runtime through the C API: storage limit, and the three loading medium flags (file, temporary file, shared memory). The storage limit setter propagates to all initialized screens and uses setLimit which handles eviction when lowering the limit. The medium setters similarly propagate to all screens. Getters read from the active screen. All options compile to no-ops or return no_value when kitty graphics are disabled at build time. --- include/ghostty/vt/terminal.h | 83 +++++++++++++++++++++++++++++++++++ src/terminal/c/terminal.zig | 61 +++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/include/ghostty/vt/terminal.h b/include/ghostty/vt/terminal.h index b43e37cf4..c243fa25c 100644 --- a/include/ghostty/vt/terminal.h +++ b/include/ghostty/vt/terminal.h @@ -534,6 +534,49 @@ typedef enum { * Input type: GhosttyColorRgb[256]* */ GHOSTTY_TERMINAL_OPT_COLOR_PALETTE = 14, + + /** + * Set the Kitty image storage limit in bytes. + * + * Applied to all initialized screens (primary and alternate). + * A value of zero disables the Kitty graphics protocol entirely, + * deleting all stored images and placements. A NULL value pointer + * is equivalent to zero (disables). Has no effect when Kitty graphics + * are disabled at build time. + * + * Input type: uint64_t* + */ + GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT = 15, + + /** + * Enable or disable Kitty image loading via the file medium. + * + * A NULL value pointer is a no-op. Has no effect when Kitty graphics + * are disabled at build time. + * + * Input type: bool* + */ + GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_FILE = 16, + + /** + * Enable or disable Kitty image loading via the temporary file medium. + * + * A NULL value pointer is a no-op. Has no effect when Kitty graphics + * are disabled at build time. + * + * Input type: bool* + */ + GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_TEMP_FILE = 17, + + /** + * Enable or disable Kitty image loading via the shared memory medium. + * + * A NULL value pointer is a no-op. Has no effect when Kitty graphics + * are disabled at build time. + * + * Input type: bool* + */ + GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_SHARED_MEM = 18, } GhosttyTerminalOption; /** @@ -756,6 +799,46 @@ typedef enum { * Output type: GhosttyColorRgb[256] * */ GHOSTTY_TERMINAL_DATA_COLOR_PALETTE_DEFAULT = 25, + + /** + * The Kitty image storage limit in bytes for the active screen. + * + * A value of zero means the Kitty graphics protocol is disabled. + * Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time. + * + * Output type: uint64_t * + */ + GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_STORAGE_LIMIT = 26, + + /** + * Whether the file medium is enabled for Kitty image loading on the + * active screen. + * + * Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_FILE = 27, + + /** + * Whether the temporary file medium is enabled for Kitty image loading + * on the active screen. + * + * Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_TEMP_FILE = 28, + + /** + * Whether the shared memory medium is enabled for Kitty image loading + * on the active screen. + * + * Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_SHARED_MEM = 29, } GhosttyTerminalData; /** diff --git a/src/terminal/c/terminal.zig b/src/terminal/c/terminal.zig index ec749ffae..a2b0d1092 100644 --- a/src/terminal/c/terminal.zig +++ b/src/terminal/c/terminal.zig @@ -1,5 +1,6 @@ const std = @import("std"); const testing = std.testing; +const build_options = @import("terminal_options"); const lib = @import("../lib.zig"); const CAllocator = lib.alloc.Allocator; const ZigTerminal = @import("../Terminal.zig"); @@ -304,6 +305,10 @@ pub const Option = enum(c_int) { color_background = 12, color_cursor = 13, color_palette = 14, + kitty_image_storage_limit = 15, + kitty_image_medium_file = 16, + kitty_image_medium_temp_file = 17, + kitty_image_medium_shared_mem = 18, /// Input type expected for setting the option. pub fn InType(comptime self: Option) type { @@ -320,6 +325,11 @@ pub const Option = enum(c_int) { .title, .pwd => ?*const lib.String, .color_foreground, .color_background, .color_cursor => ?*const color.RGB.C, .color_palette => ?*const color.PaletteC, + .kitty_image_storage_limit => ?*const u64, + .kitty_image_medium_file, + .kitty_image_medium_temp_file, + .kitty_image_medium_shared_mem, + => ?*const bool, }; } }; @@ -388,6 +398,32 @@ fn setTyped( ); wrapper.terminal.flags.dirty.palette = true; }, + .kitty_image_storage_limit => { + if (comptime !build_options.kitty_graphics) return .success; + const limit: usize = if (value) |v| @intCast(v.*) else 0; + var it = wrapper.terminal.screens.all.iterator(); + while (it.next()) |entry| { + const screen = entry.value.*; + screen.kitty_images.setLimit(screen.alloc, screen, limit) catch return .out_of_memory; + } + }, + .kitty_image_medium_file, + .kitty_image_medium_temp_file, + .kitty_image_medium_shared_mem, + => { + if (comptime !build_options.kitty_graphics) return .success; + const val = (value orelse return .success).*; + var it = wrapper.terminal.screens.all.iterator(); + while (it.next()) |entry| { + const screen = entry.value.*; + switch (option) { + .kitty_image_medium_file => screen.kitty_images.image_limits.file = val, + .kitty_image_medium_temp_file => screen.kitty_images.image_limits.temporary_file = val, + .kitty_image_medium_shared_mem => screen.kitty_images.image_limits.shared_memory = val, + else => unreachable, + } + } + }, } return .success; } @@ -513,6 +549,10 @@ pub const TerminalData = enum(c_int) { color_background_default = 23, color_cursor_default = 24, color_palette_default = 25, + kitty_image_storage_limit = 26, + kitty_image_medium_file = 27, + kitty_image_medium_temp_file = 28, + kitty_image_medium_shared_mem = 29, /// Output type expected for querying the data of the given kind. pub fn OutType(comptime self: TerminalData) type { @@ -535,6 +575,11 @@ pub const TerminalData = enum(c_int) { .color_cursor_default, => color.RGB.C, .color_palette, .color_palette_default => color.PaletteC, + .kitty_image_storage_limit => u64, + .kitty_image_medium_file, + .kitty_image_medium_temp_file, + .kitty_image_medium_shared_mem, + => bool, }; } }; @@ -603,6 +648,22 @@ fn getTyped( .color_cursor_default => out.* = (t.colors.cursor.default orelse return .no_value).cval(), .color_palette => out.* = color.paletteCval(&t.colors.palette.current), .color_palette_default => out.* = color.paletteCval(&t.colors.palette.original), + .kitty_image_storage_limit => { + if (comptime !build_options.kitty_graphics) return .no_value; + out.* = @intCast(t.screens.active.kitty_images.total_limit); + }, + .kitty_image_medium_file => { + if (comptime !build_options.kitty_graphics) return .no_value; + out.* = t.screens.active.kitty_images.image_limits.file; + }, + .kitty_image_medium_temp_file => { + if (comptime !build_options.kitty_graphics) return .no_value; + out.* = t.screens.active.kitty_images.image_limits.temporary_file; + }, + .kitty_image_medium_shared_mem => { + if (comptime !build_options.kitty_graphics) return .no_value; + out.* = t.screens.active.kitty_images.image_limits.shared_memory; + }, } return .success;