From 51992ab01ad7e6dfeced16615080b68f620bb122 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 Aug 2026 12:10:44 -0700 Subject: [PATCH 1/4] libghostty: make device and point headers self-contained --- include/ghostty/vt/device.h | 2 ++ include/ghostty/vt/point.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/ghostty/vt/device.h b/include/ghostty/vt/device.h index 0a1567280..419748a86 100644 --- a/include/ghostty/vt/device.h +++ b/include/ghostty/vt/device.h @@ -11,6 +11,8 @@ #include #include +#include + /* DA1 conformance levels (Pp parameter). */ #define GHOSTTY_DA_CONFORMANCE_VT100 1 #define GHOSTTY_DA_CONFORMANCE_VT101 1 diff --git a/include/ghostty/vt/point.h b/include/ghostty/vt/point.h index 8b717f494..9cab2758b 100644 --- a/include/ghostty/vt/point.h +++ b/include/ghostty/vt/point.h @@ -9,6 +9,8 @@ #include +#include + #ifdef __cplusplus extern "C" { #endif From e4ec4f0f95f44b131c256127d26c67258104be5a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 Aug 2026 12:22:21 -0700 Subject: [PATCH 2/4] libghostty: fix enum underlying type detection Use fixed int enum types for C++11, C23, Clang's fixed-enum extension, and GCC 13 or newer. Previously only finalized C23 mode selected an explicit underlying type, leaving C++ and common older C modes with implementation-defined enum types. --- include/ghostty/vt/types.h | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/include/ghostty/vt/types.h b/include/ghostty/vt/types.h index 672faa67c..bd3d37d65 100644 --- a/include/ghostty/vt/types.h +++ b/include/ghostty/vt/types.h @@ -39,18 +39,21 @@ * The Zig side backs all C enums with c_int, so the C declarations * must use int as their underlying type to maintain ABI compatibility. * - * C23 (detected via __STDC_VERSION__ >= 202311L) supports explicit - * enum underlying types with `enum : int { ... }`. For pre-C23 - * compilers, which are free to choose any type that can represent - * all values (C11 §6.7.2.2), we add an INT_MAX sentinel as the last - * entry to force the compiler to use int. + * C++11 and C23 support explicit enum underlying types with + * `enum : int { ... }`. Clang and GCC 13+ also support this syntax as + * an extension in older C language modes, so use it when available. + * + * Other pre-C23 C compilers are free to choose any type that can + * represent all values (C11 §6.7.2.2). For those compilers, we add an + * INT_MAX sentinel as the last entry so the compatible type must be + * able to represent INT_MAX. The exact compatible type and its + * signedness remain implementation-defined in this fallback. * * INT_MAX is used rather than a fixed constant like 0xFFFFFFFF - * because enum constants must have type int (which is signed). - * Values above INT_MAX overflow signed int and are a constraint - * violation in standard C; compilers that accept them interpret them - * as negative values via two's complement, which can collide with - * legitimate negative enum values. + * because enum constants must have type int in pre-C23 C. Values above + * INT_MAX are a constraint violation there; compilers that accept them + * may interpret them as negative values via two's complement, which can + * collide with legitimate negative enum values. * * Usage: * @code @@ -61,7 +64,18 @@ * } Foo; * @endcode */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#if defined(__cplusplus) && \ + (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700)) +#define GHOSTTY_ENUM_TYPED : int +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define GHOSTTY_ENUM_TYPED : int +#elif defined(__clang__) + #if __has_extension(c_fixed_enum) + #define GHOSTTY_ENUM_TYPED : int + #else + #define GHOSTTY_ENUM_TYPED + #endif +#elif defined(__GNUC__) && __GNUC__ >= 13 #define GHOSTTY_ENUM_TYPED : int #else #define GHOSTTY_ENUM_TYPED From 309440e07f3ff097fb4b11ab8cc92b01e29625e8 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 Aug 2026 12:27:20 -0700 Subject: [PATCH 3/4] libghostty: include all public structs in type JSON --- src/terminal/c/types.zig | 75 +++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/src/terminal/c/types.zig b/src/terminal/c/types.zig index df84d3ede..2f8a011cf 100644 --- a/src/terminal/c/types.zig +++ b/src/terminal/c/types.zig @@ -7,6 +7,7 @@ const std = @import("std"); const lib = @import("../lib.zig"); const color = @import("../color.zig"); +const sgr = @import("../sgr.zig"); const color_c = @import("color.zig"); const mouse_event = @import("mouse_event.zig"); const point = @import("../point.zig"); @@ -18,9 +19,11 @@ const selection = @import("selection.zig"); const selection_gesture = @import("selection_gesture.zig"); const render = @import("render.zig"); const style_c = @import("style.zig"); +const kitty_graphics = @import("kitty_graphics.zig"); const mouse_encode = @import("mouse_encode.zig"); const grid_ref = @import("grid_ref.zig"); const io = @import("io.zig"); +const sys = @import("sys.zig"); /// C: GhosttySurfacePosition pub const SurfacePosition = extern struct { @@ -38,6 +41,8 @@ pub const Codepoints = extern struct { pub const structs: std.StaticStringMap(StructInfo) = structs: { @setEvalBranchQuota(10_000); break :structs .initComptime(.{ + .{ "GhosttyAllocator", StructInfo.init(lib.alloc.Allocator) }, + .{ "GhosttyAllocatorVtable", StructInfo.init(lib.alloc.VTable) }, .{ "GhosttyBuffer", StructInfo.init(lib.Buffer) }, .{ "GhosttyClipboardContent", StructInfo.init(terminal.ClipboardContent) }, .{ "GhosttyClipboardWrite", StructInfo.init(terminal.ClipboardWrite) }, @@ -54,22 +59,28 @@ pub const structs: std.StaticStringMap(StructInfo) = structs: { .{ "GhosttyTerminalSelectWordOptions", StructInfo.init(selection.SelectWordOptions) }, .{ "GhosttyTerminalSelectWordBetweenOptions", StructInfo.init(selection.SelectWordBetweenOptions) }, .{ "GhosttyTerminalSelectLineOptions", StructInfo.init(selection.SelectLineOptions) }, + .{ "GhosttyTerminalSelectionFormatOptions", StructInfo.init(selection.FormatOptions) }, .{ "GhosttyFormatterTerminalExtra", StructInfo.init(formatter.TerminalOptions.Extra) }, .{ "GhosttyFormatterScreenExtra", StructInfo.init(formatter.ScreenOptions.Extra) }, .{ "GhosttyGridRef", StructInfo.init(grid_ref.CGridRef) }, + .{ "GhosttyKittyGraphicsPlacementRenderInfo", StructInfo.init(kitty_graphics.PlacementRenderInfo) }, .{ "GhosttyMouseEncoderSize", StructInfo.init(mouse_encode.Size) }, .{ "GhosttyMousePosition", StructInfo.init(mouse_event.Position) }, .{ "GhosttyPoint", StructInfo.init(point.Point.C) }, .{ "GhosttyPointCoordinate", StructInfo.init(point.Coordinate) }, .{ "GhosttyReader", StructInfo.init(io.Reader) }, .{ "GhosttyRenderStateColors", StructInfo.init(render.Colors) }, + .{ "GhosttyRenderStateRowSelection", StructInfo.init(render.RowSelection) }, .{ "GhosttySelectionGestureBehaviors", StructInfo.init(selection_gesture.Behaviors) }, .{ "GhosttySelectionGestureGeometry", StructInfo.init(selection_gesture.Geometry) }, + .{ "GhosttySgrAttribute", StructInfo.init(sgr.Attribute.C) }, + .{ "GhosttySgrUnknown", StructInfo.init(sgr.Attribute.Unknown.C) }, .{ "GhosttySizeReportSize", StructInfo.init(size_report.Size) }, .{ "GhosttyString", StructInfo.init(lib.String) }, .{ "GhosttySurfacePosition", StructInfo.init(SurfacePosition) }, .{ "GhosttyStyle", StructInfo.init(style_c.Style) }, .{ "GhosttyStyleColor", StructInfo.init(style_c.Color) }, + .{ "GhosttySysImage", StructInfo.init(sys.Image) }, .{ "GhosttyTerminalDesktopNotification", StructInfo.init(terminal.DesktopNotification) }, .{ "GhosttyTerminalModeConfig", StructInfo.init(terminal.ModeConfig) }, .{ "GhosttyTerminalProgressReport", StructInfo.init(terminal.ProgressReport) }, @@ -220,15 +231,61 @@ test "json parses" { const root = parsed.value.object; - // Verify we have all expected structs - try std.testing.expect(root.contains("GhosttyClipboardContent")); - try std.testing.expect(root.contains("GhosttyClipboardWrite")); - try std.testing.expect(root.contains("GhosttyFormatterTerminalOptions")); - try std.testing.expect(root.contains("GhosttyTerminalModeConfig")); - try std.testing.expect(root.contains("GhosttyTerminalUnknownSequence")); - try std.testing.expect(root.contains("GhosttyTerminalUnknownStringSequence")); - try std.testing.expect(root.contains("GhosttyReader")); - try std.testing.expect(root.contains("GhosttyWriter")); + // Verify we have every public struct declared by the C API headers. + const expected_structs = [_][]const u8{ + "GhosttyAllocator", + "GhosttyAllocatorVtable", + "GhosttyBuffer", + "GhosttyClipboardContent", + "GhosttyClipboardWrite", + "GhosttyCodepoints", + "GhosttyColorPaletteMask", + "GhosttyColorRgb", + "GhosttyColorX11Entry", + "GhosttyDeviceAttributes", + "GhosttyDeviceAttributesPrimary", + "GhosttyDeviceAttributesSecondary", + "GhosttyDeviceAttributesTertiary", + "GhosttyFormatterScreenExtra", + "GhosttyFormatterTerminalExtra", + "GhosttyFormatterTerminalOptions", + "GhosttyGridRef", + "GhosttyKittyGraphicsPlacementRenderInfo", + "GhosttyMouseEncoderSize", + "GhosttyMousePosition", + "GhosttyPoint", + "GhosttyPointCoordinate", + "GhosttyReader", + "GhosttyRenderStateColors", + "GhosttyRenderStateRowSelection", + "GhosttySelection", + "GhosttySelectionGestureBehaviors", + "GhosttySelectionGestureGeometry", + "GhosttySgrAttribute", + "GhosttySgrUnknown", + "GhosttySizeReportSize", + "GhosttyString", + "GhosttyStyle", + "GhosttyStyleColor", + "GhosttySurfacePosition", + "GhosttySysImage", + "GhosttyTerminalDesktopNotification", + "GhosttyTerminalModeConfig", + "GhosttyTerminalProgressReport", + "GhosttyTerminalScrollbar", + "GhosttyTerminalScrollViewport", + "GhosttyTerminalSelectLineOptions", + "GhosttyTerminalSelectWordBetweenOptions", + "GhosttyTerminalSelectWordOptions", + "GhosttyTerminalSelectionFormatOptions", + "GhosttyTerminalUnknownSequence", + "GhosttyTerminalUnknownStringSequence", + "GhosttyWriter", + }; + try std.testing.expectEqual(expected_structs.len, root.count()); + for (expected_structs) |name| { + try std.testing.expect(root.contains(name)); + } const clipboard_content = root.get("GhosttyClipboardContent").?.object; const clipboard_content_fields = clipboard_content.get("fields").?.object; From d930c74c4d8211d551d0cb99054f13338113f4f9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 13 Aug 2026 12:30:56 -0700 Subject: [PATCH 4/4] libghostty: make sized initialization valid C++ Use an immediately invoked lambda for GHOSTTY_INIT_SIZED in C++ so the macro value-initializes every field before setting the ABI size. The previous C compound literal and designated initializer required compiler extensions in C++17 and C++20. Keep the existing standard compound literal for C callers. --- include/ghostty/vt/types.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/ghostty/vt/types.h b/include/ghostty/vt/types.h index bd3d37d65..c66ee89d2 100644 --- a/include/ghostty/vt/types.h +++ b/include/ghostty/vt/types.h @@ -319,8 +319,17 @@ typedef struct { * opts.trim = true; * @endcode */ +#ifdef __cplusplus +#define GHOSTTY_INIT_SIZED(type) \ + ([]() noexcept { \ + type value{}; \ + value.size = sizeof(value); \ + return value; \ + }()) +#else #define GHOSTTY_INIT_SIZED(type) \ ((type){ .size = sizeof(type) }) +#endif /** * Return a pointer to a null-terminated JSON string describing the