libghostty: add GHOSTTY_ENUM_MAX_VALUE sentinel to all C enums

Pre-C23, the C standard allows compilers to choose any integer type
that can represent all enum values, so small enums could be backed
by char or short. This breaks ABI compatibility with the Zig side,
which backs these enums with c_int.

Define GHOSTTY_ENUM_MAX_VALUE as INT_MAX in types.h and add it as
the last entry in every enum in include/ghostty/vt/. This forces
the compiler to use int as the backing type, matching c_int on all
targets. INT_MAX is used rather than a fixed constant because enum
constants must be representable as int; values above INT_MAX are a
constraint violation in standard C.

Document this convention in AGENTS.md.
This commit is contained in:
Mitchell Hashimoto
2026-04-08 10:08:43 -07:00
parent 853183e911
commit 9897d6caba
21 changed files with 73 additions and 2 deletions

View File

@@ -22,6 +22,8 @@ A file for [guiding coding agents](https://agents.md/).
- Build WASM: `zig build -Demit-lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall`
- Test: `zig build test-lib-vt -Dtest-filter=<filter>`
- Prefer this when the change is in a libghostty-vt file
- All C enums in `include/ghostty/vt/` must have a `_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE`
sentinel as the last entry to force int enum sizing (pre-C23 portability).
## Directory Structure

View File

@@ -40,6 +40,7 @@ typedef enum {
GHOSTTY_OPTIMIZE_RELEASE_SAFE = 1,
GHOSTTY_OPTIMIZE_RELEASE_SMALL = 2,
GHOSTTY_OPTIMIZE_RELEASE_FAST = 3,
GHOSTTY_OPTIMIZE_MODE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyOptimizeMode;
/**
@@ -122,6 +123,7 @@ typedef enum {
* Output type: GhosttyString *
*/
GHOSTTY_BUILD_INFO_VERSION_BUILD = 10,
GHOSTTY_BUILD_INFO_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyBuildInfo;
/**

View File

@@ -74,6 +74,7 @@ extern "C" {
typedef enum {
GHOSTTY_COLOR_SCHEME_LIGHT = 0,
GHOSTTY_COLOR_SCHEME_DARK = 1,
GHOSTTY_COLOR_SCHEME_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyColorScheme;
/**

View File

@@ -40,6 +40,7 @@ typedef enum {
GHOSTTY_FOCUS_GAINED = 0,
/** Terminal window lost focus */
GHOSTTY_FOCUS_LOST = 1,
GHOSTTY_FOCUS_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyFocusEvent;
/**

View File

@@ -46,6 +46,7 @@ typedef enum {
/** HTML with inline styles. */
GHOSTTY_FORMATTER_FORMAT_HTML,
GHOSTTY_FORMATTER_FORMAT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyFormatterFormat;
/**

View File

@@ -73,6 +73,7 @@ typedef enum {
GHOSTTY_OPTION_AS_ALT_LEFT = 2,
/** Only right option key is treated as alt */
GHOSTTY_OPTION_AS_ALT_RIGHT = 3,
GHOSTTY_OPTION_AS_ALT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyOptionAsAlt;
/**
@@ -104,6 +105,7 @@ typedef enum {
/** macOS option-as-alt setting (value: GhosttyOptionAsAlt) */
GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT = 6,
GHOSTTY_KEY_ENCODER_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKeyEncoderOption;
/**

View File

@@ -35,6 +35,7 @@ typedef enum {
GHOSTTY_KEY_ACTION_PRESS = 1,
/** Key is being repeated (held down) */
GHOSTTY_KEY_ACTION_REPEAT = 2,
GHOSTTY_KEY_ACTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKeyAction;
/**
@@ -296,6 +297,7 @@ typedef enum {
GHOSTTY_KEY_COPY,
GHOSTTY_KEY_CUT,
GHOSTTY_KEY_PASTE,
GHOSTTY_KEY_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKey;
/**

View File

@@ -119,6 +119,7 @@ typedef enum {
* Output type: GhosttyKittyGraphicsPlacementIterator *
*/
GHOSTTY_KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR = 1,
GHOSTTY_KITTY_GRAPHICS_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsData;
/**
@@ -213,6 +214,7 @@ typedef enum {
* Output type: int32_t *
*/
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_Z = 12,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsPlacementData;
/**
@@ -231,6 +233,7 @@ typedef enum {
GHOSTTY_KITTY_PLACEMENT_LAYER_BELOW_BG = 1,
GHOSTTY_KITTY_PLACEMENT_LAYER_BELOW_TEXT = 2,
GHOSTTY_KITTY_PLACEMENT_LAYER_ABOVE_TEXT = 3,
GHOSTTY_KITTY_PLACEMENT_LAYER_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyPlacementLayer;
/**
@@ -245,6 +248,7 @@ typedef enum {
* Input type: GhosttyKittyPlacementLayer *
*/
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_ITERATOR_OPTION_LAYER = 0,
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_ITERATOR_OPTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsPlacementIteratorOption;
/**
@@ -258,6 +262,7 @@ typedef enum {
GHOSTTY_KITTY_IMAGE_FORMAT_PNG = 2,
GHOSTTY_KITTY_IMAGE_FORMAT_GRAY_ALPHA = 3,
GHOSTTY_KITTY_IMAGE_FORMAT_GRAY = 4,
GHOSTTY_KITTY_IMAGE_FORMAT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyImageFormat;
/**
@@ -268,6 +273,7 @@ typedef enum {
typedef enum {
GHOSTTY_KITTY_IMAGE_COMPRESSION_NONE = 0,
GHOSTTY_KITTY_IMAGE_COMPRESSION_ZLIB_DEFLATE = 1,
GHOSTTY_KITTY_IMAGE_COMPRESSION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyImageCompression;
/**
@@ -335,6 +341,7 @@ typedef enum {
* Output type: size_t *
*/
GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN = 8,
GHOSTTY_KITTY_IMAGE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyKittyGraphicsImageData;
/**

View File

@@ -157,6 +157,7 @@ typedef enum {
GHOSTTY_MODE_REPORT_PERMANENTLY_SET = 3,
/** Mode is permanently reset */
GHOSTTY_MODE_REPORT_PERMANENTLY_RESET = 4,
GHOSTTY_MODE_REPORT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyModeReportState;
/**

View File

@@ -45,6 +45,7 @@ typedef enum {
/** Any-event tracking mode. */
GHOSTTY_MOUSE_TRACKING_ANY = 4,
GHOSTTY_MOUSE_TRACKING_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyMouseTrackingMode;
/**
@@ -58,6 +59,7 @@ typedef enum {
GHOSTTY_MOUSE_FORMAT_SGR = 2,
GHOSTTY_MOUSE_FORMAT_URXVT = 3,
GHOSTTY_MOUSE_FORMAT_SGR_PIXELS = 4,
GHOSTTY_MOUSE_FORMAT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyMouseFormat;
/**
@@ -120,6 +122,7 @@ typedef enum {
/** Whether to enable motion deduplication by last cell (value: bool). */
GHOSTTY_MOUSE_ENCODER_OPT_TRACK_LAST_CELL = 4,
GHOSTTY_MOUSE_ENCODER_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyMouseEncoderOption;
/**

View File

@@ -36,6 +36,7 @@ typedef enum {
/** Mouse moved. */
GHOSTTY_MOUSE_ACTION_MOTION = 2,
GHOSTTY_MOUSE_ACTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyMouseAction;
/**
@@ -56,6 +57,7 @@ typedef enum {
GHOSTTY_MOUSE_BUTTON_NINE = 9,
GHOSTTY_MOUSE_BUTTON_TEN = 10,
GHOSTTY_MOUSE_BUTTON_ELEVEN = 11,
GHOSTTY_MOUSE_BUTTON_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyMouseButton;
/**

View File

@@ -63,6 +63,7 @@ typedef enum {
GHOSTTY_OSC_COMMAND_CONEMU_XTERM_EMULATION = 20,
GHOSTTY_OSC_COMMAND_CONEMU_COMMENT = 21,
GHOSTTY_OSC_COMMAND_KITTY_TEXT_SIZING = 22,
GHOSTTY_OSC_COMMAND_TYPE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyOscCommandType;
/**
@@ -88,6 +89,7 @@ typedef enum {
* the same parser instance. Memory is owned by the parser.
*/
GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR = 1,
GHOSTTY_OSC_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyOscCommandData;
/**

View File

@@ -54,7 +54,8 @@ typedef enum {
/** Scrollback history only (before active area). */
GHOSTTY_POINT_TAG_HISTORY = 3,
} GhosttyPointTag;
GHOSTTY_POINT_TAG_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyPointTag;
/**
* Point value union.

View File

@@ -95,6 +95,7 @@ typedef enum {
/** Global state changed; renderer should redraw everything. */
GHOSTTY_RENDER_STATE_DIRTY_FULL = 2,
GHOSTTY_RENDER_STATE_DIRTY_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateDirty;
/**
@@ -114,6 +115,7 @@ typedef enum {
/** Hollow block cursor. */
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK_HOLLOW = 3,
GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateCursorVisualStyle;
/**
@@ -185,6 +187,7 @@ typedef enum {
/** Whether the cursor is on the tail of a wide character (bool).
* Only valid when CURSOR_VIEWPORT_HAS_VALUE is true. */
GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_WIDE_TAIL = 17,
GHOSTTY_RENDER_STATE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateData;
/**
@@ -195,6 +198,7 @@ typedef enum {
typedef enum {
/** Set dirty state (GhosttyRenderStateDirty). */
GHOSTTY_RENDER_STATE_OPTION_DIRTY = 0,
GHOSTTY_RENDER_STATE_OPTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateOption;
/**
@@ -217,6 +221,7 @@ typedef enum {
* valid as long as the underlying render state is not updated.
* It is unsafe to use cell data after updating the render state. */
GHOSTTY_RENDER_STATE_ROW_DATA_CELLS = 3,
GHOSTTY_RENDER_STATE_ROW_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateRowData;
/**
@@ -227,6 +232,7 @@ typedef enum {
typedef enum {
/** Set dirty state for the current row (bool). */
GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY = 0,
GHOSTTY_RENDER_STATE_ROW_OPTION_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateRowOption;
/**
@@ -509,6 +515,7 @@ typedef enum {
* color, in which case the caller should use whatever default foreground
* color it wants (e.g. the terminal foreground). */
GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_FG_COLOR = 6,
GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRenderStateRowCellsData;
/**

View File

@@ -69,6 +69,7 @@ typedef enum {
/** No text; background color as RGB. */
GHOSTTY_CELL_CONTENT_BG_COLOR_RGB = 3,
GHOSTTY_CELL_CONTENT_TAG_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyCellContentTag;
/**
@@ -90,6 +91,7 @@ typedef enum {
/** Spacer at end of soft-wrapped line for a wide character. */
GHOSTTY_CELL_WIDE_SPACER_HEAD = 3,
GHOSTTY_CELL_WIDE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyCellWide;
/**
@@ -109,6 +111,7 @@ typedef enum {
/** Content that is part of a shell prompt. */
GHOSTTY_CELL_SEMANTIC_PROMPT = 2,
GHOSTTY_CELL_SEMANTIC_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyCellSemanticContent;
/**
@@ -201,6 +204,7 @@ typedef enum {
* Output type: GhosttyColorRgb *
*/
GHOSTTY_CELL_DATA_COLOR_RGB = 11,
GHOSTTY_CELL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyCellData;
/**
@@ -220,6 +224,7 @@ typedef enum {
/** Prompt cells exist and this is a continuation line. */
GHOSTTY_ROW_SEMANTIC_PROMPT_CONTINUATION = 2,
GHOSTTY_ROW_SEMANTIC_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRowSemanticPrompt;
/**
@@ -289,6 +294,7 @@ typedef enum {
* Output type: bool *
*/
GHOSTTY_ROW_DATA_DIRTY = 8,
GHOSTTY_ROW_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyRowData;
/**

View File

@@ -87,6 +87,7 @@ typedef enum {
GHOSTTY_SGR_ATTR_BRIGHT_FG_8 = 28,
GHOSTTY_SGR_ATTR_BG_256 = 29,
GHOSTTY_SGR_ATTR_FG_256 = 30,
GHOSTTY_SGR_ATTR_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttySgrAttributeTag;
/**
@@ -101,6 +102,7 @@ typedef enum {
GHOSTTY_SGR_UNDERLINE_CURLY = 3,
GHOSTTY_SGR_UNDERLINE_DOTTED = 4,
GHOSTTY_SGR_UNDERLINE_DASHED = 5,
GHOSTTY_SGR_UNDERLINE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttySgrUnderline;
/**

View File

@@ -49,6 +49,7 @@ typedef enum {
GHOSTTY_SIZE_REPORT_CSI_16_T = 2,
/** XTWINOPS text area size in characters: ESC [ 8 ; rows ; cols t */
GHOSTTY_SIZE_REPORT_CSI_18_T = 3,
GHOSTTY_SIZE_REPORT_STYLE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttySizeReportStyle;
/**

View File

@@ -50,7 +50,8 @@ typedef enum {
GHOSTTY_STYLE_COLOR_NONE = 0,
GHOSTTY_STYLE_COLOR_PALETTE = 1,
GHOSTTY_STYLE_COLOR_RGB = 2,
} GhosttyStyleColorTag;
GHOSTTY_STYLE_COLOR_TAG_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyStyleColorTag;
/**
* Style color value union.

View File

@@ -106,6 +106,7 @@ typedef enum {
* Input type: GhosttySysDecodePngFn (function pointer, or NULL)
*/
GHOSTTY_SYS_OPT_DECODE_PNG = 1,
GHOSTTY_SYS_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttySysOption;
/**

View File

@@ -189,6 +189,7 @@ typedef enum {
/** Scroll by a delta amount (up is negative). */
GHOSTTY_SCROLL_VIEWPORT_DELTA,
GHOSTTY_SCROLL_VIEWPORT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalScrollViewportTag;
/**
@@ -227,6 +228,7 @@ typedef enum {
/** The alternate screen. */
GHOSTTY_TERMINAL_SCREEN_ALTERNATE = 1,
GHOSTTY_TERMINAL_SCREEN_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalScreen;
/**
@@ -571,6 +573,7 @@ typedef enum {
* Input type: bool*
*/
GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_SHARED_MEM = 18,
GHOSTTY_TERMINAL_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalOption;
/**
@@ -846,6 +849,7 @@ typedef enum {
* Output type: GhosttyKittyGraphics *
*/
GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS = 30,
GHOSTTY_TERMINAL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyTerminalData;
/**

View File

@@ -7,6 +7,7 @@
#ifndef GHOSTTY_VT_TYPES_H
#define GHOSTTY_VT_TYPES_H
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
@@ -32,6 +33,26 @@
#endif
#endif
/**
* Sentinel value for enum definitions to force max int width sizing.
*
* Pre-C23, the C standard allows compilers to choose any integer type
* that can represent all enum values (C11 §6.7.2.2), so small enums
* could be backed by char or short. Adding this value as the last
* entry in every enum forces the compiler to use at an `int`
* type, ensuring ABI stability across compilers and platforms.
*
* We use INT_MAX 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. Using INT_MAX also ensures the enum matches
* the target's int size, which is important because the Zig side
* backs these enums with c_int for ABI compatibility.
*/
#define GHOSTTY_ENUM_MAX_VALUE INT_MAX
/**
* Result codes for libghostty-vt operations.
*/
@@ -46,6 +67,7 @@ typedef enum {
GHOSTTY_OUT_OF_SPACE = -3,
/** The requested value has no value */
GHOSTTY_NO_VALUE = -4,
GHOSTTY_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
} GhosttyResult;
/* ---- Opaque handles ---- */