From 725203d494c70d531eb75053a29072e2a3e119fe Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 6 Oct 2025 20:35:45 -0700 Subject: [PATCH] lib-vt: split header to be more consumable --- AGENTS.md | 1 + Doxyfile | 5 +- include/ghostty/vt.h | 1147 +----------------------------- include/ghostty/vt/allocator.h | 196 +++++ include/ghostty/vt/key.h | 80 +++ include/ghostty/vt/key/encoder.h | 221 ++++++ include/ghostty/vt/key/event.h | 474 ++++++++++++ include/ghostty/vt/osc.h | 231 ++++++ include/ghostty/vt/result.h | 20 + src/build/GhosttyLibVt.zig | 7 +- 10 files changed, 1234 insertions(+), 1148 deletions(-) create mode 100644 include/ghostty/vt/allocator.h create mode 100644 include/ghostty/vt/key.h create mode 100644 include/ghostty/vt/key/encoder.h create mode 100644 include/ghostty/vt/key/event.h create mode 100644 include/ghostty/vt/osc.h create mode 100644 include/ghostty/vt/result.h diff --git a/AGENTS.md b/AGENTS.md index 14fff7b3d..afa0fd1f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,7 @@ A file for [guiding coding agents](https://agents.md/). - Test: `zig build test-lib-vt` - Test filter: `zig build test-lib-vt -Dtest-filter=` - When working on libghostty-vt, do not build the full app. +- For C only changes, don't run the Zig tests. Build all the examples. ## macOS App diff --git a/Doxyfile b/Doxyfile index 58b6be48a..63e73334d 100644 --- a/Doxyfile +++ b/Doxyfile @@ -3,9 +3,10 @@ DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "libghostty" PROJECT_LOGO = images/gnome/64.png -INPUT = include/ghostty/vt.h +INPUT = include/ghostty INPUT_ENCODING = UTF-8 -RECURSIVE = NO +RECURSIVE = YES +FILE_PATTERNS = *.h EXAMPLE_PATH = example EXAMPLE_RECURSIVE = YES EXAMPLE_PATTERNS = * diff --git a/include/ghostty/vt.h b/include/ghostty/vt.h index bcbb01d00..489996530 100644 --- a/include/ghostty/vt.h +++ b/include/ghostty/vt.h @@ -57,1149 +57,10 @@ extern "C" { #endif -#include -#include -#include - -//------------------------------------------------------------------- -// Types - -/** - * Opaque handle to an OSC parser instance. - * - * This handle represents an OSC (Operating System Command) parser that can - * be used to parse the contents of OSC sequences. This isn't a full VT - * parser; it is only the OSC parser component. This is useful if you have - * a parser already and want to only extract and handle OSC sequences. - * - * @ingroup osc - */ -typedef struct GhosttyOscParser *GhosttyOscParser; - -/** - * Opaque handle to a single OSC command. - * - * This handle represents a parsed OSC (Operating System Command) command. - * The command can be queried for its type and associated data using - * `ghostty_osc_command_type` and `ghostty_osc_command_data`. - * - * @ingroup osc - */ -typedef struct GhosttyOscCommand *GhosttyOscCommand; - -/** - * Result codes for libghostty-vt operations. - */ -typedef enum { - /** Operation completed successfully */ - GHOSTTY_SUCCESS = 0, - /** Operation failed due to failed allocation */ - GHOSTTY_OUT_OF_MEMORY = -1, -} GhosttyResult; - -/** - * OSC command types. - * - * @ingroup osc - */ -typedef enum { - GHOSTTY_OSC_COMMAND_INVALID = 0, - GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_TITLE = 1, - GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_ICON = 2, - GHOSTTY_OSC_COMMAND_PROMPT_START = 3, - GHOSTTY_OSC_COMMAND_PROMPT_END = 4, - GHOSTTY_OSC_COMMAND_END_OF_INPUT = 5, - GHOSTTY_OSC_COMMAND_END_OF_COMMAND = 6, - GHOSTTY_OSC_COMMAND_CLIPBOARD_CONTENTS = 7, - GHOSTTY_OSC_COMMAND_REPORT_PWD = 8, - GHOSTTY_OSC_COMMAND_MOUSE_SHAPE = 9, - GHOSTTY_OSC_COMMAND_COLOR_OPERATION = 10, - GHOSTTY_OSC_COMMAND_KITTY_COLOR_PROTOCOL = 11, - GHOSTTY_OSC_COMMAND_SHOW_DESKTOP_NOTIFICATION = 12, - GHOSTTY_OSC_COMMAND_HYPERLINK_START = 13, - GHOSTTY_OSC_COMMAND_HYPERLINK_END = 14, - GHOSTTY_OSC_COMMAND_CONEMU_SLEEP = 15, - GHOSTTY_OSC_COMMAND_CONEMU_SHOW_MESSAGE_BOX = 16, - GHOSTTY_OSC_COMMAND_CONEMU_CHANGE_TAB_TITLE = 17, - GHOSTTY_OSC_COMMAND_CONEMU_PROGRESS_REPORT = 18, - GHOSTTY_OSC_COMMAND_CONEMU_WAIT_INPUT = 19, - GHOSTTY_OSC_COMMAND_CONEMU_GUIMACRO = 20, -} GhosttyOscCommandType; - -/** - * OSC command data types. - * - * These values specify what type of data to extract from an OSC command - * using `ghostty_osc_command_data`. - * - * @ingroup osc - */ -typedef enum { - /** Invalid data type. Never results in any data extraction. */ - GHOSTTY_OSC_DATA_INVALID = 0, - - /** - * Window title string data. - * - * Valid for: GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_TITLE - * - * Output type: const char ** (pointer to null-terminated string) - * - * Lifetime: Valid until the next call to any ghostty_osc_* function with - * the same parser instance. Memory is owned by the parser. - */ - GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR = 1, -} GhosttyOscCommandData; - -//------------------------------------------------------------------- -// Allocator Interface - -/** @defgroup allocator Memory Management - * - * libghostty-vt does require memory allocation for various operations, - * but is resilient to allocation failures and will gracefully handle - * out-of-memory situations by returning error codes. - * - * The exact memory management semantics are documented in the relevant - * functions and data structures. - * - * libghostty-vt uses explicit memory allocation via an allocator - * interface provided by GhosttyAllocator. The interface is based on the - * [Zig](https://ziglang.org) allocator interface, since this has been - * shown to be a flexible and powerful interface in practice and enables - * a wide variety of allocation strategies. - * - * **For the common case, you can pass NULL as the allocator for any - * function that accepts one,** and libghostty will use a default allocator. - * The default allocator will be libc malloc/free if libc is linked. - * Otherwise, a custom allocator is used (currently Zig's SMP allocator) - * that doesn't require any external dependencies. - * - * ## Basic Usage - * - * For simple use cases, you can ignore this interface entirely by passing NULL - * as the allocator parameter to functions that accept one. This will use the - * default allocator (typically libc malloc/free, if libc is linked, but - * we provide our own default allocator if libc isn't linked). - * - * To use a custom allocator: - * 1. Implement the GhosttyAllocatorVtable function pointers - * 2. Create a GhosttyAllocator struct with your vtable and context - * 3. Pass the allocator to functions that accept one - * - * @{ - */ - -/** - * Function table for custom memory allocator operations. - * - * This vtable defines the interface for a custom memory allocator. All - * function pointers must be valid and non-NULL. - * - * @ingroup allocator - * - * If you're not going to use a custom allocator, you can ignore all of - * this. All functions that take an allocator pointer allow NULL to use a - * default allocator. - * - * The interface is based on the Zig allocator interface. I'll say up front - * that it is easy to look at this interface and think "wow, this is really - * overcomplicated". The reason for this complexity is well thought out by - * the Zig folks, and it enables a diverse set of allocation strategies - * as shown by the Zig ecosystem. As a consolation, please note that many - * of the arguments are only needed for advanced use cases and can be - * safely ignored in simple implementations. For example, if you look at - * the Zig implementation of the libc allocator in `lib/std/heap.zig` - * (search for CAllocator), you'll see it is very simple. - * - * We chose to align with the Zig allocator interface because: - * - * 1. It is a proven interface that serves a wide variety of use cases - * in the real world via the Zig ecosystem. It's shown to work. - * - * 2. Our core implementation itself is Zig, and this lets us very - * cheaply and easily convert between C and Zig allocators. - * - * NOTE(mitchellh): In the future, we can have default implementations of - * resize/remap and allow those to be null. - */ -typedef struct { - /** - * Return a pointer to `len` bytes with specified `alignment`, or return - * `NULL` indicating the allocation failed. - * - * @param ctx The allocator context - * @param len Number of bytes to allocate - * @param alignment Required alignment for the allocation. Guaranteed to - * be a power of two between 1 and 16 inclusive. - * @param ret_addr First return address of the allocation call stack (0 if not provided) - * @return Pointer to allocated memory, or NULL if allocation failed - */ - void* (*alloc)(void *ctx, size_t len, uint8_t alignment, uintptr_t ret_addr); - - /** - * Attempt to expand or shrink memory in place. - * - * `memory_len` must equal the length requested from the most recent - * successful call to `alloc`, `resize`, or `remap`. `alignment` must - * equal the same value that was passed as the `alignment` parameter to - * the original `alloc` call. - * - * `new_len` must be greater than zero. - * - * @param ctx The allocator context - * @param memory Pointer to the memory block to resize - * @param memory_len Current size of the memory block - * @param alignment Alignment (must match original allocation) - * @param new_len New requested size - * @param ret_addr First return address of the allocation call stack (0 if not provided) - * @return true if resize was successful in-place, false if relocation would be required - */ - bool (*resize)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr); - - /** - * Attempt to expand or shrink memory, allowing relocation. - * - * `memory_len` must equal the length requested from the most recent - * successful call to `alloc`, `resize`, or `remap`. `alignment` must - * equal the same value that was passed as the `alignment` parameter to - * the original `alloc` call. - * - * A non-`NULL` return value indicates the resize was successful. The - * allocation may have same address, or may have been relocated. In either - * case, the allocation now has size of `new_len`. A `NULL` return value - * indicates that the resize would be equivalent to allocating new memory, - * copying the bytes from the old memory, and then freeing the old memory. - * In such case, it is more efficient for the caller to perform the copy. - * - * `new_len` must be greater than zero. - * - * @param ctx The allocator context - * @param memory Pointer to the memory block to remap - * @param memory_len Current size of the memory block - * @param alignment Alignment (must match original allocation) - * @param new_len New requested size - * @param ret_addr First return address of the allocation call stack (0 if not provided) - * @return Pointer to resized memory (may be relocated), or NULL if manual copy is needed - */ - void* (*remap)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr); - - /** - * Free and invalidate a region of memory. - * - * `memory_len` must equal the length requested from the most recent - * successful call to `alloc`, `resize`, or `remap`. `alignment` must - * equal the same value that was passed as the `alignment` parameter to - * the original `alloc` call. - * - * @param ctx The allocator context - * @param memory Pointer to the memory block to free - * @param memory_len Size of the memory block - * @param alignment Alignment (must match original allocation) - * @param ret_addr First return address of the allocation call stack (0 if not provided) - */ - void (*free)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, uintptr_t ret_addr); -} GhosttyAllocatorVtable; - -/** - * Custom memory allocator. - * - * For functions that take an allocator pointer, a NULL pointer indicates - * that the default allocator should be used. The default allocator will - * be libc malloc/free if we're linking to libc. If libc isn't linked, - * a custom allocator is used (currently Zig's SMP allocator). - * - * @ingroup allocator - * - * Usage example: - * @code - * GhosttyAllocator allocator = { - * .vtable = &my_allocator_vtable, - * .ctx = my_allocator_state - * }; - * @endcode - */ -typedef struct { - /** - * Opaque context pointer passed to all vtable functions. - * This allows the allocator implementation to maintain state - * or reference external resources needed for memory management. - */ - void *ctx; - - /** - * Pointer to the allocator's vtable containing function pointers - * for memory operations (alloc, resize, remap, free). - */ - const GhosttyAllocatorVtable *vtable; -} GhosttyAllocator; - -/** @} */ // end of allocator group - -//------------------------------------------------------------------- -// Key Encoding - -/** @defgroup key Key Encoding - * - * Utilities for encoding key events into terminal escape sequences, - * supporting both legacy encoding as well as Kitty Keyboard Protocol. - * - * ## Basic Usage - * - * 1. Create an encoder instance with ghostty_key_encoder_new() - * 2. Configure encoder options with ghostty_key_encoder_setopt(). - * 3. For each key event: - * - Create a key event with ghostty_key_event_new() - * - Set event properties (action, key, modifiers, etc.) - * - Encode with ghostty_key_encoder_encode() - * - Free the event with ghostty_key_event_free() - * - Note: You can also reuse the same key event multiple times by - * changing its properties. - * 4. Free the encoder with ghostty_key_encoder_free() when done - * - * ## Example - * - * @code{.c} - * #include - * #include - * #include - * - * int main() { - * // Create encoder - * GhosttyKeyEncoder encoder; - * GhosttyResult result = ghostty_key_encoder_new(NULL, &encoder); - * assert(result == GHOSTTY_SUCCESS); - * - * // Enable Kitty keyboard protocol with all features - * ghostty_key_encoder_setopt(encoder, GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS, - * &(uint8_t){GHOSTTY_KITTY_KEY_ALL}); - * - * // Create and configure key event for Ctrl+C press - * GhosttyKeyEvent event; - * result = ghostty_key_event_new(NULL, &event); - * assert(result == GHOSTTY_SUCCESS); - * ghostty_key_event_set_action(event, GHOSTTY_KEY_ACTION_PRESS); - * ghostty_key_event_set_key(event, GHOSTTY_KEY_C); - * ghostty_key_event_set_mods(event, GHOSTTY_MODS_CTRL); - * - * // Encode the key event - * char buf[128]; - * size_t written = 0; - * result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written); - * assert(result == GHOSTTY_SUCCESS); - * - * // Use the encoded sequence (e.g., write to terminal) - * fwrite(buf, 1, written, stdout); - * - * // Cleanup - * ghostty_key_event_free(event); - * ghostty_key_encoder_free(encoder); - * return 0; - * } - * @endcode - * - * For a complete working example, see example/c-vt-key-encode in the - * repository. - * - * @{ - */ - -/** - * Opaque handle to a key event. - * - * This handle represents a keyboard input event containing information about - * the physical key pressed, modifiers, and generated text. The event can be - * configured using the `ghostty_key_event_set_*` functions. - * - * @ingroup key - */ -typedef struct GhosttyKeyEvent *GhosttyKeyEvent; - -/** - * Keyboard input event types. - * - * @ingroup key - */ -typedef enum { - /** Key was released */ - GHOSTTY_KEY_ACTION_RELEASE = 0, - /** Key was pressed */ - GHOSTTY_KEY_ACTION_PRESS = 1, - /** Key is being repeated (held down) */ - GHOSTTY_KEY_ACTION_REPEAT = 2, -} GhosttyKeyAction; - -/** - * Keyboard modifier keys bitmask. - * - * A bitmask representing all keyboard modifiers. This tracks which modifier keys - * are pressed and, where supported by the platform, which side (left or right) - * of each modifier is active. - * - * Use the GHOSTTY_MODS_* constants to test and set individual modifiers. - * - * Modifier side bits are only meaningful when the corresponding modifier bit is set. - * Not all platforms support distinguishing between left and right modifier - * keys and Ghostty is built to expect that some platforms may not provide this - * information. - * - * @ingroup key - */ -typedef uint16_t GhosttyMods; - -/** Shift key is pressed */ -#define GHOSTTY_MODS_SHIFT (1 << 0) -/** Control key is pressed */ -#define GHOSTTY_MODS_CTRL (1 << 1) -/** Alt/Option key is pressed */ -#define GHOSTTY_MODS_ALT (1 << 2) -/** Super/Command/Windows key is pressed */ -#define GHOSTTY_MODS_SUPER (1 << 3) -/** Caps Lock is active */ -#define GHOSTTY_MODS_CAPS_LOCK (1 << 4) -/** Num Lock is active */ -#define GHOSTTY_MODS_NUM_LOCK (1 << 5) - -/** - * Right shift is pressed (0 = left, 1 = right). - * Only meaningful when GHOSTTY_MODS_SHIFT is set. - */ -#define GHOSTTY_MODS_SHIFT_SIDE (1 << 6) -/** - * Right ctrl is pressed (0 = left, 1 = right). - * Only meaningful when GHOSTTY_MODS_CTRL is set. - */ -#define GHOSTTY_MODS_CTRL_SIDE (1 << 7) -/** - * Right alt is pressed (0 = left, 1 = right). - * Only meaningful when GHOSTTY_MODS_ALT is set. - */ -#define GHOSTTY_MODS_ALT_SIDE (1 << 8) -/** - * Right super is pressed (0 = left, 1 = right). - * Only meaningful when GHOSTTY_MODS_SUPER is set. - */ -#define GHOSTTY_MODS_SUPER_SIDE (1 << 9) - -/** - * Physical key codes. - * - * The set of key codes that Ghostty is aware of. These represent physical keys - * on the keyboard and are layout-independent. For example, the "a" key on a US - * keyboard is the same as the "ф" key on a Russian keyboard, but both will - * report the same key_a value. - * - * Layout-dependent strings are provided separately as UTF-8 text and are produced - * by the platform. These values are based on the W3C UI Events KeyboardEvent code - * standard. See: https://www.w3.org/TR/uievents-code - * - * @ingroup key - */ -typedef enum { - GHOSTTY_KEY_UNIDENTIFIED = 0, - - // Writing System Keys (W3C § 3.1.1) - GHOSTTY_KEY_BACKQUOTE, - GHOSTTY_KEY_BACKSLASH, - GHOSTTY_KEY_BRACKET_LEFT, - GHOSTTY_KEY_BRACKET_RIGHT, - GHOSTTY_KEY_COMMA, - GHOSTTY_KEY_DIGIT_0, - GHOSTTY_KEY_DIGIT_1, - GHOSTTY_KEY_DIGIT_2, - GHOSTTY_KEY_DIGIT_3, - GHOSTTY_KEY_DIGIT_4, - GHOSTTY_KEY_DIGIT_5, - GHOSTTY_KEY_DIGIT_6, - GHOSTTY_KEY_DIGIT_7, - GHOSTTY_KEY_DIGIT_8, - GHOSTTY_KEY_DIGIT_9, - GHOSTTY_KEY_EQUAL, - GHOSTTY_KEY_INTL_BACKSLASH, - GHOSTTY_KEY_INTL_RO, - GHOSTTY_KEY_INTL_YEN, - GHOSTTY_KEY_A, - GHOSTTY_KEY_B, - GHOSTTY_KEY_C, - GHOSTTY_KEY_D, - GHOSTTY_KEY_E, - GHOSTTY_KEY_F, - GHOSTTY_KEY_G, - GHOSTTY_KEY_H, - GHOSTTY_KEY_I, - GHOSTTY_KEY_J, - GHOSTTY_KEY_K, - GHOSTTY_KEY_L, - GHOSTTY_KEY_M, - GHOSTTY_KEY_N, - GHOSTTY_KEY_O, - GHOSTTY_KEY_P, - GHOSTTY_KEY_Q, - GHOSTTY_KEY_R, - GHOSTTY_KEY_S, - GHOSTTY_KEY_T, - GHOSTTY_KEY_U, - GHOSTTY_KEY_V, - GHOSTTY_KEY_W, - GHOSTTY_KEY_X, - GHOSTTY_KEY_Y, - GHOSTTY_KEY_Z, - GHOSTTY_KEY_MINUS, - GHOSTTY_KEY_PERIOD, - GHOSTTY_KEY_QUOTE, - GHOSTTY_KEY_SEMICOLON, - GHOSTTY_KEY_SLASH, - - // Functional Keys (W3C § 3.1.2) - GHOSTTY_KEY_ALT_LEFT, - GHOSTTY_KEY_ALT_RIGHT, - GHOSTTY_KEY_BACKSPACE, - GHOSTTY_KEY_CAPS_LOCK, - GHOSTTY_KEY_CONTEXT_MENU, - GHOSTTY_KEY_CONTROL_LEFT, - GHOSTTY_KEY_CONTROL_RIGHT, - GHOSTTY_KEY_ENTER, - GHOSTTY_KEY_META_LEFT, - GHOSTTY_KEY_META_RIGHT, - GHOSTTY_KEY_SHIFT_LEFT, - GHOSTTY_KEY_SHIFT_RIGHT, - GHOSTTY_KEY_SPACE, - GHOSTTY_KEY_TAB, - GHOSTTY_KEY_CONVERT, - GHOSTTY_KEY_KANA_MODE, - GHOSTTY_KEY_NON_CONVERT, - - // Control Pad Section (W3C § 3.2) - GHOSTTY_KEY_DELETE, - GHOSTTY_KEY_END, - GHOSTTY_KEY_HELP, - GHOSTTY_KEY_HOME, - GHOSTTY_KEY_INSERT, - GHOSTTY_KEY_PAGE_DOWN, - GHOSTTY_KEY_PAGE_UP, - - // Arrow Pad Section (W3C § 3.3) - GHOSTTY_KEY_ARROW_DOWN, - GHOSTTY_KEY_ARROW_LEFT, - GHOSTTY_KEY_ARROW_RIGHT, - GHOSTTY_KEY_ARROW_UP, - - // Numpad Section (W3C § 3.4) - GHOSTTY_KEY_NUM_LOCK, - GHOSTTY_KEY_NUMPAD_0, - GHOSTTY_KEY_NUMPAD_1, - GHOSTTY_KEY_NUMPAD_2, - GHOSTTY_KEY_NUMPAD_3, - GHOSTTY_KEY_NUMPAD_4, - GHOSTTY_KEY_NUMPAD_5, - GHOSTTY_KEY_NUMPAD_6, - GHOSTTY_KEY_NUMPAD_7, - GHOSTTY_KEY_NUMPAD_8, - GHOSTTY_KEY_NUMPAD_9, - GHOSTTY_KEY_NUMPAD_ADD, - GHOSTTY_KEY_NUMPAD_BACKSPACE, - GHOSTTY_KEY_NUMPAD_CLEAR, - GHOSTTY_KEY_NUMPAD_CLEAR_ENTRY, - GHOSTTY_KEY_NUMPAD_COMMA, - GHOSTTY_KEY_NUMPAD_DECIMAL, - GHOSTTY_KEY_NUMPAD_DIVIDE, - GHOSTTY_KEY_NUMPAD_ENTER, - GHOSTTY_KEY_NUMPAD_EQUAL, - GHOSTTY_KEY_NUMPAD_MEMORY_ADD, - GHOSTTY_KEY_NUMPAD_MEMORY_CLEAR, - GHOSTTY_KEY_NUMPAD_MEMORY_RECALL, - GHOSTTY_KEY_NUMPAD_MEMORY_STORE, - GHOSTTY_KEY_NUMPAD_MEMORY_SUBTRACT, - GHOSTTY_KEY_NUMPAD_MULTIPLY, - GHOSTTY_KEY_NUMPAD_PAREN_LEFT, - GHOSTTY_KEY_NUMPAD_PAREN_RIGHT, - GHOSTTY_KEY_NUMPAD_SUBTRACT, - GHOSTTY_KEY_NUMPAD_SEPARATOR, - GHOSTTY_KEY_NUMPAD_UP, - GHOSTTY_KEY_NUMPAD_DOWN, - GHOSTTY_KEY_NUMPAD_RIGHT, - GHOSTTY_KEY_NUMPAD_LEFT, - GHOSTTY_KEY_NUMPAD_BEGIN, - GHOSTTY_KEY_NUMPAD_HOME, - GHOSTTY_KEY_NUMPAD_END, - GHOSTTY_KEY_NUMPAD_INSERT, - GHOSTTY_KEY_NUMPAD_DELETE, - GHOSTTY_KEY_NUMPAD_PAGE_UP, - GHOSTTY_KEY_NUMPAD_PAGE_DOWN, - - // Function Section (W3C § 3.5) - GHOSTTY_KEY_ESCAPE, - GHOSTTY_KEY_F1, - GHOSTTY_KEY_F2, - GHOSTTY_KEY_F3, - GHOSTTY_KEY_F4, - GHOSTTY_KEY_F5, - GHOSTTY_KEY_F6, - GHOSTTY_KEY_F7, - GHOSTTY_KEY_F8, - GHOSTTY_KEY_F9, - GHOSTTY_KEY_F10, - GHOSTTY_KEY_F11, - GHOSTTY_KEY_F12, - GHOSTTY_KEY_F13, - GHOSTTY_KEY_F14, - GHOSTTY_KEY_F15, - GHOSTTY_KEY_F16, - GHOSTTY_KEY_F17, - GHOSTTY_KEY_F18, - GHOSTTY_KEY_F19, - GHOSTTY_KEY_F20, - GHOSTTY_KEY_F21, - GHOSTTY_KEY_F22, - GHOSTTY_KEY_F23, - GHOSTTY_KEY_F24, - GHOSTTY_KEY_F25, - GHOSTTY_KEY_FN, - GHOSTTY_KEY_FN_LOCK, - GHOSTTY_KEY_PRINT_SCREEN, - GHOSTTY_KEY_SCROLL_LOCK, - GHOSTTY_KEY_PAUSE, - - // Media Keys (W3C § 3.6) - GHOSTTY_KEY_BROWSER_BACK, - GHOSTTY_KEY_BROWSER_FAVORITES, - GHOSTTY_KEY_BROWSER_FORWARD, - GHOSTTY_KEY_BROWSER_HOME, - GHOSTTY_KEY_BROWSER_REFRESH, - GHOSTTY_KEY_BROWSER_SEARCH, - GHOSTTY_KEY_BROWSER_STOP, - GHOSTTY_KEY_EJECT, - GHOSTTY_KEY_LAUNCH_APP_1, - GHOSTTY_KEY_LAUNCH_APP_2, - GHOSTTY_KEY_LAUNCH_MAIL, - GHOSTTY_KEY_MEDIA_PLAY_PAUSE, - GHOSTTY_KEY_MEDIA_SELECT, - GHOSTTY_KEY_MEDIA_STOP, - GHOSTTY_KEY_MEDIA_TRACK_NEXT, - GHOSTTY_KEY_MEDIA_TRACK_PREVIOUS, - GHOSTTY_KEY_POWER, - GHOSTTY_KEY_SLEEP, - GHOSTTY_KEY_AUDIO_VOLUME_DOWN, - GHOSTTY_KEY_AUDIO_VOLUME_MUTE, - GHOSTTY_KEY_AUDIO_VOLUME_UP, - GHOSTTY_KEY_WAKE_UP, - - // Legacy, Non-standard, and Special Keys (W3C § 3.7) - GHOSTTY_KEY_COPY, - GHOSTTY_KEY_CUT, - GHOSTTY_KEY_PASTE, -} GhosttyKey; - -/** - * Kitty keyboard protocol flags. - * - * Bitflags representing the various modes of the Kitty keyboard protocol. - * These can be combined using bitwise OR operations. Valid values all - * start with `GHOSTTY_KITTY_KEY_`. - * - * @ingroup key - */ -typedef uint8_t GhosttyKittyKeyFlags; - -/** Kitty keyboard protocol disabled (all flags off) */ -#define GHOSTTY_KITTY_KEY_DISABLED 0 - -/** Disambiguate escape codes */ -#define GHOSTTY_KITTY_KEY_DISAMBIGUATE (1 << 0) - -/** Report key press and release events */ -#define GHOSTTY_KITTY_KEY_REPORT_EVENTS (1 << 1) - -/** Report alternate key codes */ -#define GHOSTTY_KITTY_KEY_REPORT_ALTERNATES (1 << 2) - -/** Report all key events including those normally handled by the terminal */ -#define GHOSTTY_KITTY_KEY_REPORT_ALL (1 << 3) - -/** Report associated text with key events */ -#define GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED (1 << 4) - -/** All Kitty keyboard protocol flags enabled */ -#define GHOSTTY_KITTY_KEY_ALL (GHOSTTY_KITTY_KEY_DISAMBIGUATE | GHOSTTY_KITTY_KEY_REPORT_EVENTS | GHOSTTY_KITTY_KEY_REPORT_ALTERNATES | GHOSTTY_KITTY_KEY_REPORT_ALL | GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED) - -/** - * macOS option key behavior. - * - * Determines whether the "option" key on macOS is treated as "alt" or not. - * See the Ghostty `macos-option-as-alt` configuration option for more details. - * - * @ingroup key - */ -typedef enum { - /** Option key is not treated as alt */ - GHOSTTY_OPTION_AS_ALT_FALSE = 0, - /** Option key is treated as alt */ - GHOSTTY_OPTION_AS_ALT_TRUE = 1, - /** Only left option key is treated as alt */ - GHOSTTY_OPTION_AS_ALT_LEFT = 2, - /** Only right option key is treated as alt */ - GHOSTTY_OPTION_AS_ALT_RIGHT = 3, -} GhosttyOptionAsAlt; - -/** - * Create a new key event instance. - * - * Creates a new key event with default values. The event must be freed using - * ghostty_key_event_free() when no longer needed. - * - * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator - * @param event Pointer to store the created key event handle - * @return GHOSTTY_SUCCESS on success, or an error code on failure - * - * @ingroup key - */ -GhosttyResult ghostty_key_event_new(const GhosttyAllocator *allocator, GhosttyKeyEvent *event); - -/** - * Free a key event instance. - * - * Releases all resources associated with the key event. After this call, - * the event handle becomes invalid and must not be used. - * - * @param event The key event handle to free (may be NULL) - * - * @ingroup key - */ -void ghostty_key_event_free(GhosttyKeyEvent event); - -/** - * Set the key action (press, release, repeat). - * - * @param event The key event handle, must not be NULL - * @param action The action to set - * - * @ingroup key - */ -void ghostty_key_event_set_action(GhosttyKeyEvent event, GhosttyKeyAction action); - -/** - * Get the key action (press, release, repeat). - * - * @param event The key event handle, must not be NULL - * @return The key action - * - * @ingroup key - */ -GhosttyKeyAction ghostty_key_event_get_action(GhosttyKeyEvent event); - -/** - * Set the physical key code. - * - * @param event The key event handle, must not be NULL - * @param key The physical key code to set - * - * @ingroup key - */ -void ghostty_key_event_set_key(GhosttyKeyEvent event, GhosttyKey key); - -/** - * Get the physical key code. - * - * @param event The key event handle, must not be NULL - * @return The physical key code - * - * @ingroup key - */ -GhosttyKey ghostty_key_event_get_key(GhosttyKeyEvent event); - -/** - * Set the modifier keys bitmask. - * - * @param event The key event handle, must not be NULL - * @param mods The modifier keys bitmask to set - * - * @ingroup key - */ -void ghostty_key_event_set_mods(GhosttyKeyEvent event, GhosttyMods mods); - -/** - * Get the modifier keys bitmask. - * - * @param event The key event handle, must not be NULL - * @return The modifier keys bitmask - * - * @ingroup key - */ -GhosttyMods ghostty_key_event_get_mods(GhosttyKeyEvent event); - -/** - * Set the consumed modifiers bitmask. - * - * @param event The key event handle, must not be NULL - * @param consumed_mods The consumed modifiers bitmask to set - * - * @ingroup key - */ -void ghostty_key_event_set_consumed_mods(GhosttyKeyEvent event, GhosttyMods consumed_mods); - -/** - * Get the consumed modifiers bitmask. - * - * @param event The key event handle, must not be NULL - * @return The consumed modifiers bitmask - * - * @ingroup key - */ -GhosttyMods ghostty_key_event_get_consumed_mods(GhosttyKeyEvent event); - -/** - * Set whether the key event is part of a composition sequence. - * - * @param event The key event handle, must not be NULL - * @param composing Whether the key event is part of a composition sequence - * - * @ingroup key - */ -void ghostty_key_event_set_composing(GhosttyKeyEvent event, bool composing); - -/** - * Get whether the key event is part of a composition sequence. - * - * @param event The key event handle, must not be NULL - * @return Whether the key event is part of a composition sequence - * - * @ingroup key - */ -bool ghostty_key_event_get_composing(GhosttyKeyEvent event); - -/** - * Set the UTF-8 text generated by the key event. - * - * The key event does NOT take ownership of the text pointer. The caller - * must ensure the string remains valid for the lifetime needed by the event. - * - * @param event The key event handle, must not be NULL - * @param utf8 The UTF-8 text to set (or NULL for empty) - * @param len Length of the UTF-8 text in bytes - * - * @ingroup key - */ -void ghostty_key_event_set_utf8(GhosttyKeyEvent event, const char *utf8, size_t len); - -/** - * Get the UTF-8 text generated by the key event. - * - * The returned pointer is valid until the event is freed or the UTF-8 text is modified. - * - * @param event The key event handle, must not be NULL - * @param len Pointer to store the length of the UTF-8 text in bytes (may be NULL) - * @return The UTF-8 text (or NULL for empty) - * - * @ingroup key - */ -const char *ghostty_key_event_get_utf8(GhosttyKeyEvent event, size_t *len); - -/** - * Set the unshifted Unicode codepoint. - * - * @param event The key event handle, must not be NULL - * @param codepoint The unshifted Unicode codepoint to set - * - * @ingroup key - */ -void ghostty_key_event_set_unshifted_codepoint(GhosttyKeyEvent event, uint32_t codepoint); - -/** - * Get the unshifted Unicode codepoint. - * - * @param event The key event handle, must not be NULL - * @return The unshifted Unicode codepoint - * - * @ingroup key - */ -uint32_t ghostty_key_event_get_unshifted_codepoint(GhosttyKeyEvent event); - -/** - * Opaque handle to a key encoder instance. - * - * This handle represents a key encoder that converts key events into terminal - * escape sequences. The encoder supports both legacy encoding and the Kitty - * Keyboard Protocol, depending on the options set. - * - * @ingroup key - */ -typedef struct GhosttyKeyEncoder *GhosttyKeyEncoder; - -/** - * Key encoder option identifiers. - * - * These values are used with ghostty_key_encoder_setopt() to configure - * the behavior of the key encoder. - * - * @ingroup key - */ -typedef enum { - /** Terminal DEC mode 1: cursor key application mode (value: bool) */ - GHOSTTY_KEY_ENCODER_OPT_CURSOR_KEY_APPLICATION = 0, - - /** Terminal DEC mode 66: keypad key application mode (value: bool) */ - GHOSTTY_KEY_ENCODER_OPT_KEYPAD_KEY_APPLICATION = 1, - - /** Terminal DEC mode 1035: ignore keypad with numlock (value: bool) */ - GHOSTTY_KEY_ENCODER_OPT_IGNORE_KEYPAD_WITH_NUMLOCK = 2, - - /** Terminal DEC mode 1036: alt sends escape prefix (value: bool) */ - GHOSTTY_KEY_ENCODER_OPT_ALT_ESC_PREFIX = 3, - - /** xterm modifyOtherKeys mode 2 (value: bool) */ - GHOSTTY_KEY_ENCODER_OPT_MODIFY_OTHER_KEYS_STATE_2 = 4, - - /** Kitty keyboard protocol flags (value: GhosttyKittyKeyFlags bitmask) */ - GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS = 5, - - /** macOS option-as-alt setting (value: GhosttyOptionAsAlt) */ - GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT = 6, -} GhosttyKeyEncoderOption; - -/** - * Create a new key encoder instance. - * - * Creates a new key encoder with default options. The encoder can be configured - * using ghostty_key_encoder_setopt() and must be freed using - * ghostty_key_encoder_free() when no longer needed. - * - * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator - * @param encoder Pointer to store the created encoder handle - * @return GHOSTTY_SUCCESS on success, or an error code on failure - * - * @ingroup key - */ -GhosttyResult ghostty_key_encoder_new(const GhosttyAllocator *allocator, GhosttyKeyEncoder *encoder); - -/** - * Free a key encoder instance. - * - * Releases all resources associated with the key encoder. After this call, - * the encoder handle becomes invalid and must not be used. - * - * @param encoder The encoder handle to free (may be NULL) - * - * @ingroup key - */ -void ghostty_key_encoder_free(GhosttyKeyEncoder encoder); - -/** - * Set an option on the key encoder. - * - * Configures the behavior of the key encoder. Options control various aspects - * of encoding such as terminal modes (cursor key application mode, keypad mode), - * protocol selection (Kitty keyboard protocol flags), and platform-specific - * behaviors (macOS option-as-alt). - * - * A null pointer value does nothing. It does not reset the value to the - * default. The setopt call will do nothing. - * - * @param encoder The encoder handle, must not be NULL - * @param option The option to set - * @param value Pointer to the value to set (type depends on the option) - * - * @ingroup key - */ -void ghostty_key_encoder_setopt(GhosttyKeyEncoder encoder, GhosttyKeyEncoderOption option, const void *value); - -/** - * Encode a key event into a terminal escape sequence. - * - * Converts a key event into the appropriate terminal escape sequence based on - * the encoder's current options. The sequence is written to the provided buffer. - * - * Not all key events produce output. For example, unmodified modifier keys - * typically don't generate escape sequences. Check the out_len parameter to - * determine if any data was written. - * - * If the output buffer is too small, this function returns GHOSTTY_OUT_OF_MEMORY - * and out_len will contain the required buffer size. The caller can then - * allocate a larger buffer and call the function again. - * - * @param encoder The encoder handle, must not be NULL - * @param event The key event to encode, must not be NULL - * @param out_buf Buffer to write the encoded sequence to - * @param out_buf_size Size of the output buffer in bytes - * @param out_len Pointer to store the number of bytes written (may be NULL) - * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY if buffer too small, or other error code - * - * ## Example: Calculate required buffer size - * - * @code{.c} - * // Query the required size with a NULL buffer (always returns OUT_OF_MEMORY) - * size_t required = 0; - * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, NULL, 0, &required); - * assert(result == GHOSTTY_OUT_OF_MEMORY); - * - * // Allocate buffer of required size - * char *buf = malloc(required); - * - * // Encode with properly sized buffer - * size_t written = 0; - * result = ghostty_key_encoder_encode(encoder, event, buf, required, &written); - * assert(result == GHOSTTY_SUCCESS); - * - * // Use the encoded sequence... - * - * free(buf); - * @endcode - * - * ## Example: Direct encoding with static buffer - * - * @code{.c} - * // Most escape sequences are short, so a static buffer often suffices - * char buf[128]; - * size_t written = 0; - * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written); - * - * if (result == GHOSTTY_SUCCESS) { - * // Write the encoded sequence to the terminal - * write(pty_fd, buf, written); - * } else if (result == GHOSTTY_OUT_OF_MEMORY) { - * // Buffer too small, written contains required size - * char *dynamic_buf = malloc(written); - * result = ghostty_key_encoder_encode(encoder, event, dynamic_buf, written, &written); - * assert(result == GHOSTTY_SUCCESS); - * write(pty_fd, dynamic_buf, written); - * free(dynamic_buf); - * } - * @endcode - * - * @ingroup key - */ -GhosttyResult ghostty_key_encoder_encode(GhosttyKeyEncoder encoder, GhosttyKeyEvent event, char *out_buf, size_t out_buf_size, size_t *out_len); - -/** @} */ // end of key group - -//------------------------------------------------------------------- -// OSC Parser - -/** @defgroup osc OSC Parser - * - * OSC (Operating System Command) sequence parser and command handling. - * - * The parser operates in a streaming fashion, processing input byte-by-byte - * to handle OSC sequences that may arrive in fragments across multiple reads. - * This interface makes it easy to integrate into most environments and avoids - * over-allocating buffers. - * - * ## Basic Usage - * - * 1. Create a parser instance with ghostty_osc_new() - * 2. Feed bytes to the parser using ghostty_osc_next() - * 3. Finalize parsing with ghostty_osc_end() to get the command - * 4. Query command type and extract data using ghostty_osc_command_type() - * and ghostty_osc_command_data() - * 5. Free the parser with ghostty_osc_free() when done - * - * @{ - */ - -/** - * Create a new OSC parser instance. - * - * Creates a new OSC (Operating System Command) parser using the provided - * allocator. The parser must be freed using ghostty_vt_osc_free() when - * no longer needed. - * - * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator - * @param parser Pointer to store the created parser handle - * @return GHOSTTY_SUCCESS on success, or an error code on failure - */ -GhosttyResult ghostty_osc_new(const GhosttyAllocator *allocator, GhosttyOscParser *parser); - -/** - * Free an OSC parser instance. - * - * Releases all resources associated with the OSC parser. After this call, - * the parser handle becomes invalid and must not be used. - * - * @param parser The parser handle to free (may be NULL) - */ -void ghostty_osc_free(GhosttyOscParser parser); - -/** - * Reset an OSC parser instance to its initial state. - * - * Resets the parser state, clearing any partially parsed OSC sequences - * and returning the parser to its initial state. This is useful for - * reusing a parser instance or recovering from parse errors. - * - * @param parser The parser handle to reset, must not be null. - */ -void ghostty_osc_reset(GhosttyOscParser parser); - -/** - * Parse the next byte in an OSC sequence. - * - * Processes a single byte as part of an OSC sequence. The parser maintains - * internal state to track the progress through the sequence. Call this - * function for each byte in the sequence data. - * - * When finished pumping the parser with bytes, call ghostty_osc_end - * to get the final result. - * - * @param parser The parser handle, must not be null. - * @param byte The next byte to parse - */ -void ghostty_osc_next(GhosttyOscParser parser, uint8_t byte); - -/** - * Finalize OSC parsing and retrieve the parsed command. - * - * Call this function after feeding all bytes of an OSC sequence to the parser - * using ghostty_osc_next() with the exception of the terminating character - * (ESC or ST). This function finalizes the parsing process and returns the - * parsed OSC command. - * - * The return value is never NULL. Invalid commands will return a command - * with type GHOSTTY_OSC_COMMAND_INVALID. - * - * The terminator parameter specifies the byte that terminated the OSC sequence - * (typically 0x07 for BEL or 0x5C for ST after ESC). This information is - * preserved in the parsed command so that responses can use the same terminator - * format for better compatibility with the calling program. For commands that - * do not require a response, this parameter is ignored and the resulting - * command will not retain the terminator information. - * - * The returned command handle is valid until the next call to any - * `ghostty_osc_*` function with the same parser instance with the exception - * of command introspection functions such as `ghostty_osc_command_type`. - * - * @param parser The parser handle, must not be null. - * @param terminator The terminating byte of the OSC sequence (0x07 for BEL, 0x5C for ST) - * @return Handle to the parsed OSC command - */ -GhosttyOscCommand ghostty_osc_end(GhosttyOscParser parser, uint8_t terminator); - -/** - * Get the type of an OSC command. - * - * Returns the type identifier for the given OSC command. This can be used - * to determine what kind of command was parsed and what data might be - * available from it. - * - * @param command The OSC command handle to query (may be NULL) - * @return The command type, or GHOSTTY_OSC_COMMAND_INVALID if command is NULL - */ -GhosttyOscCommandType ghostty_osc_command_type(GhosttyOscCommand command); - -/** - * Extract data from an OSC command. - * - * Extracts typed data from the given OSC command based on the specified - * data type. The output pointer must be of the appropriate type for the - * requested data kind. Valid command types, output types, and memory - * safety information are documented in the `GhosttyOscCommandData` enum. - * - * @param command The OSC command handle to query (may be NULL) - * @param data The type of data to extract - * @param out Pointer to store the extracted data (type depends on data parameter) - * @return true if data extraction was successful, false otherwise - */ -bool ghostty_osc_command_data(GhosttyOscCommand command, GhosttyOscCommandData data, void *out); - -/** @} */ // end of osc group +#include +#include +#include +#include #ifdef __cplusplus } diff --git a/include/ghostty/vt/allocator.h b/include/ghostty/vt/allocator.h new file mode 100644 index 000000000..4cebe91bb --- /dev/null +++ b/include/ghostty/vt/allocator.h @@ -0,0 +1,196 @@ +/** + * @file allocator.h + * + * Memory management interface for libghostty-vt. + */ + +#ifndef GHOSTTY_VT_ALLOCATOR_H +#define GHOSTTY_VT_ALLOCATOR_H + +#include +#include +#include + +/** @defgroup allocator Memory Management + * + * libghostty-vt does require memory allocation for various operations, + * but is resilient to allocation failures and will gracefully handle + * out-of-memory situations by returning error codes. + * + * The exact memory management semantics are documented in the relevant + * functions and data structures. + * + * libghostty-vt uses explicit memory allocation via an allocator + * interface provided by GhosttyAllocator. The interface is based on the + * [Zig](https://ziglang.org) allocator interface, since this has been + * shown to be a flexible and powerful interface in practice and enables + * a wide variety of allocation strategies. + * + * **For the common case, you can pass NULL as the allocator for any + * function that accepts one,** and libghostty will use a default allocator. + * The default allocator will be libc malloc/free if libc is linked. + * Otherwise, a custom allocator is used (currently Zig's SMP allocator) + * that doesn't require any external dependencies. + * + * ## Basic Usage + * + * For simple use cases, you can ignore this interface entirely by passing NULL + * as the allocator parameter to functions that accept one. This will use the + * default allocator (typically libc malloc/free, if libc is linked, but + * we provide our own default allocator if libc isn't linked). + * + * To use a custom allocator: + * 1. Implement the GhosttyAllocatorVtable function pointers + * 2. Create a GhosttyAllocator struct with your vtable and context + * 3. Pass the allocator to functions that accept one + * + * @{ + */ + +/** + * Function table for custom memory allocator operations. + * + * This vtable defines the interface for a custom memory allocator. All + * function pointers must be valid and non-NULL. + * + * @ingroup allocator + * + * If you're not going to use a custom allocator, you can ignore all of + * this. All functions that take an allocator pointer allow NULL to use a + * default allocator. + * + * The interface is based on the Zig allocator interface. I'll say up front + * that it is easy to look at this interface and think "wow, this is really + * overcomplicated". The reason for this complexity is well thought out by + * the Zig folks, and it enables a diverse set of allocation strategies + * as shown by the Zig ecosystem. As a consolation, please note that many + * of the arguments are only needed for advanced use cases and can be + * safely ignored in simple implementations. For example, if you look at + * the Zig implementation of the libc allocator in `lib/std/heap.zig` + * (search for CAllocator), you'll see it is very simple. + * + * We chose to align with the Zig allocator interface because: + * + * 1. It is a proven interface that serves a wide variety of use cases + * in the real world via the Zig ecosystem. It's shown to work. + * + * 2. Our core implementation itself is Zig, and this lets us very + * cheaply and easily convert between C and Zig allocators. + * + * NOTE(mitchellh): In the future, we can have default implementations of + * resize/remap and allow those to be null. + */ +typedef struct { + /** + * Return a pointer to `len` bytes with specified `alignment`, or return + * `NULL` indicating the allocation failed. + * + * @param ctx The allocator context + * @param len Number of bytes to allocate + * @param alignment Required alignment for the allocation. Guaranteed to + * be a power of two between 1 and 16 inclusive. + * @param ret_addr First return address of the allocation call stack (0 if not provided) + * @return Pointer to allocated memory, or NULL if allocation failed + */ + void* (*alloc)(void *ctx, size_t len, uint8_t alignment, uintptr_t ret_addr); + + /** + * Attempt to expand or shrink memory in place. + * + * `memory_len` must equal the length requested from the most recent + * successful call to `alloc`, `resize`, or `remap`. `alignment` must + * equal the same value that was passed as the `alignment` parameter to + * the original `alloc` call. + * + * `new_len` must be greater than zero. + * + * @param ctx The allocator context + * @param memory Pointer to the memory block to resize + * @param memory_len Current size of the memory block + * @param alignment Alignment (must match original allocation) + * @param new_len New requested size + * @param ret_addr First return address of the allocation call stack (0 if not provided) + * @return true if resize was successful in-place, false if relocation would be required + */ + bool (*resize)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr); + + /** + * Attempt to expand or shrink memory, allowing relocation. + * + * `memory_len` must equal the length requested from the most recent + * successful call to `alloc`, `resize`, or `remap`. `alignment` must + * equal the same value that was passed as the `alignment` parameter to + * the original `alloc` call. + * + * A non-`NULL` return value indicates the resize was successful. The + * allocation may have same address, or may have been relocated. In either + * case, the allocation now has size of `new_len`. A `NULL` return value + * indicates that the resize would be equivalent to allocating new memory, + * copying the bytes from the old memory, and then freeing the old memory. + * In such case, it is more efficient for the caller to perform the copy. + * + * `new_len` must be greater than zero. + * + * @param ctx The allocator context + * @param memory Pointer to the memory block to remap + * @param memory_len Current size of the memory block + * @param alignment Alignment (must match original allocation) + * @param new_len New requested size + * @param ret_addr First return address of the allocation call stack (0 if not provided) + * @return Pointer to resized memory (may be relocated), or NULL if manual copy is needed + */ + void* (*remap)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, size_t new_len, uintptr_t ret_addr); + + /** + * Free and invalidate a region of memory. + * + * `memory_len` must equal the length requested from the most recent + * successful call to `alloc`, `resize`, or `remap`. `alignment` must + * equal the same value that was passed as the `alignment` parameter to + * the original `alloc` call. + * + * @param ctx The allocator context + * @param memory Pointer to the memory block to free + * @param memory_len Size of the memory block + * @param alignment Alignment (must match original allocation) + * @param ret_addr First return address of the allocation call stack (0 if not provided) + */ + void (*free)(void *ctx, void *memory, size_t memory_len, uint8_t alignment, uintptr_t ret_addr); +} GhosttyAllocatorVtable; + +/** + * Custom memory allocator. + * + * For functions that take an allocator pointer, a NULL pointer indicates + * that the default allocator should be used. The default allocator will + * be libc malloc/free if we're linking to libc. If libc isn't linked, + * a custom allocator is used (currently Zig's SMP allocator). + * + * @ingroup allocator + * + * Usage example: + * @code + * GhosttyAllocator allocator = { + * .vtable = &my_allocator_vtable, + * .ctx = my_allocator_state + * }; + * @endcode + */ +typedef struct GhosttyAllocator { + /** + * Opaque context pointer passed to all vtable functions. + * This allows the allocator implementation to maintain state + * or reference external resources needed for memory management. + */ + void *ctx; + + /** + * Pointer to the allocator's vtable containing function pointers + * for memory operations (alloc, resize, remap, free). + */ + const GhosttyAllocatorVtable *vtable; +} GhosttyAllocator; + +/** @} */ + +#endif /* GHOSTTY_VT_ALLOCATOR_H */ diff --git a/include/ghostty/vt/key.h b/include/ghostty/vt/key.h new file mode 100644 index 000000000..772b5d43b --- /dev/null +++ b/include/ghostty/vt/key.h @@ -0,0 +1,80 @@ +/** + * @file key.h + * + * Key encoding module - encode key events into terminal escape sequences. + */ + +#ifndef GHOSTTY_VT_KEY_H +#define GHOSTTY_VT_KEY_H + +/** @defgroup key Key Encoding + * + * Utilities for encoding key events into terminal escape sequences, + * supporting both legacy encoding as well as Kitty Keyboard Protocol. + * + * ## Basic Usage + * + * 1. Create an encoder instance with ghostty_key_encoder_new() + * 2. Configure encoder options with ghostty_key_encoder_setopt(). + * 3. For each key event: + * - Create a key event with ghostty_key_event_new() + * - Set event properties (action, key, modifiers, etc.) + * - Encode with ghostty_key_encoder_encode() + * - Free the event with ghostty_key_event_free() + * - Note: You can also reuse the same key event multiple times by + * changing its properties. + * 4. Free the encoder with ghostty_key_encoder_free() when done + * + * ## Example + * + * @code{.c} + * #include + * #include + * #include + * + * int main() { + * // Create encoder + * GhosttyKeyEncoder encoder; + * GhosttyResult result = ghostty_key_encoder_new(NULL, &encoder); + * assert(result == GHOSTTY_SUCCESS); + * + * // Enable Kitty keyboard protocol with all features + * ghostty_key_encoder_setopt(encoder, GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS, + * &(uint8_t){GHOSTTY_KITTY_KEY_ALL}); + * + * // Create and configure key event for Ctrl+C press + * GhosttyKeyEvent event; + * result = ghostty_key_event_new(NULL, &event); + * assert(result == GHOSTTY_SUCCESS); + * ghostty_key_event_set_action(event, GHOSTTY_KEY_ACTION_PRESS); + * ghostty_key_event_set_key(event, GHOSTTY_KEY_C); + * ghostty_key_event_set_mods(event, GHOSTTY_MODS_CTRL); + * + * // Encode the key event + * char buf[128]; + * size_t written = 0; + * result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written); + * assert(result == GHOSTTY_SUCCESS); + * + * // Use the encoded sequence (e.g., write to terminal) + * fwrite(buf, 1, written, stdout); + * + * // Cleanup + * ghostty_key_event_free(event); + * ghostty_key_encoder_free(encoder); + * return 0; + * } + * @endcode + * + * For a complete working example, see example/c-vt-key-encode in the + * repository. + * + * @{ + */ + +#include +#include + +/** @} */ + +#endif /* GHOSTTY_VT_KEY_H */ diff --git a/include/ghostty/vt/key/encoder.h b/include/ghostty/vt/key/encoder.h new file mode 100644 index 000000000..766a29427 --- /dev/null +++ b/include/ghostty/vt/key/encoder.h @@ -0,0 +1,221 @@ +/** + * @file encoder.h + * + * Key event encoding to terminal escape sequences. + */ + +#ifndef GHOSTTY_VT_KEY_ENCODER_H +#define GHOSTTY_VT_KEY_ENCODER_H + +#include +#include +#include +#include +#include + +/** + * Opaque handle to a key encoder instance. + * + * This handle represents a key encoder that converts key events into terminal + * escape sequences. + * + * @ingroup key + */ +typedef struct GhosttyKeyEncoder *GhosttyKeyEncoder; + +/** + * Kitty keyboard protocol flags. + * + * Bitflags representing the various modes of the Kitty keyboard protocol. + * These can be combined using bitwise OR operations. Valid values all + * start with `GHOSTTY_KITTY_KEY_`. + * + * @ingroup key + */ +typedef uint8_t GhosttyKittyKeyFlags; + +/** Kitty keyboard protocol disabled (all flags off) */ +#define GHOSTTY_KITTY_KEY_DISABLED 0 + +/** Disambiguate escape codes */ +#define GHOSTTY_KITTY_KEY_DISAMBIGUATE (1 << 0) + +/** Report key press and release events */ +#define GHOSTTY_KITTY_KEY_REPORT_EVENTS (1 << 1) + +/** Report alternate key codes */ +#define GHOSTTY_KITTY_KEY_REPORT_ALTERNATES (1 << 2) + +/** Report all key events including those normally handled by the terminal */ +#define GHOSTTY_KITTY_KEY_REPORT_ALL (1 << 3) + +/** Report associated text with key events */ +#define GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED (1 << 4) + +/** All Kitty keyboard protocol flags enabled */ +#define GHOSTTY_KITTY_KEY_ALL (GHOSTTY_KITTY_KEY_DISAMBIGUATE | GHOSTTY_KITTY_KEY_REPORT_EVENTS | GHOSTTY_KITTY_KEY_REPORT_ALTERNATES | GHOSTTY_KITTY_KEY_REPORT_ALL | GHOSTTY_KITTY_KEY_REPORT_ASSOCIATED) + +/** + * macOS option key behavior. + * + * Determines whether the "option" key on macOS is treated as "alt" or not. + * See the Ghostty `macos-option-as-alt` configuration option for more details. + * + * @ingroup key + */ +typedef enum { + /** Option key is not treated as alt */ + GHOSTTY_OPTION_AS_ALT_FALSE = 0, + /** Option key is treated as alt */ + GHOSTTY_OPTION_AS_ALT_TRUE = 1, + /** Only left option key is treated as alt */ + GHOSTTY_OPTION_AS_ALT_LEFT = 2, + /** Only right option key is treated as alt */ + GHOSTTY_OPTION_AS_ALT_RIGHT = 3, +} GhosttyOptionAsAlt; + +/** + * Key encoder option identifiers. + * + * These values are used with ghostty_key_encoder_setopt() to configure + * the behavior of the key encoder. + * + * @ingroup key + */ +typedef enum { + /** Terminal DEC mode 1: cursor key application mode (value: bool) */ + GHOSTTY_KEY_ENCODER_OPT_CURSOR_KEY_APPLICATION = 0, + + /** Terminal DEC mode 66: keypad key application mode (value: bool) */ + GHOSTTY_KEY_ENCODER_OPT_KEYPAD_KEY_APPLICATION = 1, + + /** Terminal DEC mode 1035: ignore keypad with numlock (value: bool) */ + GHOSTTY_KEY_ENCODER_OPT_IGNORE_KEYPAD_WITH_NUMLOCK = 2, + + /** Terminal DEC mode 1036: alt sends escape prefix (value: bool) */ + GHOSTTY_KEY_ENCODER_OPT_ALT_ESC_PREFIX = 3, + + /** xterm modifyOtherKeys mode 2 (value: bool) */ + GHOSTTY_KEY_ENCODER_OPT_MODIFY_OTHER_KEYS_STATE_2 = 4, + + /** Kitty keyboard protocol flags (value: GhosttyKittyKeyFlags bitmask) */ + GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS = 5, + + /** macOS option-as-alt setting (value: GhosttyOptionAsAlt) */ + GHOSTTY_KEY_ENCODER_OPT_MACOS_OPTION_AS_ALT = 6, +} GhosttyKeyEncoderOption; + +/** + * Create a new key encoder instance. + * + * Creates a new key encoder with default options. The encoder can be configured + * using ghostty_key_encoder_setopt() and must be freed using + * ghostty_key_encoder_free() when no longer needed. + * + * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator + * @param encoder Pointer to store the created encoder handle + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup key + */ +GhosttyResult ghostty_key_encoder_new(const GhosttyAllocator *allocator, GhosttyKeyEncoder *encoder); + +/** + * Free a key encoder instance. + * + * Releases all resources associated with the key encoder. After this call, + * the encoder handle becomes invalid and must not be used. + * + * @param encoder The encoder handle to free (may be NULL) + * + * @ingroup key + */ +void ghostty_key_encoder_free(GhosttyKeyEncoder encoder); + +/** + * Set an option on the key encoder. + * + * Configures the behavior of the key encoder. Options control various aspects + * of encoding such as terminal modes (cursor key application mode, keypad mode), + * protocol selection (Kitty keyboard protocol flags), and platform-specific + * behaviors (macOS option-as-alt). + * + * A null pointer value does nothing. It does not reset the value to the + * default. The setopt call will do nothing. + * + * @param encoder The encoder handle, must not be NULL + * @param option The option to set + * @param value Pointer to the value to set (type depends on the option) + * + * @ingroup key + */ +void ghostty_key_encoder_setopt(GhosttyKeyEncoder encoder, GhosttyKeyEncoderOption option, const void *value); + +/** + * Encode a key event into a terminal escape sequence. + * + * Converts a key event into the appropriate terminal escape sequence based on + * the encoder's current options. The sequence is written to the provided buffer. + * + * Not all key events produce output. For example, unmodified modifier keys + * typically don't generate escape sequences. Check the out_len parameter to + * determine if any data was written. + * + * If the output buffer is too small, this function returns GHOSTTY_OUT_OF_MEMORY + * and out_len will contain the required buffer size. The caller can then + * allocate a larger buffer and call the function again. + * + * @param encoder The encoder handle, must not be NULL + * @param event The key event to encode, must not be NULL + * @param out_buf Buffer to write the encoded sequence to + * @param out_buf_size Size of the output buffer in bytes + * @param out_len Pointer to store the number of bytes written (may be NULL) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY if buffer too small, or other error code + * + * ## Example: Calculate required buffer size + * + * @code{.c} + * // Query the required size with a NULL buffer (always returns OUT_OF_MEMORY) + * size_t required = 0; + * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, NULL, 0, &required); + * assert(result == GHOSTTY_OUT_OF_MEMORY); + * + * // Allocate buffer of required size + * char *buf = malloc(required); + * + * // Encode with properly sized buffer + * size_t written = 0; + * result = ghostty_key_encoder_encode(encoder, event, buf, required, &written); + * assert(result == GHOSTTY_SUCCESS); + * + * // Use the encoded sequence... + * + * free(buf); + * @endcode + * + * ## Example: Direct encoding with static buffer + * + * @code{.c} + * // Most escape sequences are short, so a static buffer often suffices + * char buf[128]; + * size_t written = 0; + * GhosttyResult result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written); + * + * if (result == GHOSTTY_SUCCESS) { + * // Write the encoded sequence to the terminal + * write(pty_fd, buf, written); + * } else if (result == GHOSTTY_OUT_OF_MEMORY) { + * // Buffer too small, written contains required size + * char *dynamic_buf = malloc(written); + * result = ghostty_key_encoder_encode(encoder, event, dynamic_buf, written, &written); + * assert(result == GHOSTTY_SUCCESS); + * write(pty_fd, dynamic_buf, written); + * free(dynamic_buf); + * } + * @endcode + * + * @ingroup key + */ +GhosttyResult ghostty_key_encoder_encode(GhosttyKeyEncoder encoder, GhosttyKeyEvent event, char *out_buf, size_t out_buf_size, size_t *out_len); + +#endif /* GHOSTTY_VT_KEY_ENCODER_H */ diff --git a/include/ghostty/vt/key/event.h b/include/ghostty/vt/key/event.h new file mode 100644 index 000000000..dbd2e9f84 --- /dev/null +++ b/include/ghostty/vt/key/event.h @@ -0,0 +1,474 @@ +/** + * @file event.h + * + * Key event representation and manipulation. + */ + +#ifndef GHOSTTY_VT_KEY_EVENT_H +#define GHOSTTY_VT_KEY_EVENT_H + +#include +#include +#include +#include +#include + +/** + * Opaque handle to a key event. + * + * This handle represents a keyboard input event containing information about + * the physical key pressed, modifiers, and generated text. + * + * @ingroup key + */ +typedef struct GhosttyKeyEvent *GhosttyKeyEvent; + +/** + * Keyboard input event types. + * + * @ingroup key + */ +typedef enum { + /** Key was released */ + GHOSTTY_KEY_ACTION_RELEASE = 0, + /** Key was pressed */ + GHOSTTY_KEY_ACTION_PRESS = 1, + /** Key is being repeated (held down) */ + GHOSTTY_KEY_ACTION_REPEAT = 2, +} GhosttyKeyAction; + +/** + * Keyboard modifier keys bitmask. + * + * A bitmask representing all keyboard modifiers. This tracks which modifier keys + * are pressed and, where supported by the platform, which side (left or right) + * of each modifier is active. + * + * Use the GHOSTTY_MODS_* constants to test and set individual modifiers. + * + * Modifier side bits are only meaningful when the corresponding modifier bit is set. + * Not all platforms support distinguishing between left and right modifier + * keys and Ghostty is built to expect that some platforms may not provide this + * information. + * + * @ingroup key + */ +typedef uint16_t GhosttyMods; + +/** Shift key is pressed */ +#define GHOSTTY_MODS_SHIFT (1 << 0) +/** Control key is pressed */ +#define GHOSTTY_MODS_CTRL (1 << 1) +/** Alt/Option key is pressed */ +#define GHOSTTY_MODS_ALT (1 << 2) +/** Super/Command/Windows key is pressed */ +#define GHOSTTY_MODS_SUPER (1 << 3) +/** Caps Lock is active */ +#define GHOSTTY_MODS_CAPS_LOCK (1 << 4) +/** Num Lock is active */ +#define GHOSTTY_MODS_NUM_LOCK (1 << 5) + +/** + * Right shift is pressed (0 = left, 1 = right). + * Only meaningful when GHOSTTY_MODS_SHIFT is set. + */ +#define GHOSTTY_MODS_SHIFT_SIDE (1 << 6) +/** + * Right ctrl is pressed (0 = left, 1 = right). + * Only meaningful when GHOSTTY_MODS_CTRL is set. + */ +#define GHOSTTY_MODS_CTRL_SIDE (1 << 7) +/** + * Right alt is pressed (0 = left, 1 = right). + * Only meaningful when GHOSTTY_MODS_ALT is set. + */ +#define GHOSTTY_MODS_ALT_SIDE (1 << 8) +/** + * Right super is pressed (0 = left, 1 = right). + * Only meaningful when GHOSTTY_MODS_SUPER is set. + */ +#define GHOSTTY_MODS_SUPER_SIDE (1 << 9) + +/** + * Physical key codes. + * + * The set of key codes that Ghostty is aware of. These represent physical keys + * on the keyboard and are layout-independent. For example, the "a" key on a US + * keyboard is the same as the "ф" key on a Russian keyboard, but both will + * report the same key_a value. + * + * Layout-dependent strings are provided separately as UTF-8 text and are produced + * by the platform. These values are based on the W3C UI Events KeyboardEvent code + * standard. See: https://www.w3.org/TR/uievents-code + * + * @ingroup key + */ +typedef enum { + GHOSTTY_KEY_UNIDENTIFIED = 0, + + // Writing System Keys (W3C § 3.1.1) + GHOSTTY_KEY_BACKQUOTE, + GHOSTTY_KEY_BACKSLASH, + GHOSTTY_KEY_BRACKET_LEFT, + GHOSTTY_KEY_BRACKET_RIGHT, + GHOSTTY_KEY_COMMA, + GHOSTTY_KEY_DIGIT_0, + GHOSTTY_KEY_DIGIT_1, + GHOSTTY_KEY_DIGIT_2, + GHOSTTY_KEY_DIGIT_3, + GHOSTTY_KEY_DIGIT_4, + GHOSTTY_KEY_DIGIT_5, + GHOSTTY_KEY_DIGIT_6, + GHOSTTY_KEY_DIGIT_7, + GHOSTTY_KEY_DIGIT_8, + GHOSTTY_KEY_DIGIT_9, + GHOSTTY_KEY_EQUAL, + GHOSTTY_KEY_INTL_BACKSLASH, + GHOSTTY_KEY_INTL_RO, + GHOSTTY_KEY_INTL_YEN, + GHOSTTY_KEY_A, + GHOSTTY_KEY_B, + GHOSTTY_KEY_C, + GHOSTTY_KEY_D, + GHOSTTY_KEY_E, + GHOSTTY_KEY_F, + GHOSTTY_KEY_G, + GHOSTTY_KEY_H, + GHOSTTY_KEY_I, + GHOSTTY_KEY_J, + GHOSTTY_KEY_K, + GHOSTTY_KEY_L, + GHOSTTY_KEY_M, + GHOSTTY_KEY_N, + GHOSTTY_KEY_O, + GHOSTTY_KEY_P, + GHOSTTY_KEY_Q, + GHOSTTY_KEY_R, + GHOSTTY_KEY_S, + GHOSTTY_KEY_T, + GHOSTTY_KEY_U, + GHOSTTY_KEY_V, + GHOSTTY_KEY_W, + GHOSTTY_KEY_X, + GHOSTTY_KEY_Y, + GHOSTTY_KEY_Z, + GHOSTTY_KEY_MINUS, + GHOSTTY_KEY_PERIOD, + GHOSTTY_KEY_QUOTE, + GHOSTTY_KEY_SEMICOLON, + GHOSTTY_KEY_SLASH, + + // Functional Keys (W3C § 3.1.2) + GHOSTTY_KEY_ALT_LEFT, + GHOSTTY_KEY_ALT_RIGHT, + GHOSTTY_KEY_BACKSPACE, + GHOSTTY_KEY_CAPS_LOCK, + GHOSTTY_KEY_CONTEXT_MENU, + GHOSTTY_KEY_CONTROL_LEFT, + GHOSTTY_KEY_CONTROL_RIGHT, + GHOSTTY_KEY_ENTER, + GHOSTTY_KEY_META_LEFT, + GHOSTTY_KEY_META_RIGHT, + GHOSTTY_KEY_SHIFT_LEFT, + GHOSTTY_KEY_SHIFT_RIGHT, + GHOSTTY_KEY_SPACE, + GHOSTTY_KEY_TAB, + GHOSTTY_KEY_CONVERT, + GHOSTTY_KEY_KANA_MODE, + GHOSTTY_KEY_NON_CONVERT, + + // Control Pad Section (W3C § 3.2) + GHOSTTY_KEY_DELETE, + GHOSTTY_KEY_END, + GHOSTTY_KEY_HELP, + GHOSTTY_KEY_HOME, + GHOSTTY_KEY_INSERT, + GHOSTTY_KEY_PAGE_DOWN, + GHOSTTY_KEY_PAGE_UP, + + // Arrow Pad Section (W3C § 3.3) + GHOSTTY_KEY_ARROW_DOWN, + GHOSTTY_KEY_ARROW_LEFT, + GHOSTTY_KEY_ARROW_RIGHT, + GHOSTTY_KEY_ARROW_UP, + + // Numpad Section (W3C § 3.4) + GHOSTTY_KEY_NUM_LOCK, + GHOSTTY_KEY_NUMPAD_0, + GHOSTTY_KEY_NUMPAD_1, + GHOSTTY_KEY_NUMPAD_2, + GHOSTTY_KEY_NUMPAD_3, + GHOSTTY_KEY_NUMPAD_4, + GHOSTTY_KEY_NUMPAD_5, + GHOSTTY_KEY_NUMPAD_6, + GHOSTTY_KEY_NUMPAD_7, + GHOSTTY_KEY_NUMPAD_8, + GHOSTTY_KEY_NUMPAD_9, + GHOSTTY_KEY_NUMPAD_ADD, + GHOSTTY_KEY_NUMPAD_BACKSPACE, + GHOSTTY_KEY_NUMPAD_CLEAR, + GHOSTTY_KEY_NUMPAD_CLEAR_ENTRY, + GHOSTTY_KEY_NUMPAD_COMMA, + GHOSTTY_KEY_NUMPAD_DECIMAL, + GHOSTTY_KEY_NUMPAD_DIVIDE, + GHOSTTY_KEY_NUMPAD_ENTER, + GHOSTTY_KEY_NUMPAD_EQUAL, + GHOSTTY_KEY_NUMPAD_MEMORY_ADD, + GHOSTTY_KEY_NUMPAD_MEMORY_CLEAR, + GHOSTTY_KEY_NUMPAD_MEMORY_RECALL, + GHOSTTY_KEY_NUMPAD_MEMORY_STORE, + GHOSTTY_KEY_NUMPAD_MEMORY_SUBTRACT, + GHOSTTY_KEY_NUMPAD_MULTIPLY, + GHOSTTY_KEY_NUMPAD_PAREN_LEFT, + GHOSTTY_KEY_NUMPAD_PAREN_RIGHT, + GHOSTTY_KEY_NUMPAD_SUBTRACT, + GHOSTTY_KEY_NUMPAD_SEPARATOR, + GHOSTTY_KEY_NUMPAD_UP, + GHOSTTY_KEY_NUMPAD_DOWN, + GHOSTTY_KEY_NUMPAD_RIGHT, + GHOSTTY_KEY_NUMPAD_LEFT, + GHOSTTY_KEY_NUMPAD_BEGIN, + GHOSTTY_KEY_NUMPAD_HOME, + GHOSTTY_KEY_NUMPAD_END, + GHOSTTY_KEY_NUMPAD_INSERT, + GHOSTTY_KEY_NUMPAD_DELETE, + GHOSTTY_KEY_NUMPAD_PAGE_UP, + GHOSTTY_KEY_NUMPAD_PAGE_DOWN, + + // Function Section (W3C § 3.5) + GHOSTTY_KEY_ESCAPE, + GHOSTTY_KEY_F1, + GHOSTTY_KEY_F2, + GHOSTTY_KEY_F3, + GHOSTTY_KEY_F4, + GHOSTTY_KEY_F5, + GHOSTTY_KEY_F6, + GHOSTTY_KEY_F7, + GHOSTTY_KEY_F8, + GHOSTTY_KEY_F9, + GHOSTTY_KEY_F10, + GHOSTTY_KEY_F11, + GHOSTTY_KEY_F12, + GHOSTTY_KEY_F13, + GHOSTTY_KEY_F14, + GHOSTTY_KEY_F15, + GHOSTTY_KEY_F16, + GHOSTTY_KEY_F17, + GHOSTTY_KEY_F18, + GHOSTTY_KEY_F19, + GHOSTTY_KEY_F20, + GHOSTTY_KEY_F21, + GHOSTTY_KEY_F22, + GHOSTTY_KEY_F23, + GHOSTTY_KEY_F24, + GHOSTTY_KEY_F25, + GHOSTTY_KEY_FN, + GHOSTTY_KEY_FN_LOCK, + GHOSTTY_KEY_PRINT_SCREEN, + GHOSTTY_KEY_SCROLL_LOCK, + GHOSTTY_KEY_PAUSE, + + // Media Keys (W3C § 3.6) + GHOSTTY_KEY_BROWSER_BACK, + GHOSTTY_KEY_BROWSER_FAVORITES, + GHOSTTY_KEY_BROWSER_FORWARD, + GHOSTTY_KEY_BROWSER_HOME, + GHOSTTY_KEY_BROWSER_REFRESH, + GHOSTTY_KEY_BROWSER_SEARCH, + GHOSTTY_KEY_BROWSER_STOP, + GHOSTTY_KEY_EJECT, + GHOSTTY_KEY_LAUNCH_APP_1, + GHOSTTY_KEY_LAUNCH_APP_2, + GHOSTTY_KEY_LAUNCH_MAIL, + GHOSTTY_KEY_MEDIA_PLAY_PAUSE, + GHOSTTY_KEY_MEDIA_SELECT, + GHOSTTY_KEY_MEDIA_STOP, + GHOSTTY_KEY_MEDIA_TRACK_NEXT, + GHOSTTY_KEY_MEDIA_TRACK_PREVIOUS, + GHOSTTY_KEY_POWER, + GHOSTTY_KEY_SLEEP, + GHOSTTY_KEY_AUDIO_VOLUME_DOWN, + GHOSTTY_KEY_AUDIO_VOLUME_MUTE, + GHOSTTY_KEY_AUDIO_VOLUME_UP, + GHOSTTY_KEY_WAKE_UP, + + // Legacy, Non-standard, and Special Keys (W3C § 3.7) + GHOSTTY_KEY_COPY, + GHOSTTY_KEY_CUT, + GHOSTTY_KEY_PASTE, +} GhosttyKey; + +/** + * Create a new key event instance. + * + * Creates a new key event with default values. The event must be freed using + * ghostty_key_event_free() when no longer needed. + * + * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator + * @param event Pointer to store the created key event handle + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup key + */ +GhosttyResult ghostty_key_event_new(const GhosttyAllocator *allocator, GhosttyKeyEvent *event); + +/** + * Free a key event instance. + * + * Releases all resources associated with the key event. After this call, + * the event handle becomes invalid and must not be used. + * + * @param event The key event handle to free (may be NULL) + * + * @ingroup key + */ +void ghostty_key_event_free(GhosttyKeyEvent event); + +/** + * Set the key action (press, release, repeat). + * + * @param event The key event handle, must not be NULL + * @param action The action to set + * + * @ingroup key + */ +void ghostty_key_event_set_action(GhosttyKeyEvent event, GhosttyKeyAction action); + +/** + * Get the key action (press, release, repeat). + * + * @param event The key event handle, must not be NULL + * @return The key action + * + * @ingroup key + */ +GhosttyKeyAction ghostty_key_event_get_action(GhosttyKeyEvent event); + +/** + * Set the physical key code. + * + * @param event The key event handle, must not be NULL + * @param key The physical key code to set + * + * @ingroup key + */ +void ghostty_key_event_set_key(GhosttyKeyEvent event, GhosttyKey key); + +/** + * Get the physical key code. + * + * @param event The key event handle, must not be NULL + * @return The physical key code + * + * @ingroup key + */ +GhosttyKey ghostty_key_event_get_key(GhosttyKeyEvent event); + +/** + * Set the modifier keys bitmask. + * + * @param event The key event handle, must not be NULL + * @param mods The modifier keys bitmask to set + * + * @ingroup key + */ +void ghostty_key_event_set_mods(GhosttyKeyEvent event, GhosttyMods mods); + +/** + * Get the modifier keys bitmask. + * + * @param event The key event handle, must not be NULL + * @return The modifier keys bitmask + * + * @ingroup key + */ +GhosttyMods ghostty_key_event_get_mods(GhosttyKeyEvent event); + +/** + * Set the consumed modifiers bitmask. + * + * @param event The key event handle, must not be NULL + * @param consumed_mods The consumed modifiers bitmask to set + * + * @ingroup key + */ +void ghostty_key_event_set_consumed_mods(GhosttyKeyEvent event, GhosttyMods consumed_mods); + +/** + * Get the consumed modifiers bitmask. + * + * @param event The key event handle, must not be NULL + * @return The consumed modifiers bitmask + * + * @ingroup key + */ +GhosttyMods ghostty_key_event_get_consumed_mods(GhosttyKeyEvent event); + +/** + * Set whether the key event is part of a composition sequence. + * + * @param event The key event handle, must not be NULL + * @param composing Whether the key event is part of a composition sequence + * + * @ingroup key + */ +void ghostty_key_event_set_composing(GhosttyKeyEvent event, bool composing); + +/** + * Get whether the key event is part of a composition sequence. + * + * @param event The key event handle, must not be NULL + * @return Whether the key event is part of a composition sequence + * + * @ingroup key + */ +bool ghostty_key_event_get_composing(GhosttyKeyEvent event); + +/** + * Set the UTF-8 text generated by the key event. + * + * The key event does NOT take ownership of the text pointer. The caller + * must ensure the string remains valid for the lifetime needed by the event. + * + * @param event The key event handle, must not be NULL + * @param utf8 The UTF-8 text to set (or NULL for empty) + * @param len Length of the UTF-8 text in bytes + * + * @ingroup key + */ +void ghostty_key_event_set_utf8(GhosttyKeyEvent event, const char *utf8, size_t len); + +/** + * Get the UTF-8 text generated by the key event. + * + * The returned pointer is valid until the event is freed or the UTF-8 text is modified. + * + * @param event The key event handle, must not be NULL + * @param len Pointer to store the length of the UTF-8 text in bytes (may be NULL) + * @return The UTF-8 text (or NULL for empty) + * + * @ingroup key + */ +const char *ghostty_key_event_get_utf8(GhosttyKeyEvent event, size_t *len); + +/** + * Set the unshifted Unicode codepoint. + * + * @param event The key event handle, must not be NULL + * @param codepoint The unshifted Unicode codepoint to set + * + * @ingroup key + */ +void ghostty_key_event_set_unshifted_codepoint(GhosttyKeyEvent event, uint32_t codepoint); + +/** + * Get the unshifted Unicode codepoint. + * + * @param event The key event handle, must not be NULL + * @return The unshifted Unicode codepoint + * + * @ingroup key + */ +uint32_t ghostty_key_event_get_unshifted_codepoint(GhosttyKeyEvent event); + +#endif /* GHOSTTY_VT_KEY_EVENT_H */ diff --git a/include/ghostty/vt/osc.h b/include/ghostty/vt/osc.h new file mode 100644 index 000000000..7e2c8f322 --- /dev/null +++ b/include/ghostty/vt/osc.h @@ -0,0 +1,231 @@ +/** + * @file osc.h + * + * OSC (Operating System Command) sequence parser and command handling. + */ + +#ifndef GHOSTTY_VT_OSC_H +#define GHOSTTY_VT_OSC_H + +#include +#include +#include +#include +#include + +/** + * Opaque handle to an OSC parser instance. + * + * This handle represents an OSC (Operating System Command) parser that can + * be used to parse the contents of OSC sequences. + * + * @ingroup osc + */ +typedef struct GhosttyOscParser *GhosttyOscParser; + +/** + * Opaque handle to a single OSC command. + * + * This handle represents a parsed OSC (Operating System Command) command. + * The command can be queried for its type and associated data. + * + * @ingroup osc + */ +typedef struct GhosttyOscCommand *GhosttyOscCommand; + +/** @defgroup osc OSC Parser + * + * OSC (Operating System Command) sequence parser and command handling. + * + * The parser operates in a streaming fashion, processing input byte-by-byte + * to handle OSC sequences that may arrive in fragments across multiple reads. + * This interface makes it easy to integrate into most environments and avoids + * over-allocating buffers. + * + * ## Basic Usage + * + * 1. Create a parser instance with ghostty_osc_new() + * 2. Feed bytes to the parser using ghostty_osc_next() + * 3. Finalize parsing with ghostty_osc_end() to get the command + * 4. Query command type and extract data using ghostty_osc_command_type() + * and ghostty_osc_command_data() + * 5. Free the parser with ghostty_osc_free() when done + * + * @{ + */ + +/** + * OSC command types. + * + * @ingroup osc + */ +typedef enum { + GHOSTTY_OSC_COMMAND_INVALID = 0, + GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_TITLE = 1, + GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_ICON = 2, + GHOSTTY_OSC_COMMAND_PROMPT_START = 3, + GHOSTTY_OSC_COMMAND_PROMPT_END = 4, + GHOSTTY_OSC_COMMAND_END_OF_INPUT = 5, + GHOSTTY_OSC_COMMAND_END_OF_COMMAND = 6, + GHOSTTY_OSC_COMMAND_CLIPBOARD_CONTENTS = 7, + GHOSTTY_OSC_COMMAND_REPORT_PWD = 8, + GHOSTTY_OSC_COMMAND_MOUSE_SHAPE = 9, + GHOSTTY_OSC_COMMAND_COLOR_OPERATION = 10, + GHOSTTY_OSC_COMMAND_KITTY_COLOR_PROTOCOL = 11, + GHOSTTY_OSC_COMMAND_SHOW_DESKTOP_NOTIFICATION = 12, + GHOSTTY_OSC_COMMAND_HYPERLINK_START = 13, + GHOSTTY_OSC_COMMAND_HYPERLINK_END = 14, + GHOSTTY_OSC_COMMAND_CONEMU_SLEEP = 15, + GHOSTTY_OSC_COMMAND_CONEMU_SHOW_MESSAGE_BOX = 16, + GHOSTTY_OSC_COMMAND_CONEMU_CHANGE_TAB_TITLE = 17, + GHOSTTY_OSC_COMMAND_CONEMU_PROGRESS_REPORT = 18, + GHOSTTY_OSC_COMMAND_CONEMU_WAIT_INPUT = 19, + GHOSTTY_OSC_COMMAND_CONEMU_GUIMACRO = 20, +} GhosttyOscCommandType; + +/** + * OSC command data types. + * + * These values specify what type of data to extract from an OSC command + * using `ghostty_osc_command_data`. + * + * @ingroup osc + */ +typedef enum { + /** Invalid data type. Never results in any data extraction. */ + GHOSTTY_OSC_DATA_INVALID = 0, + + /** + * Window title string data. + * + * Valid for: GHOSTTY_OSC_COMMAND_CHANGE_WINDOW_TITLE + * + * Output type: const char ** (pointer to null-terminated string) + * + * Lifetime: Valid until the next call to any ghostty_osc_* function with + * the same parser instance. Memory is owned by the parser. + */ + GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR = 1, +} GhosttyOscCommandData; + +/** + * Create a new OSC parser instance. + * + * Creates a new OSC (Operating System Command) parser using the provided + * allocator. The parser must be freed using ghostty_vt_osc_free() when + * no longer needed. + * + * @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator + * @param parser Pointer to store the created parser handle + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup osc + */ +GhosttyResult ghostty_osc_new(const GhosttyAllocator *allocator, GhosttyOscParser *parser); + +/** + * Free an OSC parser instance. + * + * Releases all resources associated with the OSC parser. After this call, + * the parser handle becomes invalid and must not be used. + * + * @param parser The parser handle to free (may be NULL) + * + * @ingroup osc + */ +void ghostty_osc_free(GhosttyOscParser parser); + +/** + * Reset an OSC parser instance to its initial state. + * + * Resets the parser state, clearing any partially parsed OSC sequences + * and returning the parser to its initial state. This is useful for + * reusing a parser instance or recovering from parse errors. + * + * @param parser The parser handle to reset, must not be null. + * + * @ingroup osc + */ +void ghostty_osc_reset(GhosttyOscParser parser); + +/** + * Parse the next byte in an OSC sequence. + * + * Processes a single byte as part of an OSC sequence. The parser maintains + * internal state to track the progress through the sequence. Call this + * function for each byte in the sequence data. + * + * When finished pumping the parser with bytes, call ghostty_osc_end + * to get the final result. + * + * @param parser The parser handle, must not be null. + * @param byte The next byte to parse + * + * @ingroup osc + */ +void ghostty_osc_next(GhosttyOscParser parser, uint8_t byte); + +/** + * Finalize OSC parsing and retrieve the parsed command. + * + * Call this function after feeding all bytes of an OSC sequence to the parser + * using ghostty_osc_next() with the exception of the terminating character + * (ESC or ST). This function finalizes the parsing process and returns the + * parsed OSC command. + * + * The return value is never NULL. Invalid commands will return a command + * with type GHOSTTY_OSC_COMMAND_INVALID. + * + * The terminator parameter specifies the byte that terminated the OSC sequence + * (typically 0x07 for BEL or 0x5C for ST after ESC). This information is + * preserved in the parsed command so that responses can use the same terminator + * format for better compatibility with the calling program. For commands that + * do not require a response, this parameter is ignored and the resulting + * command will not retain the terminator information. + * + * The returned command handle is valid until the next call to any + * `ghostty_osc_*` function with the same parser instance with the exception + * of command introspection functions such as `ghostty_osc_command_type`. + * + * @param parser The parser handle, must not be null. + * @param terminator The terminating byte of the OSC sequence (0x07 for BEL, 0x5C for ST) + * @return Handle to the parsed OSC command + * + * @ingroup osc + */ +GhosttyOscCommand ghostty_osc_end(GhosttyOscParser parser, uint8_t terminator); + +/** + * Get the type of an OSC command. + * + * Returns the type identifier for the given OSC command. This can be used + * to determine what kind of command was parsed and what data might be + * available from it. + * + * @param command The OSC command handle to query (may be NULL) + * @return The command type, or GHOSTTY_OSC_COMMAND_INVALID if command is NULL + * + * @ingroup osc + */ +GhosttyOscCommandType ghostty_osc_command_type(GhosttyOscCommand command); + +/** + * Extract data from an OSC command. + * + * Extracts typed data from the given OSC command based on the specified + * data type. The output pointer must be of the appropriate type for the + * requested data kind. Valid command types, output types, and memory + * safety information are documented in the `GhosttyOscCommandData` enum. + * + * @param command The OSC command handle to query (may be NULL) + * @param data The type of data to extract + * @param out Pointer to store the extracted data (type depends on data parameter) + * @return true if data extraction was successful, false otherwise + * + * @ingroup osc + */ +bool ghostty_osc_command_data(GhosttyOscCommand command, GhosttyOscCommandData data, void *out); + +/** @} */ + +#endif /* GHOSTTY_VT_OSC_H */ diff --git a/include/ghostty/vt/result.h b/include/ghostty/vt/result.h new file mode 100644 index 000000000..cc382eade --- /dev/null +++ b/include/ghostty/vt/result.h @@ -0,0 +1,20 @@ +/** + * @file result.h + * + * Result codes for libghostty-vt operations. + */ + +#ifndef GHOSTTY_VT_RESULT_H +#define GHOSTTY_VT_RESULT_H + +/** + * Result codes for libghostty-vt operations. + */ +typedef enum { + /** Operation completed successfully */ + GHOSTTY_SUCCESS = 0, + /** Operation failed due to failed allocation */ + GHOSTTY_OUT_OF_MEMORY = -1, +} GhosttyResult; + +#endif /* GHOSTTY_VT_RESULT_H */ diff --git a/src/build/GhosttyLibVt.zig b/src/build/GhosttyLibVt.zig index 1e57da7b1..590792ef3 100644 --- a/src/build/GhosttyLibVt.zig +++ b/src/build/GhosttyLibVt.zig @@ -30,9 +30,10 @@ pub fn initShared( .root_module = zig.vt_c, .version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 }, }); - lib.installHeader( - b.path("include/ghostty/vt.h"), - "ghostty/vt.h", + lib.installHeadersDirectory( + b.path("include/ghostty"), + "ghostty", + .{ .include_extensions = &.{".h"} }, ); // Get our debug symbols