diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-10 21:51:57 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-10 21:51:57 +0300 |
| commit | 425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch) | |
| tree | e52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/include | |
| parent | 90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff) | |
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/include')
23 files changed, 2935 insertions, 346 deletions
diff --git a/vendor/libghostty-vt/include/ghostty.h b/vendor/libghostty-vt/include/ghostty.h index 72bbb57a..518af3ff 100644 --- a/vendor/libghostty-vt/include/ghostty.h +++ b/vendor/libghostty-vt/include/ghostty.h @@ -1,10 +1,14 @@ -// Ghostty embedding API. The documentation for the embedding API is -// only within the Zig source files that define the implementations. This -// isn't meant to be a general purpose embedding API (yet) so there hasn't -// been documentation or example work beyond that. +// Ghostty's internal embedder API, a.k.a. "libghostty-internal". // -// The only consumer of this API is the macOS app, but the API is built to -// be more general purpose. +// The only consumer of this API is the macOS app, and while it is fairly +// comprehensive, it is tailored to the needs of the macOS app and not designed +// for external use, hence why most functions are undocumented and some are +// macOS-specific (e.g. ones dealing with the Metal graphics API). +// +// External embedders should instead use `libghostty-vt` or other related +// packages, which are extensively documented and designed from the ground up +// to be used in other software. Header files for which can be found in +// `include/ghostty/`. #ifndef GHOSTTY_H #define GHOSTTY_H @@ -71,19 +75,56 @@ typedef enum { typedef enum { GHOSTTY_CLIPBOARD_STANDARD, GHOSTTY_CLIPBOARD_SELECTION, + GHOSTTY_CLIPBOARD_PRIMARY, } ghostty_clipboard_e; +// One representation of clipboard contents. The data is binary-safe with +// an explicit length; it is not necessarily null-terminated. typedef struct { const char *mime; const char *data; + size_t len; } ghostty_clipboard_content_s; +// The payload for completing a clipboard read request. See +// ghostty_surface_complete_clipboard_request. +typedef struct { + const ghostty_clipboard_content_s *contents; + size_t contents_len; + const char *const *available; + size_t available_len; + bool confirmed; + bool remember; +} ghostty_clipboard_complete_s; + +// The payload of a clipboard read confirmation request: the would-be +// completion contents plus the information shown in the permission +// prompt. See ghostty_runtime_confirm_read_clipboard_cb. +typedef struct { + const ghostty_clipboard_content_s *contents; + size_t contents_len; + const char *const *available; + size_t available_len; + const char *name; + bool can_remember; +} ghostty_clipboard_confirm_s; + typedef enum { GHOSTTY_CLIPBOARD_REQUEST_PASTE, GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ, GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE, + GHOSTTY_CLIPBOARD_REQUEST_KITTY_READ, + GHOSTTY_CLIPBOARD_REQUEST_KITTY_WRITE, + GHOSTTY_CLIPBOARD_REQUEST_LIST, } ghostty_clipboard_request_e; +// apprt.ClipboardReadResult +typedef enum { + GHOSTTY_CLIPBOARD_READ_STARTED, + GHOSTTY_CLIPBOARD_READ_UNAVAILABLE, + GHOSTTY_CLIPBOARD_READ_UNSUPPORTED, +} ghostty_clipboard_read_result_e; + typedef enum { GHOSTTY_MOUSE_RELEASE, GHOSTTY_MOUSE_PRESS, @@ -645,6 +686,12 @@ typedef enum { GHOSTTY_INSPECTOR_HIDE, } ghostty_action_inspector_e; +// apprt.action.ExportTerminalIO.C +typedef struct { + const char* contents; + size_t len; +} ghostty_action_export_terminal_io_s; + // apprt.action.QuitTimer typedef enum { GHOSTTY_QUIT_TIMER_START, @@ -672,6 +719,7 @@ typedef struct { typedef enum { GHOSTTY_PROMPT_TITLE_SURFACE, GHOSTTY_PROMPT_TITLE_TAB, + GHOSTTY_PROMPT_TITLE_WINDOW, } ghostty_action_prompt_title_e; // apprt.action.Pwd.C @@ -679,6 +727,14 @@ typedef struct { const char* pwd; } ghostty_action_pwd_s; +// apprt.action.OpenConfig +typedef enum { + // Open the config in the OS default editor. + GHOSTTY_ACTION_OPEN_CONFIG_OS_OPEN, + // Open the config in a new window using $EDITOR or $VISUAL + GHOSTTY_ACTION_OPEN_CONFIG_NEW_WINDOW, +} ghostty_action_open_config_e; + // terminal.MouseShape typedef enum { GHOSTTY_MOUSE_SHAPE_DEFAULT, @@ -812,6 +868,7 @@ typedef enum { GHOSTTY_ACTION_OPEN_URL_KIND_UNKNOWN, GHOSTTY_ACTION_OPEN_URL_KIND_TEXT, GHOSTTY_ACTION_OPEN_URL_KIND_HTML, + GHOSTTY_ACTION_OPEN_URL_KIND_OSC8, } ghostty_action_open_url_kind_e; // apprt.action.OpenUrl.C @@ -914,9 +971,11 @@ typedef enum { GHOSTTY_ACTION_INSPECTOR, GHOSTTY_ACTION_SHOW_GTK_INSPECTOR, GHOSTTY_ACTION_RENDER_INSPECTOR, + GHOSTTY_ACTION_EXPORT_TERMINAL_IO, GHOSTTY_ACTION_DESKTOP_NOTIFICATION, GHOSTTY_ACTION_SET_TITLE, GHOSTTY_ACTION_SET_TAB_TITLE, + GHOSTTY_ACTION_SET_WINDOW_TITLE, GHOSTTY_ACTION_PROMPT_TITLE, GHOSTTY_ACTION_PWD, GHOSTTY_ACTION_MOUSE_SHAPE, @@ -949,6 +1008,7 @@ typedef enum { GHOSTTY_ACTION_SEARCH_SELECTED, GHOSTTY_ACTION_READONLY, GHOSTTY_ACTION_COPY_TITLE_TO_CLIPBOARD, + GHOSTTY_ACTION_MOVE_TAB_TO_NEW_WINDOW, } ghostty_action_tag_e; typedef union { @@ -964,6 +1024,7 @@ typedef union { ghostty_action_cell_size_s cell_size; ghostty_action_scrollbar_s scrollbar; ghostty_action_inspector_e inspector; + ghostty_action_export_terminal_io_s export_terminal_io; ghostty_action_desktop_notification_s desktop_notification; ghostty_action_set_title_s set_title; ghostty_action_set_title_s set_tab_title; @@ -990,6 +1051,7 @@ typedef union { ghostty_action_search_total_s search_total; ghostty_action_search_selected_s search_selected; ghostty_action_readonly_e readonly; + ghostty_action_open_config_e open_config; } ghostty_action_u; typedef struct { @@ -998,12 +1060,16 @@ typedef struct { } ghostty_action_s; typedef void (*ghostty_runtime_wakeup_cb)(void*); -typedef bool (*ghostty_runtime_read_clipboard_cb)(void*, - ghostty_clipboard_e, - void*); +typedef ghostty_clipboard_read_result_e (*ghostty_runtime_read_clipboard_cb)( + void*, + ghostty_clipboard_e, + void*, + const char* const*, + size_t, + bool); typedef void (*ghostty_runtime_confirm_read_clipboard_cb)( void*, - const char*, + const ghostty_clipboard_confirm_s*, void*, ghostty_clipboard_request_e); typedef void (*ghostty_runtime_write_clipboard_cb)(void*, @@ -1055,6 +1121,7 @@ typedef union { // apprt.ipc.Action.Key typedef enum { GHOSTTY_IPC_ACTION_NEW_WINDOW, + GHOSTTY_IPC_ACTION_NEW_TAB, GHOSTTY_IPC_ACTION_TOGGLE_QUICK_TERMINAL, } ghostty_ipc_action_tag_e; @@ -1152,10 +1219,12 @@ GHOSTTY_API void ghostty_surface_split_resize(ghostty_surface_t, uint16_t); GHOSTTY_API void ghostty_surface_split_equalize(ghostty_surface_t); GHOSTTY_API bool ghostty_surface_binding_action(ghostty_surface_t, const char*, uintptr_t); -GHOSTTY_API void ghostty_surface_complete_clipboard_request(ghostty_surface_t, - const char*, - void*, - bool); +GHOSTTY_API void ghostty_surface_complete_clipboard_request( + ghostty_surface_t, + const ghostty_clipboard_complete_s*, + void*); +GHOSTTY_API void ghostty_surface_deny_clipboard_request(ghostty_surface_t, + void*); GHOSTTY_API bool ghostty_surface_has_selection(ghostty_surface_t); GHOSTTY_API bool ghostty_surface_read_selection(ghostty_surface_t, ghostty_text_s*); GHOSTTY_API bool ghostty_surface_read_text(ghostty_surface_t, diff --git a/vendor/libghostty-vt/include/ghostty/vt.h b/vendor/libghostty-vt/include/ghostty/vt.h index 5606f169..cfa1017e 100644 --- a/vendor/libghostty-vt/include/ghostty/vt.h +++ b/vendor/libghostty-vt/include/ghostty/vt.h @@ -31,12 +31,15 @@ * - @ref terminal "Terminal" - Complete terminal emulator state and rendering * - @ref render "Render State" - Incremental render state updates for custom renderers * - @ref formatter "Formatter" - Format terminal content as plain text, VT sequences, or HTML + * - @ref snapshot "Terminal Snapshot" - Encode and incrementally restore terminal state + * - @ref search "Search" - Search terminal contents, including scrollback * - @ref osc "OSC Parser" - Parse OSC (Operating System Command) sequences * - @ref sgr "SGR Parser" - Parse SGR (Select Graphic Rendition) sequences - * - @ref paste "Paste Utilities" - Validate paste data safety + * - @ref paste "Paste" - Paste into a terminal, validate and encode paste data * - @ref unicode "Unicode Utilities" - Codepoint properties for text layout * - @ref build_info "Build Info" - Query compile-time build configuration * - @ref allocator "Memory Management" - Memory management and custom allocators + * - @ref io "Byte-stream I/O" - Reusable synchronous reader and writer callbacks * - @ref wasm "WebAssembly Utilities" - WebAssembly convenience functions * * Encoding related APIs: @@ -51,12 +54,13 @@ * - @ref c-vt/src/main.c - OSC parser example * - @ref c-vt-encode-key/src/main.c - Key encoding example * - @ref c-vt-encode-mouse/src/main.c - Mouse encoding example - * - @ref c-vt-paste/src/main.c - Paste safety check example + * - @ref c-vt-paste/src/main.c - Paste example * - @ref c-vt-sgr/src/main.c - SGR parser example * - @ref c-vt-formatter/src/main.c - Terminal formatter example * - @ref c-vt-grid-traverse/src/main.c - Grid traversal example using grid refs * - @ref c-vt-grid-ref-tracked/src/main.c - Tracked grid ref example * - @ref c-vt-compression/src/main.c - Idle scrollback compression example + * - @ref c-vt-search/src/main.c - Terminal search example * */ @@ -81,8 +85,10 @@ */ /** @example c-vt-paste/src/main.c - * This example demonstrates how to use the paste utilities to check if - * paste data is safe before sending it to the terminal. + * This example demonstrates how to paste into a terminal, including the + * unsafe-paste confirmation flow and Kitty clipboard protocol paste events + * (mode 5522), as well as the terminal-free paste safety and encoding + * utilities. */ /** @example c-vt-sgr/src/main.c @@ -121,6 +127,12 @@ * PNG decoder callback and send a Kitty Graphics Protocol image. */ +/** @example c-vt-search/src/main.c + * This example demonstrates how to search terminal contents for a + * string, navigate between the matches like a find bar, and read the + * viewport matches used to draw highlights. + */ + #ifndef GHOSTTY_VT_H #define GHOSTTY_VT_H @@ -140,6 +152,7 @@ extern "C" { #include <ghostty/vt/terminal.h> #include <ghostty/vt/grid_ref.h> #include <ghostty/vt/grid_ref_tracked.h> +#include <ghostty/vt/io.h> #include <ghostty/vt/osc.h> #include <ghostty/vt/sgr.h> #include <ghostty/vt/style.h> @@ -151,8 +164,10 @@ extern "C" { #include <ghostty/vt/paste.h> #include <ghostty/vt/point.h> #include <ghostty/vt/screen.h> +#include <ghostty/vt/search.h> #include <ghostty/vt/selection.h> #include <ghostty/vt/size_report.h> +#include <ghostty/vt/snapshot.h> #include <ghostty/vt/unicode.h> #include <ghostty/vt/wasm.h> diff --git a/vendor/libghostty-vt/include/ghostty/vt/allocator.h b/vendor/libghostty-vt/include/ghostty/vt/allocator.h index 2e8685e8..f68ffa2f 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/allocator.h +++ b/vendor/libghostty-vt/include/ghostty/vt/allocator.h @@ -31,14 +31,17 @@ * 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. + * that doesn't require any external dependencies. On native freestanding + * targets, the default allocator always fails instead. * * ## 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). + * we provide our own default allocator if libc isn't linked). Native + * freestanding builds must provide a custom allocator for operations that + * allocate memory. * * To use a custom allocator: * 1. Implement the GhosttyAllocatorVtable function pointers @@ -76,7 +79,8 @@ * * 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. + * default allocator. Native freestanding builds must provide an allocator + * for operations that allocate memory. * * 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 @@ -183,7 +187,8 @@ typedef struct { * 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). + * a custom allocator is used (currently Zig's SMP allocator). On native + * freestanding targets, the default allocator always fails instead. * * @ingroup allocator * @@ -219,7 +224,8 @@ typedef struct GhosttyAllocator { * * @param allocator Pointer to the allocator to use, or NULL for the default * @param len Number of bytes to allocate - * @return Pointer to the allocated buffer, or NULL if allocation failed + * @return Pointer to the allocated buffer, or NULL if len is zero or + * allocation failed * * @ingroup allocator */ diff --git a/vendor/libghostty-vt/include/ghostty/vt/device.h b/vendor/libghostty-vt/include/ghostty/vt/device.h index 0a156728..419748a8 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/device.h +++ b/vendor/libghostty-vt/include/ghostty/vt/device.h @@ -11,6 +11,8 @@ #include <stddef.h> #include <stdint.h> +#include <ghostty/vt/types.h> + /* DA1 conformance levels (Pp parameter). */ #define GHOSTTY_DA_CONFORMANCE_VT100 1 #define GHOSTTY_DA_CONFORMANCE_VT101 1 diff --git a/vendor/libghostty-vt/include/ghostty/vt/formatter.h b/vendor/libghostty-vt/include/ghostty/vt/formatter.h index 5cdcd11a..fd593b13 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/formatter.h +++ b/vendor/libghostty-vt/include/ghostty/vt/formatter.h @@ -11,6 +11,7 @@ #include <stddef.h> #include <stdint.h> #include <ghostty/vt/allocator.h> +#include <ghostty/vt/io.h> #include <ghostty/vt/selection.h> #include <ghostty/vt/types.h> #include <ghostty/vt/terminal.h> @@ -138,6 +139,30 @@ GHOSTTY_API GhosttyResult ghostty_formatter_terminal_new( GhosttyFormatterTerminalOptions options); /** + * Run the formatter and stream output to a writer. + * + * Each call formats the current terminal state and invokes the writer + * synchronously as output becomes available. The callback may be called more + * than once and must not call formatter or terminal APIs using the same + * formatter or its terminal. + * + * If an error occurs, the writer may already contain a partial formatted + * output. The operation cannot be resumed from that partial output. This + * function does not flush or make the caller's destination durable. + * + * @param formatter The formatter handle (must not be NULL) + * @param writer Destination writer whose write callback must not be NULL + * @return GHOSTTY_SUCCESS on success, GHOSTTY_IO_ERROR if the writer rejects + * output, GHOSTTY_LIMIT_EXCEEDED if output accounting overflows, or + * GHOSTTY_INVALID_VALUE if an argument is invalid + * + * @ingroup formatter + */ +GHOSTTY_API GhosttyResult ghostty_formatter_format( + GhosttyFormatter formatter, + GhosttyWriter writer); + +/** * Run the formatter and produce output into the caller-provided buffer. * * Each call formats the current terminal state. Pass NULL for buf to @@ -171,6 +196,8 @@ GHOSTTY_API GhosttyResult ghostty_formatter_format_buf(GhosttyFormatter formatte * The caller is responsible for freeing the returned buffer with * ghostty_free(), passing the same allocator (or NULL for the default) * that was used for the allocation. + * Empty output returns GHOSTTY_SUCCESS with *out_ptr set to NULL and + * *out_len set to zero. This result can be passed to ghostty_free(). * * @param formatter The formatter handle (must not be NULL) * @param allocator Pointer to allocator, or NULL to use the default allocator diff --git a/vendor/libghostty-vt/include/ghostty/vt/io.h b/vendor/libghostty-vt/include/ghostty/vt/io.h new file mode 100644 index 00000000..ad2c57ad --- /dev/null +++ b/vendor/libghostty-vt/include/ghostty/vt/io.h @@ -0,0 +1,151 @@ +/** + * @file io.h + * + * Generic IO callbacks for libghostty-vt. + */ + +#ifndef GHOSTTY_VT_IO_H +#define GHOSTTY_VT_IO_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <ghostty/vt/types.h> + +/** @defgroup io I/O + * + * Synchronous callback interfaces used by APIs that consume or produce byte + * streams. The callback and userdata pointers must remain valid for the full + * lifetime documented by the API receiving a GhosttyReader or GhosttyWriter. + * + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Read bytes from a source. + * + * The callback must set @p out_read to a value no greater than @p capacity + * when returning true. A positive value reports progress; it may be less than + * capacity and does not indicate end-of-file. A zero value is definitive + * end-of-file. It must not be used to report temporary input starvation or a + * would-block condition. + * + * Returning false reports a fatal read error and the value of @p out_read is + * ignored. The library does not inspect or modify errno. + * + * All pointer arguments are borrowed and valid only for the duration of the + * callback. The callback is invoked synchronously on the calling thread. + * + * @param userdata Opaque userdata from GhosttyReader + * @param buffer Destination for read bytes; always non-NULL + * @param capacity Writable capacity of @p buffer; always greater than zero + * @param[out] out_read Number of bytes read when returning true; non-NULL + * @return true for a successful read or end-of-file, false for a fatal error + */ +typedef bool (*GhosttyReaderFn)( + void* userdata, + uint8_t* buffer, + size_t capacity, + size_t* out_read); + +/** + * Write bytes to a destination. + * + * Returning true means all @p len bytes were accepted. Returning false + * reports a fatal write error. A callback wrapping an interface that permits + * partial writes must retry internally until the full slice is accepted or + * an error occurs. + * + * On failure, the destination may already contain a prefix of the bytes. The + * calling operation fails and must not be resumed from that partial output. + * The library does not inspect or modify errno. + * + * @p data is borrowed and valid only for the duration of the callback. The + * callback is invoked synchronously on the calling thread. Successful return + * means the bytes were handed to the destination; it does not imply that the + * destination was flushed or made durable. + * + * @param userdata Opaque userdata from GhosttyWriter + * @param data Source bytes; always non-NULL + * @param len Number of source bytes; always greater than zero + * @return true if the complete slice was accepted, false on fatal error + */ +typedef bool (*GhosttyWriterFn)( + void* userdata, + const uint8_t* data, + size_t len); + +/** + * A byte source callback and its opaque context. + * + * The struct is passed by value. @p read must be non-NULL. + */ +typedef struct { + GhosttyReaderFn read; + void* userdata; +} GhosttyReader; + +/** + * A byte destination callback and its opaque context. + * + * The struct is passed by value. @p write must be non-NULL. + */ +typedef struct { + GhosttyWriterFn write; + void* userdata; +} GhosttyWriter; + +/** + * Read one MIME-typed representation of some content, streaming its + * bytes to a writer. + * + * The library calls this with the MIME type of the representation it + * needs. The callback writes all of that representation's data to + * @p writer, in as many calls to `writer.write(writer.userdata, data, + * len)` as is convenient (one call with everything or many small + * pieces both work), and returns true. Nothing written is retained + * beyond each write call, so the data may be borrowed from anywhere: + * a pasteboard item, a file being read, a stream. + * + * Returning false reports that the data could not be read. If the + * writer refuses a write (returns false), stop and return false + * without writing more. + * + * All pointer arguments, the mime, and the writer are borrowed and + * valid only for the duration of the callback. The callback is + * invoked synchronously on the calling thread. The API receiving the + * GhosttyMimeReader defines which MIME types are requested, how many + * times, and any consistency requirements across repeated reads. + * + * @param userdata Opaque userdata from GhosttyMimeReader + * @param mime The MIME type of the representation to read + * @param writer Where to write the data; valid only during this call + * @return true once all the data was written, false if it could not + * be read or the writer refused a write + */ +typedef bool (*GhosttyMimeReaderFn)( + void* userdata, + GhosttyString mime, + GhosttyWriter writer); + +/** + * A MIME-typed content source callback and its opaque context. + * + * The struct is passed by value. @p read must be non-NULL. + */ +typedef struct { + GhosttyMimeReaderFn read; + void* userdata; +} GhosttyMimeReader; + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* GHOSTTY_VT_IO_H */ diff --git a/vendor/libghostty-vt/include/ghostty/vt/key.h b/vendor/libghostty-vt/include/ghostty/vt/key.h index 61b95475..517d0c30 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/key.h +++ b/vendor/libghostty-vt/include/ghostty/vt/key.h @@ -42,8 +42,7 @@ * @code{.c} * // Create a terminal and feed it some VT data that changes modes * GhosttyTerminal terminal; - * ghostty_terminal_new(NULL, &terminal, - * (GhosttyTerminalOptions){.cols = 80, .rows = 24, .max_scrollback = 0}); + * ghostty_terminal_new(NULL, &terminal, 80, 24); * * // Application might write data that enables Kitty keyboard protocol, etc. * ghostty_terminal_vt_write(terminal, vt_data, vt_len); diff --git a/vendor/libghostty-vt/include/ghostty/vt/kitty_graphics.h b/vendor/libghostty-vt/include/ghostty/vt/kitty_graphics.h index dda8b836..99cee60a 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/kitty_graphics.h +++ b/vendor/libghostty-vt/include/ghostty/vt/kitty_graphics.h @@ -390,7 +390,8 @@ typedef enum GHOSTTY_ENUM_TYPED { /** * Borrowed pointer to the raw pixel data. Valid as long as the - * underlying terminal is not mutated. + * underlying terminal is not mutated. Returns GHOSTTY_NO_VALUE when + * the image metadata is resident but its pixel payload is pending. * * The data is always fully decoded, uncompressed pixels in the * format reported by GHOSTTY_KITTY_IMAGE_DATA_FORMAT: zlib payloads @@ -398,13 +399,21 @@ typedef enum GHOSTTY_ENUM_TYPED { * time, before the image is stored. Consumers can upload this * directly to the GPU without any decode step. * + * For an animated image (Kitty graphics animation, actions a=f/a=a) + * this is the pixel data of the current animation frame. The + * image's GHOSTTY_KITTY_IMAGE_DATA_GENERATION changes whenever the + * current frame changes, so generation-keyed caches remain + * coherent. + * * Output type: const uint8_t ** */ GHOSTTY_KITTY_IMAGE_DATA_DATA_PTR = 7, /** * Length of the raw pixel data in bytes. Always equal to - * width * height * bytes-per-pixel for the reported format. + * width * height * bytes-per-pixel for the reported format. For a + * pending image, this is the expected length reserved against the + * storage limit even though DATA_PTR is not available yet. * * Output type: size_t * */ @@ -421,7 +430,10 @@ typedef enum GHOSTTY_ENUM_TYPED { * Stamps are unique and monotonically increasing process-wide and * are drawn from the same sequence as * GHOSTTY_KITTY_GRAPHICS_DATA_GENERATION. Never zero for a stored - * image, so zero can be used as an "empty" sentinel by callers. + * image, so zero can be used as an "empty" sentinel by callers. Pending + * payload completion preserves this value to retain image age; consumers + * detect that completion through GHOSTTY_KITTY_GRAPHICS_DATA_GENERATION + * and retry DATA_PTR. * * Output type: uint64_t * */ diff --git a/vendor/libghostty-vt/include/ghostty/vt/modes.h b/vendor/libghostty-vt/include/ghostty/vt/modes.h index 8e1fd911..6786f004 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/modes.h +++ b/vendor/libghostty-vt/include/ghostty/vt/modes.h @@ -92,7 +92,9 @@ extern "C" { #define GHOSTTY_MODE_SYNC_OUTPUT (ghostty_mode_new(2026, false)) /**< Synchronized output */ #define GHOSTTY_MODE_GRAPHEME_CLUSTER (ghostty_mode_new(2027, false)) /**< Grapheme cluster mode */ #define GHOSTTY_MODE_COLOR_SCHEME_REPORT (ghostty_mode_new(2031, false)) /**< Report color scheme */ +#define GHOSTTY_MODE_VISIBILITY_REPORT (ghostty_mode_new(2033, false)) /**< Report terminal visibility */ #define GHOSTTY_MODE_IN_BAND_RESIZE (ghostty_mode_new(2048, false)) /**< In-band size reports */ +#define GHOSTTY_MODE_PASTE_EVENTS (ghostty_mode_new(5522, false)) /**< Kitty clipboard protocol paste events */ /** @} */ /** diff --git a/vendor/libghostty-vt/include/ghostty/vt/mouse.h b/vendor/libghostty-vt/include/ghostty/vt/mouse.h index 4ba5f52e..4a582a23 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/mouse.h +++ b/vendor/libghostty-vt/include/ghostty/vt/mouse.h @@ -39,8 +39,7 @@ * @code{.c} * // Create a terminal and feed it some VT data that enables mouse tracking * GhosttyTerminal terminal; - * ghostty_terminal_new(NULL, &terminal, - * (GhosttyTerminalOptions){.cols = 80, .rows = 24, .max_scrollback = 0}); + * ghostty_terminal_new(NULL, &terminal, 80, 24); * * // Application might write data that enables mouse reporting, etc. * ghostty_terminal_vt_write(terminal, vt_data, vt_len); diff --git a/vendor/libghostty-vt/include/ghostty/vt/osc.h b/vendor/libghostty-vt/include/ghostty/vt/osc.h index 9409ebc7..7a681c3b 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/osc.h +++ b/vendor/libghostty-vt/include/ghostty/vt/osc.h @@ -63,6 +63,10 @@ typedef enum GHOSTTY_ENUM_TYPED { GHOSTTY_OSC_COMMAND_CONEMU_XTERM_EMULATION = 20, GHOSTTY_OSC_COMMAND_CONEMU_COMMENT = 21, GHOSTTY_OSC_COMMAND_KITTY_TEXT_SIZING = 22, + GHOSTTY_OSC_COMMAND_KITTY_CLIPBOARD_PROTOCOL = 23, + GHOSTTY_OSC_COMMAND_KITTY_DND_PROTOCOL = 24, + GHOSTTY_OSC_COMMAND_CONTEXT_SIGNAL = 25, + GHOSTTY_OSC_COMMAND_KITTY_DESKTOP_NOTIFICATION = 26, GHOSTTY_OSC_COMMAND_TYPE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyOscCommandType; diff --git a/vendor/libghostty-vt/include/ghostty/vt/paste.h b/vendor/libghostty-vt/include/ghostty/vt/paste.h index b3df5be4..4ba21a9a 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/paste.h +++ b/vendor/libghostty-vt/include/ghostty/vt/paste.h @@ -1,26 +1,67 @@ /** * @file paste.h * - * Paste utilities - validate and encode paste data for terminal input. + * Paste - paste into a terminal, and validate and encode paste data. */ #ifndef GHOSTTY_VT_PASTE_H #define GHOSTTY_VT_PASTE_H -/** @defgroup paste Paste Utilities - * - * Utilities for validating and encoding paste data for terminal input. - * - * ## Basic Usage - * - * Use ghostty_paste_is_safe() to check if paste data contains potentially - * dangerous sequences before sending it to the terminal. - * - * Use ghostty_paste_encode() to encode paste data for writing to the pty, +/** @defgroup paste Paste + * + * Pasting into a terminal, plus the terminal-free utilities for + * validating and encoding paste data. + * + * ## Pasting into a Terminal + * + * What a paste writes to the pty depends on the terminal's state, so + * the recommended way to paste is ghostty_terminal_paste(). The embedder + * hands over the MIME types the clipboard holds (just `text/plain` for + * an ordinary paste), a GhosttyMimeReader that produces the data of + * any one of them, and where the paste came from, and the terminal + * decides how its current modes apply: + * + * - If Kitty clipboard protocol paste events (mode 5522, + * GHOSTTY_MODE_PASTE_EVENTS) are enabled, the paste was user-initiated + * (GHOSTTY_PASTE_SOURCE_CLIPBOARD), and a clipboard_read callback is + * installed, the terminal sends the program a paste event listing the + * clipboard's MIME types with a one-time password instead of the data. + * The program then reads what it wants through the clipboard_read + * callback, which arrives with `granted` set so no permission prompt + * is needed. No data is read for the event. + * - Otherwise the first text representation is written: unsafe control + * bytes are replaced with spaces, and it is wrapped in bracketed paste + * sequences if mode 2004 (GHOSTTY_MODE_BRACKETED_PASTE) is enabled, or + * has its newlines converted to carriage returns if not. + * + * The data is pulled through GhosttyPaste::reader only when a + * representation is actually pasted (so a clipboard holding a large + * image next to some text costs nothing), and the encoded bytes + * stream to the write_pty callback (GHOSTTY_TERMINAL_OPT_WRITE_PTY) + * in chunks as they are produced, never in one piece. The callback + * may be invoked several times for a single paste; the pieces must be + * written to the pty in order. + * + * Text that could inject commands (a newline when unbracketed, or the + * bracketed paste terminator when bracketed) is refused with + * GHOSTTY_REJECTED and nothing written unless GhosttyPaste::allow_unsafe + * is set. The usual flow is to call once, confirm with the user on + * GHOSTTY_REJECTED, and call again with `allow_unsafe` set. Each call + * reads the text at most once and buffers it whole while the rule is + * applied, so the source needs no stability across reads (the + * confirmed retry simply pastes whatever the source holds then) and a + * refused or failed paste writes nothing at all. + * + * @snippet c-vt-paste/src/main.c terminal-paste + * + * ## Building Blocks + * + * For embedders that encode without a terminal, ghostty_paste_is_safe() + * checks if paste data contains potentially dangerous sequences + * (conservatively, regardless of terminal state) and + * ghostty_paste_encode() encodes paste data for writing to the pty, * including bracketed paste wrapping and unsafe byte stripping. * - * ## Examples - * * ### Safety Check * * @snippet c-vt-paste/src/main.c paste-safety @@ -35,12 +76,120 @@ #include <stdbool.h> #include <stddef.h> #include <ghostty/vt/types.h> +#include <ghostty/vt/io.h> +#include <ghostty/vt/terminal.h> #ifdef __cplusplus extern "C" { #endif /** + * Why a paste happened. + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** The user pasted from a clipboard: keybind, menu, middle click. */ + GHOSTTY_PASTE_SOURCE_CLIPBOARD = 0, + + /** + * Text inserted some other way: IME commit, drag and drop, scripted + * input. Always written as text, never as a paste event, matching + * kitty. This is not a way to opt out of paste events; an embedder + * that doesn't want them doesn't install a clipboard_read callback. + */ + GHOSTTY_PASTE_SOURCE_TEXT = 1, + GHOSTTY_PASTE_SOURCE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttyPasteSource; + +/** + * A paste of clipboard contents into the terminal. + * + * This is a sized struct; set `size` to `sizeof(GhosttyPaste)`. The + * MIME type array and the strings it points to are borrowed only for + * the duration of the ghostty_terminal_paste() call, as is everything + * the reader produces. + */ +typedef struct { + /** Size of this struct in bytes. */ + size_t size; + + /** + * The clipboard the contents came from. Reported to the program on a + * paste event (the selection and primary locations are both reported + * as the primary selection, the protocol knows only two); no effect + * on a text paste. + */ + GhosttyClipboardLocation location; + + /** Why this paste happened. */ + GhosttyPasteSource source; + + /** + * Borrowed array of the MIME types of the representations available, + * in preferred order. A text paste reads and writes the first entry + * with a text MIME type such as "text/plain" and ignores the rest. A + * paste event lists every entry and reads none. May be NULL when + * mimes_len is zero, which is nothing to paste. + */ + const GhosttyString* mimes; + + /** Number of entries in mimes. */ + size_t mimes_len; + + /** + * Produces the data of a representation on demand. Required when + * mimes_len is nonzero. + * + * Called at most once per ghostty_terminal_paste() call: for the + * text representation being pasted, never for anything else and + * never for a paste event. The MIME type requested is always an + * entry of `mimes`, passed through exactly as given there (the same + * pointer and length), so the callback may identify the + * representation by pointer or by content. A false return fails the + * paste with GHOSTTY_IO_ERROR. + */ + GhosttyMimeReader reader; + + /** + * Write text that could inject commands. Call with false, confirm + * with the user on GHOSTTY_REJECTED, and call again with true. + */ + bool allow_unsafe; +} GhosttyPaste; + +/** + * Paste into the terminal according to its current state: a Kitty + * clipboard protocol paste event if mode 5522 is enabled and a + * clipboard_read callback is installed, otherwise the text framed per + * mode 2004. See the group documentation for the full behavior. Output + * streams through the write_pty callback in chunks. The viewport is not + * scrolled; that is up to the embedder, as for key input. + * + * A paste event records a session grant for its one-time password only + * once the event is written; a failed call never leaves a grant for an + * event that was never sent. + * + * @param terminal The terminal handle + * @param paste The paste request, borrowed for the duration of the call + * @param[out] out_written On success, whether anything was written to + * the pty (the encoded text or a paste event). False means + * there was nothing to paste: no non-empty text + * representation. May be NULL. + * @return GHOSTTY_SUCCESS on success (see @p out_written); + * GHOSTTY_REJECTED if the text could inject commands and + * GhosttyPaste::allow_unsafe is false (nothing was written); + * GHOSTTY_INVALID_VALUE for a NULL terminal or paste, MIME + * types without a reader, or when no write_pty callback is + * installed; GHOSTTY_OUT_OF_MEMORY; GHOSTTY_IO_ERROR if the + * reader failed or there is no secure entropy source to mint a + * paste event password (wasm32-freestanding without + * GHOSTTY_SYS_OPT_RANDOM_SECURE set). Errors write nothing. + */ +GHOSTTY_API GhosttyResult ghostty_terminal_paste( + GhosttyTerminal terminal, + const GhosttyPaste* paste, + bool* out_written); + +/** * Check if paste data is safe to paste into the terminal. * * Data is considered unsafe if it contains: @@ -49,7 +198,9 @@ extern "C" { * to exit bracketed paste mode and inject commands * * This check is conservative and considers data unsafe regardless of - * current terminal state. + * current terminal state. ghostty_terminal_paste() applies the + * terminal-state-aware rule itself (newlines are safe inside a + * bracketed paste); use this to apply the stricter rule on top. * * @param data The paste data to check (must not be NULL) * @param len The length of the data in bytes @@ -74,6 +225,9 @@ GHOSTTY_API bool ghostty_paste_is_safe(const char* data, size_t len); * GHOSTTY_OUT_OF_SPACE and sets the required size in @p out_written. * The caller can then retry with a sufficiently sized buffer. * + * This is the encoder ghostty_terminal_paste() uses for a text paste; + * use it directly when there is no terminal to paste into. + * * @param data The paste data to encode (modified in place, may be NULL) * @param data_len The length of the input data in bytes * @param bracketed Whether bracketed paste mode is active diff --git a/vendor/libghostty-vt/include/ghostty/vt/point.h b/vendor/libghostty-vt/include/ghostty/vt/point.h index 8b717f49..9cab2758 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/point.h +++ b/vendor/libghostty-vt/include/ghostty/vt/point.h @@ -9,6 +9,8 @@ #include <stdint.h> +#include <ghostty/vt/types.h> + #ifdef __cplusplus extern "C" { #endif diff --git a/vendor/libghostty-vt/include/ghostty/vt/render.h b/vendor/libghostty-vt/include/ghostty/vt/render.h index 2e3f2f71..f6b5e222 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/render.h +++ b/vendor/libghostty-vt/include/ghostty/vt/render.h @@ -63,7 +63,10 @@ extern "C" { * tracking which rows in a partially dirty frame have changed. * * The user of the render state API is expected to unset both of these. - * The `update` call does not unset dirty state, it only updates it. + * The `update` call does not unset dirty state, it only updates it. After + * successfully rendering a complete frame, use ghostty_render_state_clean() + * to unset both layers in one call. The granular setters remain available + * for callers that only consume part of a frame. * * An extremely important detail: setting one dirty state doesn't unset * the other. For example, setting the global dirty state to false does not @@ -199,6 +202,14 @@ typedef enum GHOSTTY_ENUM_TYPED { /** 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, + + /** All cursor state in one sized struct (GhosttyRenderStateCursor). + * Initialize the output with GHOSTTY_INIT_SIZED before querying. */ + GHOSTTY_RENDER_STATE_DATA_CURSOR = 18, + + /** All render-state colors in one sized struct (GhosttyRenderStateColors). + * Initialize the output with GHOSTTY_INIT_SIZED before querying. */ + GHOSTTY_RENDER_STATE_DATA_COLORS = 19, GHOSTTY_RENDER_STATE_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyRenderStateData; @@ -236,6 +247,22 @@ typedef enum GHOSTTY_ENUM_TYPED { /** Row-local selected cell range (GhosttyRenderStateRowSelection). */ GHOSTTY_RENDER_STATE_ROW_DATA_SELECTION = 4, + + /** A borrowed view of the raw cell values for the current row + * (GhosttyCellsView). One value per column, identical to querying + * GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_RAW for each cell. The view + * is only valid as long as the underlying render state is not + * updated; it is unsafe to use after updating the render state. + * + * This is the bulk alternative to iterating cells one at a time. + * It lets callers with expensive call boundaries (e.g. WebAssembly + * embedders) read an entire row with a single call. + * + * Bit positions aren't protected by ABI, so callers should parse them + * out of the manifest from `ghostty_type_json`. Callers with access + * to the C header or without high FFI costs should use `ghostty_cell_get`. + */ + GHOSTTY_RENDER_STATE_ROW_DATA_CELLS_RAW = 5, GHOSTTY_RENDER_STATE_ROW_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyRenderStateRowData; @@ -274,16 +301,58 @@ typedef struct { } GhosttyRenderStateRowSelection; /** + * Render-state cursor information. + * + * This struct uses the sized-struct ABI pattern. Initialize with + * GHOSTTY_INIT_SIZED(GhosttyRenderStateCursor) before querying + * GHOSTTY_RENDER_STATE_DATA_CURSOR. + * + * When viewport_has_value is false, viewport_x, viewport_y, and wide_tail + * contain undefined data and must not be read. + * + * @ingroup render + */ +typedef struct { + /** Size of this struct in bytes. Must be set to sizeof(GhosttyRenderStateCursor). */ + size_t size; + + /** Whether the cursor is visible within the viewport. */ + bool viewport_has_value; + + /** Cursor viewport x position in cells. */ + uint16_t viewport_x; + + /** Cursor viewport y position in cells. */ + uint16_t viewport_y; + + /** Whether the cursor is on the tail of a wide character. */ + bool wide_tail; + + /** Whether the cursor is visible based on terminal modes. */ + bool visible; + + /** Whether the cursor should blink based on terminal modes. */ + bool blinking; + + /** Whether the cursor is at a password input field. */ + bool password_input; + + /** The visual style of the cursor. */ + GhosttyRenderStateCursorVisualStyle visual_style; +} GhosttyRenderStateCursor; + +/** * Render-state color information. * * This struct uses the sized-struct ABI pattern. Initialize with - * GHOSTTY_INIT_SIZED(GhosttyRenderStateColors) before calling - * ghostty_render_state_colors_get(). + * GHOSTTY_INIT_SIZED(GhosttyRenderStateColors) before querying + * GHOSTTY_RENDER_STATE_DATA_COLORS. * * Example: * @code * GhosttyRenderStateColors colors = GHOSTTY_INIT_SIZED(GhosttyRenderStateColors); - * GhosttyResult result = ghostty_render_state_colors_get(state, &colors); + * GhosttyResult result = ghostty_render_state_get( + * state, GHOSTTY_RENDER_STATE_DATA_COLORS, &colors); * @endcode * * @ingroup render @@ -409,6 +478,23 @@ GHOSTTY_API GhosttyResult ghostty_render_state_begin_update(GhosttyRenderState s GHOSTTY_API GhosttyResult ghostty_render_state_end_update(GhosttyRenderState state); /** + * Mark all dirty render-state data as consumed. + * + * This sets the global dirty state to GHOSTTY_RENDER_STATE_DIRTY_FALSE and + * clears every per-row dirty flag. It is idempotent and does not modify cell + * contents or dirty state owned by the terminal. Call this only after a + * complete frame has been rendered successfully; partial consumers should + * use ghostty_render_state_set() and ghostty_render_state_row_set() instead. + * + * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is + * NULL + * + * @ingroup render + */ +GHOSTTY_API GhosttyResult ghostty_render_state_clean(GhosttyRenderState state); + +/** * Get a value from a render state. * * The `out` pointer must point to a value of the type corresponding to the @@ -417,8 +503,9 @@ GHOSTTY_API GhosttyResult ghostty_render_state_end_update(GhosttyRenderState sta * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE) * @param data The data kind to query * @param[out] out Pointer to receive the queried value - * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is - * NULL or `data` is not a recognized enum value + * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or + * `out` is NULL, `data` is not a recognized enum value, or a sized + * output struct is smaller than `sizeof(size_t)` * * @ingroup render */ @@ -474,24 +561,6 @@ GHOSTTY_API GhosttyResult ghostty_render_state_set(GhosttyRenderState state, const void* value); /** - * Get the current color information from a render state. - * - * This writes as many fields as fit in the caller-provided sized struct. - * `out_colors->size` must be set by the caller (typically via - * GHOSTTY_INIT_SIZED(GhosttyRenderStateColors)). - * - * @param state The render state handle (NULL returns GHOSTTY_INVALID_VALUE) - * @param[out] out_colors Sized output struct to receive render-state colors - * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` or - * `out_colors` is NULL, or if `out_colors->size` is smaller than - * `sizeof(size_t)` - * - * @ingroup render - */ -GHOSTTY_API GhosttyResult ghostty_render_state_colors_get(GhosttyRenderState state, - GhosttyRenderStateColors* out_colors); - -/** * Create a new row iterator instance. * * All fields except the allocator are left undefined until populated @@ -521,7 +590,8 @@ GHOSTTY_API void ghostty_render_state_row_iterator_free(GhosttyRenderStateRowIte /** * Move a render-state row iterator to the next row. * - * Returns true if the iterator moved successfully and row data is + * Rows are visited contiguously in ascending viewport order, starting at + * y = 0. Returns true if the iterator moved successfully and row data is * available to read at the new position. * * @param iterator The iterator handle to advance (may be NULL) @@ -533,11 +603,35 @@ GHOSTTY_API void ghostty_render_state_row_iterator_free(GhosttyRenderStateRowIte GHOSTTY_API bool ghostty_render_state_row_iterator_next(GhosttyRenderStateRowIterator iterator); /** + * Move a render-state row iterator to the next row requiring a redraw. + * + * If the global dirty state is GHOSTTY_RENDER_STATE_DIRTY_FALSE, this returns + * false. If it is GHOSTTY_RENDER_STATE_DIRTY_PARTIAL, clean rows are skipped. + * If it is GHOSTTY_RENDER_STATE_DIRTY_FULL, every remaining row is returned + * regardless of its per-row dirty flag. Rows are returned in ascending + * viewport order. This function does not clear any dirty state. + * + * @param iterator The iterator handle to advance (NULL returns false) + * @param[out] out_y Receives the viewport y coordinate when true is returned + * (NULL returns false); it is not modified when false is + * returned + * @return true if advanced to a row requiring a redraw, false if an argument + * is NULL or the iterator has reached the end of the effective dirty + * rows + * + * @ingroup render + */ +GHOSTTY_API bool ghostty_render_state_row_iterator_next_dirty( + GhosttyRenderStateRowIterator iterator, + uint16_t* out_y); + +/** * Get a value from the current row in a render-state row iterator. * * The `out` pointer must point to a value of the type corresponding to the * requested data kind (see GhosttyRenderStateRowData). - * Call ghostty_render_state_row_iterator_next() at least once before + * Call ghostty_render_state_row_iterator_next() or + * ghostty_render_state_row_iterator_next_dirty() at least once before * calling this function. * * @param iterator The iterator handle to query (NULL returns GHOSTTY_INVALID_VALUE) @@ -586,7 +680,8 @@ GHOSTTY_API GhosttyResult ghostty_render_state_row_get_multi( * * The `value` pointer must point to a value of the type corresponding to the * requested option kind (see GhosttyRenderStateRowOption). - * Call ghostty_render_state_row_iterator_next() at least once before + * Call ghostty_render_state_row_iterator_next() or + * ghostty_render_state_row_iterator_next_dirty() at least once before * calling this function. * * @param iterator The iterator handle to update (NULL returns GHOSTTY_INVALID_VALUE) diff --git a/vendor/libghostty-vt/include/ghostty/vt/screen.h b/vendor/libghostty-vt/include/ghostty/vt/screen.h index 9f639b58..2899989d 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/screen.h +++ b/vendor/libghostty-vt/include/ghostty/vt/screen.h @@ -21,19 +21,21 @@ extern "C" { * Terminal screen cell and row types. * * These types represent the contents of a terminal screen. A GhosttyCell - * is a single grid cell and a GhosttyRow is a single row. Both are opaque - * values whose fields are accessed via ghostty_cell_get() and - * ghostty_row_get() respectively. + * is a single grid cell and a GhosttyRow is a single row. Cell fields can + * be accessed via ghostty_cell_get() or decoded from the packed layout in + * ghostty_type_json(). Rows are opaque and accessed via ghostty_row_get(). * * @{ */ /** - * Opaque cell value. + * Packed cell value. * - * Represents a single terminal cell. The internal layout is opaque and - * must be queried via ghostty_cell_get(). Obtain cell values from - * terminal query APIs. + * Represents a single terminal cell. Portable callers can query fields via + * ghostty_cell_get(). Boundary-sensitive callers can decode the packed value + * using the GhosttyCell descriptor returned by ghostty_type_json(). The + * manifest is authoritative for the linked build; hardcoding bit positions + * is unsupported. * * @ingroup screen */ @@ -51,6 +53,24 @@ typedef uint64_t GhosttyCell; typedef uint64_t GhosttyRow; /** + * A borrowed view of contiguous raw cell values. + * + * The memory is not owned by this struct. The pointer is only valid + * for the lifetime documented by the API that produces it. Each value + * can be queried via ghostty_cell_get() or decoded using the GhosttyCell + * packed descriptor returned by ghostty_type_json(). + * + * @ingroup screen + */ +typedef struct { + /** Pointer to len contiguous cell values. */ + const GhosttyCell* ptr; + + /** Number of cells. */ + size_t len; +} GhosttyCellsView; + +/** * Cell content tag. * * Describes what kind of content a cell holds. diff --git a/vendor/libghostty-vt/include/ghostty/vt/search.h b/vendor/libghostty-vt/include/ghostty/vt/search.h new file mode 100644 index 00000000..68aebcc4 --- /dev/null +++ b/vendor/libghostty-vt/include/ghostty/vt/search.h @@ -0,0 +1,501 @@ +/** + * @file search.h + * + * Search terminal contents, including scrollback, for a string. + */ + +#ifndef GHOSTTY_VT_SEARCH_H +#define GHOSTTY_VT_SEARCH_H + +#include <stddef.h> +#include <ghostty/vt/allocator.h> +#include <ghostty/vt/selection.h> +#include <ghostty/vt/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup search Search + * + * Search a terminal for a string, covering the active area and + * scrollback of both the primary and alternate screens. + * + * A GhosttySearch searches the terminal it was created with for a + * needle set with GHOSTTY_SEARCH_OPT_NEEDLE. It handles the hard + * parts of terminal search internally: results stay in sync with the + * live screens, survive primary/alternate screen switches (entering + * and leaving a fullscreen app such as vim does not restart a + * scrollback search), and recover from resize, reflow, resets, and + * scrollback pruning. + * + * A search starts idle. Setting the needle starts the search, + * changing it restarts the search from scratch, and clearing it + * returns the search to idle. Matching is byte-exact except ASCII + * letters, which compare case-insensitively. + * + * ## Driving a search + * + * Searching a large scrollback takes time, so the work is split into + * small steps the caller drives so that the caller can control + * performance more directly: + * + * - ghostty_search_tick() makes a bounded amount of progress on data + * the search has already copied. It never touches the terminal, + * meaning it can be safely called from a thread. + * - ghostty_search_feed() reads the terminal to copy in more data and + * pick up terminal changes. Feeding is the only way the search + * learns that the terminal changed, so keep feeding periodically + * while the search is in use. This requires exclusive terminal access. + * - ghostty_search_run() is a blocking convenience that feeds and + * ticks until the search is caught up. + * + * GHOSTTY_SEARCH_STATUS_COMPLETE means the search is caught up with + * the terminal as of the last feed. It never means finished forever, + * since later terminal writes require another feed to be seen. + * + * ## Matches are selections + * + * Every match is returned as a GhosttySelection snapshot with + * rectangle set to false, so the existing selection APIs all work on + * matches: ghostty_terminal_selection_format_buf() to copy the + * matched text, ghostty_terminal_point_from_grid_ref() with + * GHOSTTY_POINT_TAG_VIEWPORT to position highlight rectangles, + * ghostty_terminal_selection_contains() for hit testing, and + * ghostty_terminal_set() with GHOSTTY_TERMINAL_OPT_SELECTION to make + * a match the terminal's selection. + * + * Returned matches follow the usual snapshot lifetime rules: they are + * only valid until the next operation that modifies the terminal, + * including ghostty_terminal_vt_write(), resize, reset, and free. + * Read matches after a feed, use them before the terminal changes + * again, and re-read them rather than caching them. The selected + * match is kept accurate internally across terminal changes, so the + * safe way to follow a match is to re-read + * GHOSTTY_SEARCH_DATA_SELECTED_MATCH after each feed. + * + * ## Lifetime + * + * The search borrows the terminal it was created with and never frees + * it. Any number of searches, alongside other terminal readers such + * as formatters and render states, may share one terminal. + * + * The search and its terminal can be freed in either order. Freeing + * the search first releases tracked state it holds within the + * terminal. If the terminal is freed first, the search detects this: + * calls that need the terminal return GHOSTTY_INVALID_VALUE, reads + * return whatever the search last saw, and ghostty_search_free() + * releases only search-owned memory. A search cannot be rebound, so + * searching another terminal means creating a new search. + * + * ## Threading + * + * The library creates no threads. Calls on one GhosttySearch are not + * safe to make concurrently with each other, so the caller must + * serialize them. + * + * Functions that touch the terminal (ghostty_search_new(), + * ghostty_search_feed(), ghostty_search_run(), ghostty_search_set() + * with the needle and select options, and ghostty_search_free()) must + * also be serialized with all other access to the same terminal. + * + * Everything else (ghostty_search_tick(), ghostty_search_get(), and + * ghostty_search_get_multi()) only touches memory owned by the search + * and is safe to call while another thread modifies the terminal. + * This split is how Ghostty runs search on a background thread: tick + * freely, and take the terminal lock only to feed. Reading returned + * match values is always safe, but passing them to APIs that take the + * terminal follows the terminal serialization rule above. + * + * ## Example + * + * @snippet c-vt-search/src/main.c search-main + * + * @{ + */ + +/** + * Progress state of a search. + * + * @ingroup search + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** + * ghostty_search_tick() can make progress without terminal access. + */ + GHOSTTY_SEARCH_STATUS_RUNNING = 0, + + /** + * Blocked until ghostty_search_feed(). This is also the state right + * after a needle is set, since the search has not yet seen the + * terminal. + */ + GHOSTTY_SEARCH_STATUS_FEED_REQUIRED = 1, + + /** + * Caught up with the terminal state as of the last feed. This never + * means finished forever, since later terminal writes require + * another feed to be seen. A search with no needle set also reports + * complete, since there is nothing to look for. + */ + GHOSTTY_SEARCH_STATUS_COMPLETE = 2, + + GHOSTTY_SEARCH_STATUS_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySearchStatus; + +/** + * Scroll policy applied when a match becomes selected via + * GHOSTTY_SEARCH_OPT_SELECT_NEXT or GHOSTTY_SEARCH_OPT_SELECT_PREV. + * + * @ingroup search + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** Scroll the viewport so the match is visible, only if it is not + * already visible. This is the default. */ + GHOSTTY_SEARCH_SCROLL_IF_NEEDED = 0, + + /** Never scroll the viewport. */ + GHOSTTY_SEARCH_SCROLL_NONE = 1, + + GHOSTTY_SEARCH_SCROLL_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySearchScroll; + +/** + * Data fields readable with ghostty_search_get(). The output value + * type is documented per field. + * + * All reads reflect the terminal's active screen as of the last feed. + * When the running application switches to the alternate screen, the + * next feed switches counts, matches, and selection to that screen's + * results. Primary screen results, including completed scrollback + * searches, are retained and restored on the way back. + * + * @ingroup search + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** Current search status: GhosttySearchStatus*. */ + GHOSTTY_SEARCH_DATA_STATUS = 0, + + /** + * The needle this search is looking for: GhosttyString*. The bytes + * are borrowed from the search and remain valid until the needle is + * changed or the search is freed. Returns GHOSTTY_NO_VALUE when no + * needle is set. + */ + GHOSTTY_SEARCH_DATA_NEEDLE = 1, + + /** + * Total matches found so far on the active screen: size_t*. Zero + * until the first feed. + */ + GHOSTTY_SEARCH_DATA_TOTAL_MATCHES = 2, + + /** + * Index of the selected match: size_t*. This indexes the newest to + * oldest ordering of GHOSTTY_SEARCH_DATA_MATCHES, where 0 is the + * newest match, so a "k of n" find bar renders index + 1 of + * GHOSTTY_SEARCH_DATA_TOTAL_MATCHES. Returns GHOSTTY_NO_VALUE when + * nothing is selected. + */ + GHOSTTY_SEARCH_DATA_SELECTED_INDEX = 3, + + /** + * The selected match: GhosttySelection*. This is an untracked + * snapshot with standard GhosttySelection lifetime rules. Returns + * GHOSTTY_NO_VALUE when nothing is selected. + */ + GHOSTTY_SEARCH_DATA_SELECTED_MATCH = 4, + + /** + * All matches on the active screen, ordered newest to oldest, from + * the bottom of the active area up through scrollback: + * GhosttySelectionBuffer*. Set ptr to NULL with cap 0 to query the + * required capacity. An undersized buffer returns + * GHOSTTY_OUT_OF_SPACE with the required capacity in len. + */ + GHOSTTY_SEARCH_DATA_MATCHES = 5, + + /** + * Matches on the pages covering the viewport, for drawing highlight + * rectangles: GhosttySelectionBuffer*. The list is computed during + * feeds and cached, so it reflects the viewport as of the last + * feed. + * + * Matches are found a page at a time, so the list can include + * matches slightly outside the visible viewport when they share a + * page with it. Ghostty's own renderer behaves the same way. + * Converting each match to viewport coordinates with + * ghostty_terminal_point_from_grid_ref() clips this naturally: skip + * matches that fail the conversion or whose row is beyond the + * visible row count. + */ + GHOSTTY_SEARCH_DATA_VIEWPORT_MATCHES = 6, + + /** Current scroll policy: GhosttySearchScroll*. */ + GHOSTTY_SEARCH_DATA_SELECT_SCROLL = 7, + + GHOSTTY_SEARCH_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySearchData; + +/** + * Options writable with ghostty_search_set(). The value type, and + * what a NULL value means, is documented per option. + * + * @ingroup search + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** + * Set the needle to search for: const GhosttyString*. The bytes are + * copied, so the caller's memory does not need to outlive the call. + * Matching is byte-exact except ASCII letters, which compare + * case-insensitively. + * + * Changing the needle restarts the search from scratch and drops + * all results. As an exception, setting a needle equal to the current + * one (compared the same way as matching) keeps existing results, + * so find bars can resubmit freely. A NULL or empty value clears + * the needle and returns the search to idle. + * + * Replacing or clearing a needle releases tracked state held + * within the terminal, so the caller must serialize this with all + * other access to the same terminal. Returns GHOSTTY_INVALID_VALUE + * after the terminal was freed. + */ + GHOSTTY_SEARCH_OPT_NEEDLE = 0, + + /** + * Select the next match, moving toward older content: from the + * bottom of the screen upward into history, the direction a search + * from the prompt usually wants. Wraps around past the oldest + * match. + * + * The value must be NULL. It is reserved for future use. + * + * This catches up with the terminal first, so it is safe to call at + * any time relative to feeds. The viewport scrolls to the newly + * selected match according to GHOSTTY_SEARCH_OPT_SELECT_SCROLL. + * This reads the terminal, so the caller must serialize it with all + * other access to the same terminal. Returns GHOSTTY_NO_VALUE when + * there are no matches. + */ + GHOSTTY_SEARCH_OPT_SELECT_NEXT = 1, + + /** + * Select the previous match, moving toward newer content, wrapping + * around past the newest match. Otherwise identical to + * GHOSTTY_SEARCH_OPT_SELECT_NEXT. + */ + GHOSTTY_SEARCH_OPT_SELECT_PREV = 2, + + /** + * Set the scroll policy applied by the select options: const + * GhosttySearchScroll*. The policy persists until changed. A NULL + * value resets it to GHOSTTY_SEARCH_SCROLL_IF_NEEDED. This only + * modifies search-owned state and never reads the terminal. + */ + GHOSTTY_SEARCH_OPT_SELECT_SCROLL = 3, + + GHOSTTY_SEARCH_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySearchOption; + +/** + * Create a search bound to a terminal. + * + * The search borrows the terminal and never frees it. The search and + * the terminal can be freed in either order; see ghostty_search_free(). + * + * The search starts idle with no needle: it reports + * GHOSTTY_SEARCH_STATUS_COMPLETE and finds nothing. Set + * GHOSTTY_SEARCH_OPT_NEEDLE to start searching. + * + * Creation is cheap and does not read terminal contents, but it + * registers the search with the terminal so the two can be freed in + * any order. The caller must serialize this call with all other + * access to the same terminal. + * + * @param allocator Allocator, or NULL for the default allocator + * @param out_search Receives the created search handle + * @param terminal Terminal to bind the search to + * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if + * out_search or terminal is invalid, or GHOSTTY_OUT_OF_MEMORY + * if allocation fails + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_new( + const GhosttyAllocator* allocator, + GhosttySearch* out_search, + GhosttyTerminal terminal); + +/** + * Free a search. + * + * If the bound terminal is still alive, this releases tracked state + * the search holds within it, so the caller must serialize this call + * with all other access to the same terminal. If the terminal was + * already freed, the search has been detached and this releases only + * search-owned memory. Passing NULL is allowed and is a no-op. + * + * @param search Search handle to free + * + * @ingroup search + */ +GHOSTTY_API void ghostty_search_free(GhosttySearch search); + +/** + * Make a bounded amount of search progress. + * + * This only works on data the search has already copied and never + * reads the terminal, so it is safe to call while another thread + * modifies the terminal. Call it in a loop while the status is + * GHOSTTY_SEARCH_STATUS_RUNNING. When the status becomes + * GHOSTTY_SEARCH_STATUS_FEED_REQUIRED, call ghostty_search_feed() to + * unblock it. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @param[out] out_status Receives the status after the tick (may be NULL) + * @return GHOSTTY_SUCCESS on success, or GHOSTTY_INVALID_VALUE if + * search is NULL + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_tick( + GhosttySearch search, + GhosttySearchStatus* out_status); + +/** + * Read the terminal to update the search. + * + * Each feed catches the search up with the terminal: it reconciles + * the tracked screens against the live ones, re-scans the active + * area, refreshes the viewport match list, gives the scrollback + * searcher its next chunk of data, and prunes results that scrollback + * eviction invalidated. Feeding is also the only way the search + * learns about terminal changes, so keep feeding periodically while + * the search is in use, even after it reports complete. + * + * This reads the terminal, so the caller must serialize it with all + * other access to the same terminal. Each call does a bounded amount + * of work so that any caller-held terminal lock is held only briefly. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @return GHOSTTY_SUCCESS on success, or GHOSTTY_INVALID_VALUE if + * search is NULL or the terminal was freed + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_feed(GhosttySearch search); + +/** + * Feed and tick until the search is caught up with the terminal. + * + * This is a blocking convenience for one-shot and single-threaded + * embedders. It always performs at least one feed, so it also picks + * up any terminal changes since the last feed, then loops until the + * status is GHOSTTY_SEARCH_STATUS_COMPLETE. Searching a large + * scrollback can take a while, so interactive embedders should drive + * ghostty_search_tick() and ghostty_search_feed() themselves. + * + * This reads the terminal for the entire call, so the caller must + * serialize it with all other access to the same terminal. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @return GHOSTTY_SUCCESS on success, or GHOSTTY_INVALID_VALUE if + * search is NULL or the terminal was freed + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_run(GhosttySearch search); + +/** + * Write an option to a search. + * + * The value type, and what a NULL value means, depends on the option + * and is documented by GhosttySearchOption. The needle and select + * options touch the terminal, so the caller must serialize those + * calls with all other access to the same terminal. + * GHOSTTY_SEARCH_OPT_SELECT_SCROLL only modifies search-owned state. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @param option Option to write + * @param value Pointer to the input value for the option. The meaning + * of NULL is documented per option. + * @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if a select + * option found no matches, GHOSTTY_OUT_OF_MEMORY if + * allocation fails, or GHOSTTY_INVALID_VALUE if search, + * option, or value is invalid or the option needs a terminal + * that was already freed + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_set( + GhosttySearch search, + GhosttySearchOption option, + const void* value); + +/** + * Read a data field from a search. + * + * The output value type depends on data and is documented by + * GhosttySearchData. This never reads the terminal, so it is safe to + * call while another thread modifies the terminal. Returned + * selections are untracked snapshots with standard GhosttySelection + * lifetime rules. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @param data Data field to read + * @param value Output pointer whose type depends on data + * @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the + * requested data has no value, GHOSTTY_OUT_OF_SPACE if a + * provided GhosttySelectionBuffer is too small (required + * capacity in its len), GHOSTTY_OUT_OF_MEMORY if collecting + * viewport matches fails, or GHOSTTY_INVALID_VALUE if search, + * data, or value is invalid + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_get( + GhosttySearch search, + GhosttySearchData data, + void* value); + +/** + * Read multiple data fields from a search in a single call. + * + * This is an optimization over calling ghostty_search_get() multiple + * times. Each entry in values must point to storage of the type + * documented by the corresponding GhosttySearchData key. + * + * If any individual read fails, the function returns that error and + * writes the index of the failing key to out_written when out_written + * is non-NULL. Earlier keys have already been written. On success, + * out_written receives count when non-NULL. A too-small + * GhosttySelectionBuffer stops the batch with GHOSTTY_OUT_OF_SPACE at + * that key's index with the required capacity in its len, so order + * buffer-valued keys after scalar keys. + * + * @param search Search handle (NULL returns GHOSTTY_INVALID_VALUE) + * @param count Number of data fields to read + * @param keys Data fields to read (must not be NULL) + * @param values Output pointers corresponding to keys (must not be NULL) + * @param out_written Optional number of fields read, or failing index + * on error + * @return GHOSTTY_SUCCESS on success, or the first failing read's + * result + * + * @ingroup search + */ +GHOSTTY_API GhosttyResult ghostty_search_get_multi( + GhosttySearch search, + size_t count, + const GhosttySearchData* keys, + void** values, + size_t* out_written); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* GHOSTTY_VT_SEARCH_H */ diff --git a/vendor/libghostty-vt/include/ghostty/vt/selection.h b/vendor/libghostty-vt/include/ghostty/vt/selection.h index 3b926aab..0158965e 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/selection.h +++ b/vendor/libghostty-vt/include/ghostty/vt/selection.h @@ -117,6 +117,29 @@ typedef struct { } GhosttySelection; /** + * A caller-provided buffer of selections. + * + * This follows the same conventions as GhosttyBuffer: ptr may be NULL with + * cap 0 to query the required capacity. APIs that fill this type set len to + * the number of entries written on GHOSTTY_SUCCESS, or to the required entry + * capacity on GHOSTTY_OUT_OF_SPACE. + * + * @ingroup selection + */ +typedef struct { + /** Destination buffer for selections. May be NULL when cap is 0 to query + * the required capacity. */ + GhosttySelection* ptr; + + /** Capacity of ptr in entries. */ + size_t cap; + + /** Entries written on success, or required entry capacity on + * GHOSTTY_OUT_OF_SPACE. */ + size_t len; +} GhosttySelectionBuffer; + +/** * Options for deriving a word selection from a terminal grid reference. * * This is a sized struct. Use GHOSTTY_INIT_SIZED() to initialize it. @@ -884,6 +907,8 @@ GHOSTTY_API GhosttyResult ghostty_terminal_selection_format_buf( * The returned buffer is allocated using allocator, or the default allocator * if NULL is passed. The caller owns the returned buffer and must free it with * ghostty_free(), passing the same allocator and returned length. + * Empty output returns GHOSTTY_SUCCESS with *out_ptr set to NULL and + * *out_len set to zero. This result can be passed to ghostty_free(). * * The returned bytes are not NUL-terminated. This supports plain text, VT, and * HTML uniformly as byte output. diff --git a/vendor/libghostty-vt/include/ghostty/vt/sgr.h b/vendor/libghostty-vt/include/ghostty/vt/sgr.h index 757c5aed..b8aecb64 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/sgr.h +++ b/vendor/libghostty-vt/include/ghostty/vt/sgr.h @@ -319,31 +319,6 @@ GHOSTTY_API GhosttySgrAttributeTag ghostty_sgr_attribute_tag(GhosttySgrAttribute GHOSTTY_API GhosttySgrAttributeValue* ghostty_sgr_attribute_value( GhosttySgrAttribute* attr); -#ifdef __wasm__ -/** - * Allocate memory for an SGR attribute (WebAssembly only). - * - * This is a convenience function for WebAssembly environments to allocate - * memory for an SGR attribute structure that can be passed to ghostty_sgr_next. - * - * @return Pointer to the allocated attribute structure - * - * @ingroup wasm - */ -GHOSTTY_API GhosttySgrAttribute* ghostty_wasm_alloc_sgr_attribute(void); - -/** - * Free memory for an SGR attribute (WebAssembly only). - * - * Frees memory allocated by ghostty_wasm_alloc_sgr_attribute. - * - * @param attr Pointer to the attribute structure to free - * - * @ingroup wasm - */ -GHOSTTY_API void ghostty_wasm_free_sgr_attribute(GhosttySgrAttribute* attr); -#endif - #ifdef __cplusplus } #endif diff --git a/vendor/libghostty-vt/include/ghostty/vt/snapshot.h b/vendor/libghostty-vt/include/ghostty/vt/snapshot.h new file mode 100644 index 00000000..89cb5398 --- /dev/null +++ b/vendor/libghostty-vt/include/ghostty/vt/snapshot.h @@ -0,0 +1,569 @@ +/** + * @file snapshot.h + * + * Encode and restore complete terminal snapshots. + */ + +#ifndef GHOSTTY_VT_SNAPSHOT_H +#define GHOSTTY_VT_SNAPSHOT_H + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <ghostty/vt/allocator.h> +#include <ghostty/vt/io.h> +#include <ghostty/vt/terminal.h> +#include <ghostty/vt/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup snapshot Terminal Snapshot + * + * Encode and restore the complete state of a terminal via a binary format. + * + * A snapshot is an ordered, CRC-protected record stream. Its READY marker + * follows enough state to render and resume the terminal, including any + * unfinished VT parser input. Older scrollback pages follow READY and the + * FINISH marker terminates the complete snapshot. + * + * End-of-file before an operation's required READY or FINISH marker is + * malformed, truncated snapshot data and returns GHOSTTY_INVALID_VALUE. + * GHOSTTY_IO_ERROR is reserved for a reader callback that returns false. + * + * ## Examples + * + * The complete working example is available in `example/c-vt-snapshot`. + * + * ### Encode a terminal and its unfinished VT continuation + * @snippet c-vt-snapshot/src/main.c snapshot-encode + * + * ### Restore a complete snapshot in one call + * @snippet c-vt-snapshot/src/main.c snapshot-decode + * + * ### Adapt a byte source to GhosttyReader + * @snippet c-vt-snapshot/src/main.c snapshot-buffer-reader + * + * ### Restore READY first, then incrementally prepend history + * @snippet c-vt-snapshot/src/main.c snapshot-incremental + * + * ## Format + * + * Every integer is unsigned and little-endian. The stream begins with this + * fixed ten-byte envelope: + * + * @code{.unparsed} + * byte 0 8 10 + * +---------------+--------+ + * | "GHOSTSNP" | version| + * | 8-byte magic | u16 | + * +---------------+--------+ + * @endcode + * + * The envelope is followed by independently checksummed records. A record's + * CRC32C covers its encoded tag and payload length followed by its payload; it + * does not cover the CRC field itself. + * + * @code{.unparsed} + * byte 0 2 6 10 10 + payload_len + * +-------+-------------+-----------+----------------+ + * | tag | payload_len | CRC32C | payload | + * | u16 | u32 | u32 | payload_len B | + * +-------+-------------+-----------+----------------+ + * \____________________/ \______________/ + * CRC prefix CRC suffix + * @endcode + * + * Record groups occur in this strict order. SCREEN and HISTORY groups contain + * one entry for each screen declared by TERMINAL. Each manifest is followed + * by the number of PAGE records it declares. Active SCREEN pages make the + * terminal renderable; HISTORY pages are older scrollback ordered newest to + * oldest so an incremental decoder can prepend them as they arrive. + * + * @code{.unparsed} + * +---------------- TERMINAL ----------------+ + * | terminal-wide state and screen count | + * +----------------- SCREEN -----------------+ repeated per screen + * | active-screen manifest | + * +------------------ PAGE ------------------+ repeated per manifest + * | active screen rows | + * +------------- CONTINUATION ---------------+ + * | unfinished VT/UTF-8 input, or ground | + * +------------------ READY -----------------+ + * | empty renderable-state marker | ready() returns here + * +----------------- HISTORY ----------------+ repeated per screen + * | scrollback manifest | + * +------------------ PAGE ------------------+ next() consumes one page + * | older screen rows | + * +------------------ FINISH ----------------+ + * | empty end-of-snapshot marker | next() returns NO_VALUE + * +------------------------------------------+ + * | trailing transport bytes (not consumed) | + * +------------------------------------------+ + * @endcode + * + * READY separates the renderable prefix through CONTINUATION from history. + * FINISH terminates the record sequence. Both are empty records protected by + * CRC32C, like every other record. Declared record counts, tags, and strict + * decoding enforce the stream's ordering and completeness. + * + * Snapshot format version 1 is a work in progress and does not yet carry a + * binary-compatibility guarantee. + * + * @see <a href="https://github.com/ghostty-org/ghostty/blob/main/src/terminal/snapshot/main.zig">Snapshot format and Zig codec documentation</a> + * + * @{ + */ + +/** + * Configurable snapshot decoder options. + * + * Options may only be changed before decoding starts. Calling + * ghostty_snapshot_decoder_set() after ghostty_snapshot_decoder_ready() or + * ghostty_snapshot_decoder_decode() returns GHOSTTY_INVALID_VALUE. + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** + * Largest non-ground continuation the decoder will accept. + * + * A value of zero accepts only snapshots whose VT parser is in the ground + * state. The decoder default matches the largest built-in APC protocol + * buffer limit, currently 65 MiB. + * + * This is primarily an input validation limit. When + * GHOSTTY_SNAPSHOT_DECODER_OPT_RETAIN_CONTINUATION is true, the same value + * also becomes the continuation tracking limit on the returned terminal. + * + * Input type: size_t * + */ + GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_CONTINUATION_BYTES = 0, + + /** + * Retain the decoded continuation on the returned terminal. + * + * When true, terminals returned by ghostty_snapshot_decoder_ready() and + * ghostty_snapshot_decoder_decode() use + * GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_CONTINUATION_BYTES as their continuation + * tracking limit. The existing ghostty_terminal_continuation_* APIs can then + * export the exact unfinished VT or UTF-8 input restored from the snapshot. + * + * This is false by default. A maximum continuation size of zero leaves + * tracking disabled. With a nonzero maximum, tracking remains enabled even + * when the decoded continuation is empty. Exporting an empty continuation + * does not disable it. Callers that do not need ongoing tracking must still + * set GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to zero after export and + * before writing post-snapshot input. + * + * Input type: bool * + */ + GHOSTTY_SNAPSHOT_DECODER_OPT_RETAIN_CONTINUATION = 1, + + GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySnapshotDecoderOption; + +/** + * Queryable snapshot decoder data. + * + * Each variant documents the output pointer type expected by + * ghostty_snapshot_decoder_get(). + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** Invalid data type. Never results in data extraction. */ + GHOSTTY_SNAPSHOT_DECODER_DATA_INVALID = 0, + + /** + * Current maximum accepted continuation size. + * + * This value is available in every non-failed decoder state. + * + * Output type: size_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_MAX_CONTINUATION_BYTES = 1, + + /** + * Number of snapshot source bytes consumed so far. + * + * At FINISH this identifies the first byte after the snapshot. Trailing + * bytes are not consumed. This value is unavailable after a decoding error, + * because the decoder can no longer guarantee its source position. + * + * Output type: size_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_SOURCE_OFFSET = 2, + + /** + * Advisory complete logical history extent for the primary screen. + * + * The value counts rows before the active area, including any resident + * overlap carried before READY. It becomes available after READY validates. + * + * Output type: uint64_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY = 3, + + /** + * Advisory complete logical history extent for the alternate screen. + * + * The value has the same semantics and lifetime as + * GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY. Querying it returns + * GHOSTTY_NO_VALUE when the snapshot does not declare an alternate screen. + * + * Output type: uint64_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_ALTERNATE = 4, + + /** + * Screen associated with the most recently decoded history page. + * + * This value is available only after ghostty_snapshot_decoder_next() + * returns GHOSTTY_SUCCESS. A later call to next replaces it or clears it + * when FINISH is reached or an error occurs. + * + * Output type: GhosttyTerminalScreen * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_SCREEN = 5, + + /** + * Rows prepended by the most recently decoded history page. + * + * Zero means the page was consumed and validated but could not be + * applied to the live terminal. + * + * Output type: size_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS = 6, + + /** + * Page records remaining in the same screen's HISTORY sequence. + * + * This is not a count of all pages remaining in the snapshot. + * + * Output type: uint32_t * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_REMAINING = 7, + + /** + * Whether decoded continuation tracking is retained on returned terminals. + * + * This value is available in every non-failed decoder state. + * + * Output type: bool * + */ + GHOSTTY_SNAPSHOT_DECODER_DATA_RETAIN_CONTINUATION = 8, + + GHOSTTY_SNAPSHOT_DECODER_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttySnapshotDecoderData; + +/** + * Encode a complete terminal snapshot to a writer. + * + * The terminal's persistent VT stream supplies the continuation bytes needed + * to reconstruct unfinished parser state. The caller must prevent concurrent + * writes or other terminal mutation for the duration of this call. The writer + * callback must not call terminal APIs with the same terminal handle. + * A terminal can be encoded with tracking disabled when its VT parser and + * UTF-8 decoder are both at ground. If either is unfinished, tracking must + * have been enabled before the input that produced that state was written; + * otherwise this returns GHOSTTY_INVALID_VALUE. + * + * Encoding begins at the writer's current position. If an error occurs, the + * writer may contain a partial snapshot without a valid FINISH marker. + * Calls to the writer are synchronous; this function does not flush or make + * the caller's destination durable. + * + * @param terminal Terminal to encode (must not be NULL) + * @param writer Destination writer whose write callback must not be NULL + * @return GHOSTTY_SUCCESS on success, GHOSTTY_IO_ERROR if the writer rejects + * output, GHOSTTY_LIMIT_EXCEEDED if output accounting overflows, or + * another error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_encode(GhosttyTerminal terminal, + GhosttyWriter writer); + +/** + * Encode a complete terminal snapshot to a caller-provided buffer. + * + * Pass NULL for buf with buf_len zero to query the required size. If the + * buffer is too small, this returns GHOSTTY_OUT_OF_SPACE and stores the + * required capacity in out_written. A non-NULL undersized buffer may contain + * a partial snapshot prefix. On success, out_written receives the number of + * bytes encoded. + * + * A terminal can be encoded with tracking disabled when its VT parser and + * UTF-8 decoder are both at ground. If either is unfinished, tracking must + * have been enabled before the input that produced that state was written; + * otherwise this returns GHOSTTY_INVALID_VALUE. + * + * @param terminal Terminal to encode (must not be NULL) + * @param buf Destination buffer, or NULL when buf_len is zero + * @param buf_len Destination buffer capacity in bytes + * @param[out] out_written Bytes written, or required capacity on + * GHOSTTY_OUT_OF_SPACE (must not be NULL) + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_encode_buf( + GhosttyTerminal terminal, + uint8_t* buf, + size_t buf_len, + size_t* out_written); + +/** + * Encode a complete terminal snapshot to an allocated buffer. + * + * The returned buffer is allocated with allocator, or the default allocator + * when allocator is NULL. The caller must release it with ghostty_free(), + * passing the same allocator used here. + * + * A terminal can be encoded with tracking disabled when its VT parser and + * UTF-8 decoder are both at ground. If either is unfinished, tracking must + * have been enabled before the input that produced that state was written; + * otherwise this returns GHOSTTY_INVALID_VALUE. + * + * @param terminal Terminal to encode (must not be NULL) + * @param allocator Allocator for the output, or NULL for the default allocator + * @param[out] out_ptr Allocated snapshot bytes (must not be NULL) + * @param[out] out_len Number of allocated snapshot bytes (must not be NULL) + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_encode_alloc( + GhosttyTerminal terminal, + const GhosttyAllocator* allocator, + uint8_t** out_ptr, + size_t* out_len); + +/** + * Create a snapshot decoder that reads from a caller-provided reader. + * + * The decoder stores a copy of reader. Its read callback must not be NULL, and + * both the callback and its caller-owned context must remain valid until + * FINISH is reached or the decoder is freed. Reads are synchronous and occur + * only during ready, next, or decode calls. A zero-byte successful read is + * permanent end-of-file, not temporary starvation; nonblocking sources must + * wait outside the decoder or block in their callback. The read callback must + * not call APIs, including ghostty_snapshot_decoder_free(), on the decoder + * that owns it. Returning false reports GHOSTTY_IO_ERROR; returning true with + * zero bytes before a required marker reports truncated snapshot data as + * GHOSTTY_INVALID_VALUE. + * + * @param allocator Allocator for decoder and decoded terminal state, or NULL + * for the default allocator + * @param decoder Pointer to receive the decoder handle (must not be NULL) + * @param reader Snapshot source reader + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new( + const GhosttyAllocator* allocator, + GhosttySnapshotDecoder* decoder, + GhosttyReader reader); + +/** + * Create a snapshot decoder over a borrowed byte buffer. + * + * The bytes are not copied. ptr must remain valid and immutable until FINISH + * is reached or the decoder is freed. Bytes after FINISH are not consumed; + * query GHOSTTY_SNAPSHOT_DECODER_DATA_SOURCE_OFFSET to locate them. + * + * @param allocator Allocator for decoder and decoded terminal state, or NULL + * for the default allocator + * @param decoder Pointer to receive the decoder handle (must not be NULL) + * @param ptr Snapshot source bytes + * @param len Number of source bytes + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new_buf( + const GhosttyAllocator* allocator, + GhosttySnapshotDecoder* decoder, + const uint8_t* ptr, + size_t len); + +/** + * Free a snapshot decoder. + * + * This does not release the caller's ownership of a terminal returned by + * ready or decode. Abandoning an incremental decode leaves that terminal + * usable with whatever history had already been restored. + * + * @param decoder Decoder to free (may be NULL) + * + * @ingroup snapshot + */ +GHOSTTY_API void ghostty_snapshot_decoder_free(GhosttySnapshotDecoder decoder); + +/** + * Set a snapshot decoder option. + * + * The value pointer must have the type documented by option. Options may only + * be changed before decoding starts. + * + * @param decoder Decoder handle (must not be NULL) + * @param option Option to change + * @param value Pointer to the option value (must not be NULL) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if decoding has + * started or an argument is invalid, or another error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_set( + GhosttySnapshotDecoder decoder, + GhosttySnapshotDecoderOption option, + const void* value); + +/** + * Decode and validate the renderable snapshot prefix through READY. + * + * On success, terminal receives a caller-owned terminal with its persistent + * VT stream already restored from the snapshot continuation. The terminal is + * immediately usable for rendering and live input. Older scrollback remains + * to be restored with ghostty_snapshot_decoder_next(). + * + * The restored parser state may be unfinished. By default, terminal + * continuation tracking is disabled and + * GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES returns zero. When + * GHOSTTY_SNAPSHOT_DECODER_OPT_RETAIN_CONTINUATION is true, the decoder's + * maximum continuation size is applied to the terminal, and the terminal + * continuation APIs export the exact current continuation when that limit is + * nonzero. Tracking remains enabled even if the exported continuation is + * empty. Callers that do not need ongoing tracking must set + * GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to zero after export and before + * writing any post-snapshot bytes, because later input may change it. + * + * The caller must keep the returned terminal alive until FINISH validates or + * the decoder is freed. The decoder borrows this terminal handle while it + * restores history; ghostty_snapshot_decoder_next() uses it automatically. + * + * This operation may only be called once and only before decoding starts. + * terminal is set to NULL on every error. A decoding, I/O, or allocation + * error after input consumption begins poisons the decoder, after which it + * must be freed. An invalid argument or lifecycle error detected before the + * operation consumes input does not poison it. + * + * @param decoder Decoder handle (must not be NULL) + * @param[out] terminal Pointer to receive the terminal (must not be NULL) + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_ready( + GhosttySnapshotDecoder decoder, + GhosttyTerminal* terminal); + +/** + * Decode one history page into the terminal returned by READY. + * + * Each GHOSTTY_SUCCESS consumes and validates one PAGE record. Query the + * GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_* values before calling next again. + * GHOSTTY_NO_VALUE means FINISH was validated; repeated calls after FINISH + * also return GHOSTTY_NO_VALUE. + * + * The terminal may be rendered, resized, and fed live PTY input between calls. + * If a history page can no longer be applied safely, it is still consumed and + * validated and progress reports zero rows. The decoder applies history + * to the caller-owned terminal produced by its READY operation. + * + * A decoding error invalidates the decoder's source position. The terminal + * remains caller-owned and usable with its already-restored history, but only + * ghostty_snapshot_decoder_free() may subsequently be called on the decoder. + * + * @param decoder Decoder handle (must not be NULL) + * @return GHOSTTY_SUCCESS for one page, GHOSTTY_NO_VALUE after FINISH, or an + * error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_next( + GhosttySnapshotDecoder decoder); + +/** + * Decode and validate one complete snapshot. + * + * This is the one-shot form of READY followed by all history pages through + * FINISH. It may only be called before decoding starts. Bytes following FINISH + * are left unread. On success terminal receives a caller-owned terminal with + * its persistent VT stream restored. Continuation tracking on the returned + * terminal is disabled by default. When + * GHOSTTY_SNAPSHOT_DECODER_OPT_RETAIN_CONTINUATION is true, the decoder's + * maximum continuation size is applied to the terminal, and the terminal + * continuation APIs export the exact current continuation when that limit is + * nonzero. Tracking remains enabled even if the exported continuation is + * empty. Callers that do not need ongoing tracking must set + * GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to zero after export and before + * writing any post-snapshot bytes, because later input may change it. + * terminal is set to NULL on every error. + * A decoding, I/O, or allocation error after input consumption begins poisons + * the decoder, after which it must be freed. An invalid argument or + * lifecycle error detected before the operation consumes input does not + * poison it. + * + * @param decoder Decoder handle (must not be NULL) + * @param[out] terminal Pointer to receive the terminal (must not be NULL) + * @return GHOSTTY_SUCCESS on success, or an error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_decode( + GhosttySnapshotDecoder decoder, + GhosttyTerminal* terminal); + +/** + * Get typed data from a snapshot decoder. + * + * The output pointer must have the type documented by data. A phase-dependent + * value that is not currently available returns GHOSTTY_NO_VALUE. + * + * @param decoder Decoder handle (must not be NULL) + * @param data Data kind to query + * @param[out] out Pointer to receive the value (must not be NULL) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_NO_VALUE if the requested data + * is unavailable, or another error code on failure + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get( + GhosttySnapshotDecoder decoder, + GhosttySnapshotDecoderData data, + void* out); + +/** + * Get multiple snapshot decoder data fields in a single call. + * + * Each keys element selects a data kind and the corresponding values element + * points to storage of the documented output type. Processing stops at the + * first error. On success out_written is set to count; on error it is set to + * the number of values written before the failing key. Invalid array arguments + * report zero values written. + * + * @param decoder Decoder handle (must not be NULL) + * @param count Number of key/value pairs + * @param keys Array of data kinds to query + * @param values Array of output pointers corresponding to keys + * @param[out] out_written Number of successfully written values (may be NULL) + * @return GHOSTTY_SUCCESS if every query succeeds, or the first error + * + * @ingroup snapshot + */ +GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get_multi( + GhosttySnapshotDecoder decoder, + size_t count, + const GhosttySnapshotDecoderData* keys, + void** values, + size_t* out_written); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* GHOSTTY_VT_SNAPSHOT_H */ diff --git a/vendor/libghostty-vt/include/ghostty/vt/sys.h b/vendor/libghostty-vt/include/ghostty/vt/sys.h index ae905969..c5e5903b 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/sys.h +++ b/vendor/libghostty-vt/include/ghostty/vt/sys.h @@ -125,6 +125,24 @@ typedef bool (*GhosttySysDecodePngFn)( GhosttySysImage* out); /** + * Callback type for secure random bytes. + * + * Fills @p buf with @p len cryptographically secure random bytes. The + * library uses this for secrets, so it must be a real CSPRNG (getrandom, + * arc4random_buf, BCryptGenRandom, crypto.getRandomValues, ...); a + * predictable source is a security hole. + * + * @param userdata The userdata pointer set via GHOSTTY_SYS_OPT_USERDATA + * @param buf Buffer to fill + * @param len Number of bytes to fill + * @return true if the buffer was filled, false if no entropy is available + */ +typedef bool (*GhosttySysRandomSecureFn)( + void* userdata, + uint8_t* buf, + size_t len); + +/** * System option identifiers for ghostty_sys_set(). */ typedef enum GHOSTTY_ENUM_TYPED { @@ -165,6 +183,21 @@ typedef enum GHOSTTY_ENUM_TYPED { * Input type: GhosttySysLogFn (function pointer, or NULL) */ GHOSTTY_SYS_OPT_LOG = 2, + + /** + * Override the secure random source. + * + * By default the library draws secure random bytes from the + * platform (getrandom or arc4random_buf on POSIX, CNG on Windows). + * Targets without one, such as wasm32-freestanding, have no default + * and operations that need entropy fail with GHOSTTY_IO_ERROR until + * this is set. When set, + * it is used instead of the platform source on every target. When + * cleared (NULL value), the platform default is restored. + * + * Input type: GhosttySysRandomSecureFn (function pointer, or NULL) + */ + GHOSTTY_SYS_OPT_RANDOM_SECURE = 3, GHOSTTY_SYS_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttySysOption; diff --git a/vendor/libghostty-vt/include/ghostty/vt/terminal.h b/vendor/libghostty-vt/include/ghostty/vt/terminal.h index 9bb961d3..ad5eec54 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/terminal.h +++ b/vendor/libghostty-vt/include/ghostty/vt/terminal.h @@ -16,6 +16,7 @@ #include <ghostty/vt/modes.h> #include <ghostty/vt/size_report.h> #include <ghostty/vt/grid_ref.h> +#include <ghostty/vt/io.h> #include <ghostty/vt/kitty_graphics.h> #include <ghostty/vt/screen.h> #include <ghostty/vt/point.h> @@ -53,17 +54,17 @@ extern "C" { * * ## Effects * - * By default, the terminal sequence processing with ghostty_terminal_vt_write() - * only process sequences that directly affect terminal state and + * By default, terminal sequence processing with the VT write functions only + * processes sequences that directly affect terminal state and * ignores sequences that have side effect behavior or require responses. * These sequences include things like bell characters, title changes, device * attributes queries, and more. To handle these sequences, the embedder * must configure "effects." * * Effects are callbacks that the terminal invokes in response to VT - * sequences processed during ghostty_terminal_vt_write(). They let the - * embedding application react to terminal-initiated events such as bell - * characters, title changes, device status report responses, and more. + * sequences processed during VT writes. They let the embedding application + * react to terminal-initiated events such as bell characters, title changes, + * device status report responses, and more. * * Each effect is registered with ghostty_terminal_set() using the * corresponding `GhosttyTerminalOption` identifier. A `NULL` value @@ -74,9 +75,10 @@ extern "C" { * back to their own application state without global variables. * You cannot specify different userdata for different callbacks. * - * All callbacks are invoked synchronously during - * ghostty_terminal_vt_write(). Callbacks **must not** call - * ghostty_terminal_vt_write() on the same terminal (no reentrancy). + * All callbacks are invoked synchronously during VT writes. Callbacks + * **must not** call ghostty_terminal_vt_write() or + * ghostty_terminal_vt_write_until_ground() on the same terminal + * (no reentrancy). * And callbacks must be very careful to not block for too long or perform * expensive operations, since they are blocking further IO processing. * @@ -84,16 +86,20 @@ extern "C" { * * | Option | Callback Type | Trigger | * |-----------------------------------------|-----------------------------------|-------------------------------------------| - * | `GHOSTTY_TERMINAL_OPT_WRITE_PTY` | `GhosttyTerminalWritePtyFn` | Query responses written back to the pty | + * | `GHOSTTY_TERMINAL_OPT_WRITE_PTY` | `GhosttyTerminalWritePtyFn` | VT query and mode reports written back to the PTY | * | `GHOSTTY_TERMINAL_OPT_BELL` | `GhosttyTerminalBellFn` | BEL character (0x07) | * | `GHOSTTY_TERMINAL_OPT_TITLE_CHANGED` | `GhosttyTerminalTitleChangedFn` | Title change via OSC 0 / OSC 2 | * | `GHOSTTY_TERMINAL_OPT_PWD_CHANGED` | `GhosttyTerminalPwdChangedFn` | Pwd change via OSC 7 / OSC 9 / OSC 1337 | * | `GHOSTTY_TERMINAL_OPT_ENQUIRY` | `GhosttyTerminalEnquiryFn` | ENQ character (0x05) | * | `GHOSTTY_TERMINAL_OPT_XTVERSION` | `GhosttyTerminalXtversionFn` | XTVERSION query (CSI > q) | - * | `GHOSTTY_TERMINAL_OPT_SIZE` | `GhosttyTerminalSizeFn` | XTWINOPS size query (CSI 14/16/18 t) | + * | `GHOSTTY_TERMINAL_OPT_SIZE` | `GhosttyTerminalSizeFn` | XTWINOPS query (CSI 14/16/18 t) or mode 2048 enable | * | `GHOSTTY_TERMINAL_OPT_COLOR_SCHEME` | `GhosttyTerminalColorSchemeFn` | Color scheme query (CSI ? 996 n) | * | `GHOSTTY_TERMINAL_OPT_DEVICE_ATTRIBUTES`| `GhosttyTerminalDeviceAttributesFn`| Device attributes query (CSI c / > c / = c)| - * | `GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE` | `GhosttyTerminalClipboardWriteFn` | Clipboard write via OSC 52 / OSC 1337 | + * | `GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE` | `GhosttyTerminalClipboardWriteFn` | Clipboard write via OSC 52 / OSC 1337 / OSC 5522 | + * | `GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ` | `GhosttyTerminalClipboardReadFn` | Clipboard read via OSC 52 "?" / OSC 5522 | + * | `GHOSTTY_TERMINAL_OPT_DESKTOP_NOTIFICATION`| `GhosttyTerminalDesktopNotificationFn` | Desktop notification via OSC 9 / OSC 777 | + * | `GHOSTTY_TERMINAL_OPT_PROGRESS_REPORT` | `GhosttyTerminalProgressReportFn` | Progress report via OSC 9;4 | + * | `GHOSTTY_TERMINAL_OPT_UNKNOWN_SEQUENCE` | `GhosttyTerminalUnknownSequenceFn` | Unsupported sequence identifier | * * ### Defining a write_pty callback * @snippet c-vt-effects/src/main.c effects-write-pty @@ -107,6 +113,12 @@ extern "C" { * ### Defining a clipboard_write callback * @snippet c-vt-effects/src/main.c effects-clipboard-write * + * ### Defining a clipboard_read callback + * @snippet c-vt-effects/src/main.c effects-clipboard-read + * + * ### Defining an unknown_sequence callback + * @snippet c-vt-effects/src/main.c effects-unknown-sequence + * * ### Registering effects and processing VT data * @snippet c-vt-effects/src/main.c effects-register * @@ -173,26 +185,6 @@ extern "C" { */ /** - * Terminal initialization options. - * - * @ingroup terminal - */ -typedef struct { - /** Terminal width in cells. Must be greater than zero. */ - uint16_t cols; - - /** Terminal height in cells. Must be greater than zero. */ - uint16_t rows; - - /** Maximum number of lines to keep in scrollback history. */ - size_t max_scrollback; - - // TODO: Consider ABI compatibility implications of this struct. - // We may want to artificially pad it significantly to support - // future options. -} GhosttyTerminalOptions; - -/** * Amount of compression work to perform before returning. * * @ingroup terminal @@ -349,6 +341,88 @@ typedef void (*GhosttyTerminalBellFn)(GhosttyTerminal terminal, void* userdata); /** + * Unsupported terminal sequence tags. + * + * Only APC sequences are currently reported. Additional sequence types may + * be added without changing the callback shape. + * + * @ingroup terminal + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** Application Program Command (APC). */ + GHOSTTY_TERMINAL_UNKNOWN_SEQUENCE_APC = 0, + GHOSTTY_TERMINAL_UNKNOWN_SEQUENCE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttyTerminalUnknownSequenceTag; + +/** + * An unsupported string terminal sequence. + * + * The content is borrowed and valid only for the callback duration. It + * contains the bytes between the sequence introducer and terminator, may + * contain arbitrary binary data, and is not null-terminated. + * + * @ingroup terminal + */ +typedef struct { + /** Whether content was shortened by the byte limit or allocation failure. */ + bool truncated; + + /** Retained sequence content. */ + GhosttyString content; +} GhosttyTerminalUnknownStringSequence; + +/** + * Unsupported terminal sequence value. + * + * @ingroup terminal + */ +typedef union { + /** Application Program Command (APC). */ + GhosttyTerminalUnknownStringSequence apc; + + /** + * Padding for ABI compatibility. Do not use. + * + * 128 bytes leaves room for future structured sequence payloads, such as + * CSI with borrowed parameter, separator, and intermediate arrays, without + * changing the tagged union's ABI. + */ + uint64_t _padding[16]; +} GhosttyTerminalUnknownSequenceValue; + +/** + * An unsupported terminal sequence. + * + * @ingroup terminal + */ +typedef struct { + GhosttyTerminalUnknownSequenceTag tag; + GhosttyTerminalUnknownSequenceValue value; +} GhosttyTerminalUnknownSequence; + +/** + * Callback function type for unsupported terminal sequences. + * + * Called synchronously for normally terminated sequences whose identifier is + * not supported by the active terminal handler. Aborted sequences, malformed + * recognized commands, and explicitly disabled known protocols are ignored. + * + * Capture must also be enabled with a nonzero + * GHOSTTY_TERMINAL_OPT_UNKNOWN_MAX_BYTES value. Installing this callback alone + * does not retain sequence content or allocate memory. + * + * @param terminal The terminal handle + * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA + * @param sequence Borrowed unsupported sequence + * + * @ingroup terminal + */ +typedef void (*GhosttyTerminalUnknownSequenceFn)( + GhosttyTerminal terminal, + void* userdata, + const GhosttyTerminalUnknownSequence* sequence); + +/** * Clipboard destination for a clipboard write. * * Protocol-specific destination identifiers are normalized to these values @@ -389,7 +463,78 @@ typedef struct { } GhosttyClipboardContent; /** - * A semantic, atomic clipboard write. + * Result of a clipboard write reply. + * + * @ingroup terminal + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** The clipboard write completed successfully. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS = 0, + + /** The clipboard write was denied by policy or the user. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_DENIED = 1, + + /** The destination or one or more representations are unsupported. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_UNSUPPORTED = 2, + + /** The clipboard is temporarily unavailable. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_BUSY = 3, + + /** One or more representations contain invalid data. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_INVALID_DATA = 4, + + /** The clipboard write failed due to an I/O error. */ + GHOSTTY_CLIPBOARD_WRITE_RESULT_IO_ERROR = 5, + GHOSTTY_CLIPBOARD_WRITE_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttyClipboardWriteResult; + +/** + * The reply to a clipboard write request. + * + * This is a sized struct; set `size` to `sizeof(GhosttyClipboardWriteReply)`. + * The reply is borrowed only for the duration of the reply call and may be + * freed as soon as it returns. + * + * The result answers the program with the matching protocol status for + * protocols with a write acknowledgement (OSC 5522: DONE, EPERM, ENOSYS, + * EBUSY, EINVAL, EIO); protocols without one (OSC 52, OSC 1337 Copy) + * discard the reply. `remember` is ignored on any result other than + * GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS. + * + * @ingroup terminal + */ +typedef struct { + /** Size of this struct in bytes. */ + size_t size; + + /** Outcome of the write. */ + GhosttyClipboardWriteResult result; + + /** + * Record a session grant so future requests from the same program skip + * the permission prompt. Only honored on success when + * GhosttyClipboardWrite::can_remember is set. + */ + bool remember; +} GhosttyClipboardWriteReply; + +typedef struct GhosttyClipboardWrite GhosttyClipboardWrite; + +/** + * Function type used to answer a clipboard write request. Obtained from + * GhosttyClipboardWrite::reply; see that struct for the contract. + * + * @param write The request being answered + * @param reply The reply, borrowed only for the duration of this call + * + * @ingroup terminal + */ +typedef void (*GhosttyClipboardWriteReplyFn)( + const GhosttyClipboardWrite* write, + const GhosttyClipboardWriteReply* reply); + +/** + * A synchronous request to write clipboard contents. * * This is a sized struct. The callback must only access fields present in the * size reported by `size`. The request, contents array, MIME strings, and @@ -400,9 +545,15 @@ typedef struct { * the destination be cleared. This is distinct from a content entry whose data * has zero length. * + * The write is answered by calling `reply` with this request and a + * GhosttyClipboardWriteReply. This must happen within the clipboard write + * request callback. This struct is only valid during that time. Calling + * `reply` more than once is safely ignored. Returning without replying + * denies the write. + * * @ingroup terminal */ -typedef struct { +struct GhosttyClipboardWrite { /** Size of this struct in bytes. */ size_t size; @@ -414,58 +565,351 @@ typedef struct { /** Number of entries in contents; zero means clear the destination. */ size_t contents_len; -} GhosttyClipboardWrite; + + /** + * Name of the writing program for permission prompts, if the protocol + * carries one. Empty otherwise. + */ + GhosttyString name; + + /** + * True if the terminal already holds a session grant for this request + * The embedder should skip any permission prompt and perform the write. + */ + bool granted; + + /** + * True if the program supplied a session password, so the embedder may + * offer to remember the user's decision through + * GhosttyClipboardWriteReply::remember. When false, remember is ignored. + */ + bool can_remember; + + /** Terminal-owned reply state. Do not access. */ + const void* ctx; + + /** Answer the write; see the struct documentation. */ + GhosttyClipboardWriteReplyFn reply; +}; /** - * Result of a clipboard write callback. + * Callback function type for clipboard_write. * - * Protocols without write acknowledgements, including OSC 52 and iTerm2 - * OSC 1337 Copy, ignore this result. + * The embedder may ask for permission to write or perform the write + * async, but the callback itself is synchronous and the reply function + * must be called during the lifetime of this function. While this callback + * is active the VT stream is paused. + * + * Answer by calling `write->reply(write, &reply)` before returning. See + * GhosttyClipboardWrite for the full contract. + * + * The request may carry an optional program name requesting the write + * and the state of prior permission granted. If `can_remember` is set + * the response may set the `remember` flag and future requests from this + * same program will be "granted" and the embedder can skip permission + * requests. + * + * Clipboard read requests (OSC 52 "?" and OSC 5522 reads) are delivered + * to GhosttyTerminalClipboardReadFn instead. + * + * @param terminal The terminal handle + * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA + * @param write Borrowed atomic clipboard write request + * + * @ingroup terminal + */ +typedef void (*GhosttyTerminalClipboardWriteFn)( + GhosttyTerminal terminal, + void* userdata, + const GhosttyClipboardWrite* write); + +/** + * Result of a clipboard read reply. * * @ingroup terminal */ typedef enum GHOSTTY_ENUM_TYPED { - /** The clipboard write completed successfully. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_SUCCESS = 0, + /** The clipboard was read; the reply carries its contents. */ + GHOSTTY_CLIPBOARD_READ_RESULT_SUCCESS = 0, - /** The clipboard write was denied by policy or the user. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_DENIED = 1, + /** The clipboard read was denied by policy or the user. */ + GHOSTTY_CLIPBOARD_READ_RESULT_DENIED = 1, - /** The destination or one or more representations are unsupported. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_UNSUPPORTED = 2, + /** The embedder cannot read this clipboard. */ + GHOSTTY_CLIPBOARD_READ_RESULT_UNSUPPORTED = 2, /** The clipboard is temporarily unavailable. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_BUSY = 3, + GHOSTTY_CLIPBOARD_READ_RESULT_BUSY = 3, - /** One or more representations contain invalid data. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_INVALID_DATA = 4, + /** Reading the clipboard failed due to an I/O error. */ + GHOSTTY_CLIPBOARD_READ_RESULT_IO_ERROR = 4, + GHOSTTY_CLIPBOARD_READ_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttyClipboardReadResult; - /** The clipboard write failed due to an I/O error. */ - GHOSTTY_CLIPBOARD_WRITE_RESULT_IO_ERROR = 5, - GHOSTTY_CLIPBOARD_WRITE_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, -} GhosttyClipboardWriteResult; +/** + * The reply to a clipboard read request. + * + * This is a sized struct; set `size` to `sizeof(GhosttyClipboardReadReply)`. + * All arrays and the strings they point to are borrowed only for the + * duration of the reply call and may be freed as soon as it returns. + * + * Any result other than GHOSTTY_CLIPBOARD_READ_RESULT_SUCCESS answers the + * program with an empty clipboard (OSC 52) or the matching protocol status + * (OSC 5522: EPERM, ENOSYS, EBUSY, EIO); the other fields are ignored in + * that case. On success, `contents` should carry one representation per + * requested MIME type (GhosttyClipboardRead::mimes) that the clipboard + * has; unrequested representations are ignored. Protocols that carry a + * single text value (OSC 52) use the first entry with a text MIME type + * such as "text/plain". + * + * @ingroup terminal + */ +typedef struct { + /** Size of this struct in bytes. */ + size_t size; + + /** Outcome of the read. */ + GhosttyClipboardReadResult result; + + /** Borrowed array of MIME representations of the clipboard contents. */ + const GhosttyClipboardContent* contents; + + /** Number of entries in contents. */ + size_t contents_len; + + /** + * Borrowed array of all MIME types available on the clipboard. Only + * used when GhosttyClipboardRead::list is set; may be NULL otherwise. + */ + const GhosttyString* available; + + /** Number of entries in available. */ + size_t available_len; + + /** + * Record a session grant so future requests from the same program skip + * the permission prompt. Only honored on success when + * GhosttyClipboardRead::can_remember is set. + */ + bool remember; +} GhosttyClipboardReadReply; + +typedef struct GhosttyClipboardRead GhosttyClipboardRead; /** - * Callback function type for clipboard_write. + * Function type used to answer a clipboard read request. Obtained from + * GhosttyClipboardRead::reply; see that struct for the contract. + * + * @param read The request being answered + * @param reply The reply, borrowed only for the duration of this call + * + * @ingroup terminal + */ +typedef void (*GhosttyClipboardReadReplyFn)( + const GhosttyClipboardRead* read, + const GhosttyClipboardReadReply* reply); + +/** + * A synchronous request to read clipboard contents. * - * Called synchronously for a complete logical clipboard write. Protocol - * details such as OSC 52 selectors, base64 encoding, multipart chunks, - * aliases, and terminators are normalized before this callback is invoked. - * OSC 52 and iTerm2 OSC 1337 Copy writes therefore use the same callback - * shape. OSC 52 clipboard read requests ("?") are always ignored and never - * forwarded to this callback. + * This is a sized struct. The callback must only access fields present in the + * size reported by `size`. The request is borrowed and valid only for the + * callback duration. + * + * The read is answered by calling `reply` with this request and a + * GhosttyClipboardReadReply. This must happen before the callback returns; + * the request is invalid afterwards. Calling `reply` more than once is + * ignored. Returning without replying answers the program with an empty + * clipboard (OSC 52) or EPERM (OSC 5522). + * + * @ingroup terminal + */ +struct GhosttyClipboardRead { + /** Size of this struct in bytes. */ + size_t size; + + /** Clipboard to read. */ + GhosttyClipboardLocation location; + + /** + * Borrowed array of the MIME types the program wants, in order of + * preference. Protocols that only carry text (OSC 52) request + * "text/plain". NULL when mimes_len is zero. + */ + const GhosttyString* mimes; + + /** Number of entries in mimes. */ + size_t mimes_len; + + /** + * True if the program also wants the list of MIME types available on the + * clipboard, delivered through GhosttyClipboardReadReply::available. + */ + bool list; + + /** + * Name of the requesting program for permission prompts, if the protocol + * carries one. Empty otherwise. + */ + GhosttyString name; + + /** + * True if the terminal already holds a session grant for this request + * (kitty clipboard protocol passwords). The embedder should skip any + * permission prompt and serve the read. + * + * Always false when mimes_len is zero: such a request is served + * without a prompt (see the callback docs), so the terminal never + * consults grants for it and a one-time password is preserved for + * the follow-up data read. + */ + bool granted; + + /** + * True if the program supplied a session password, so the embedder may + * offer to remember the user's decision through + * GhosttyClipboardReadReply::remember. When false, remember is ignored. + */ + bool can_remember; + + /** Terminal-owned reply state. Do not access. */ + const void* ctx; + + /** Answer the read; see the struct documentation. */ + GhosttyClipboardReadReplyFn reply; +}; + +/** + * Callback function type for clipboard_read. + * + * Called synchronously when the running program requests clipboard contents + * via OSC 52 with a "?" payload or a Kitty clipboard (OSC 5522) read. + * Answering lets the program read the user's clipboard, so the embedder is + * expected to mediate consent. Because the read is synchronous, an embedder + * that needs to ask the user must block (for example by running a modal + * prompt) until it has an answer; the VT stream waits until the callback + * returns. + * + * Answer by calling `read->reply(read, &reply)` before returning. See + * GhosttyClipboardRead for the full contract. + * + * OSC 5522 requests carry the program's MIME list, name, and password grant + * state; a reply that sets `remember` records a session grant so later + * requests with the same password arrive with `granted` set. Kitty itself + * serves a request for only the targets listing (`list` with no `mimes`) + * without prompting, and embedders are expected to do the same; the + * terminal never consults grants for such requests (`granted` is false + * and one-time passwords are not consumed). + * + * Installing this callback also enables Kitty paste events (mode 5522): + * ghostty_terminal_paste() sends the program an event instead of the text, + * and the program's follow-up read arrives here with `granted` set since + * the user already pasted. See ghostty_terminal_paste(). * * @param terminal The terminal handle * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA - * @param write Borrowed atomic clipboard write request - * @return The result of attempting the clipboard write + * @param read Borrowed clipboard read request * * @ingroup terminal */ -typedef GhosttyClipboardWriteResult (*GhosttyTerminalClipboardWriteFn)( +typedef void (*GhosttyTerminalClipboardReadFn)( GhosttyTerminal terminal, void* userdata, - const GhosttyClipboardWrite* write); + const GhosttyClipboardRead* read); + +/** + * A request to show a desktop notification. + * + * This is a sized struct. The callback must only access fields present in the + * size reported by `size`. Both strings are borrowed and valid only for the + * duration of the callback. + * + * @ingroup terminal + */ +typedef struct { + /** Size of this struct in bytes. */ + size_t size; + + /** Notification title, or an empty string when the protocol omits it. */ + GhosttyString title; + + /** Notification body. */ + GhosttyString body; +} GhosttyTerminalDesktopNotification; + +/** + * Callback function type for desktop notifications. + * + * Called synchronously when the terminal receives OSC 9 or OSC 777. + * + * @param terminal The terminal handle + * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA + * @param notification Borrowed desktop notification request + * + * @ingroup terminal + */ +typedef void (*GhosttyTerminalDesktopNotificationFn)( + GhosttyTerminal terminal, + void* userdata, + const GhosttyTerminalDesktopNotification* notification); + +/** + * State of a terminal progress report. + * + * @ingroup terminal + */ +typedef enum GHOSTTY_ENUM_TYPED { + /** Remove any visible progress indication. */ + GHOSTTY_TERMINAL_PROGRESS_STATE_REMOVE = 0, + + /** Show determinate progress. */ + GHOSTTY_TERMINAL_PROGRESS_STATE_SET = 1, + + /** Show a failed progress state. */ + GHOSTTY_TERMINAL_PROGRESS_STATE_ERROR = 2, + + /** Show indeterminate progress. */ + GHOSTTY_TERMINAL_PROGRESS_STATE_INDETERMINATE = 3, + + /** Show paused progress. */ + GHOSTTY_TERMINAL_PROGRESS_STATE_PAUSE = 4, + GHOSTTY_TERMINAL_PROGRESS_STATE_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, +} GhosttyTerminalProgressState; + +/** + * A progress report emitted by the running program. + * + * This is a sized struct. The callback must only access fields present in the + * size reported by `size`. + * + * @ingroup terminal + */ +typedef struct { + /** Size of this struct in bytes. */ + size_t size; + + /** Literal progress state reported by the running program. */ + GhosttyTerminalProgressState state; + + /** Progress percentage from 0 through 100, or -1 when omitted. */ + int8_t progress; +} GhosttyTerminalProgressReport; + +/** + * Callback function type for progress reports. + * + * Called synchronously when the terminal receives OSC 9;4. + * + * @param terminal The terminal handle + * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA + * @param report Borrowed progress report + * + * @ingroup terminal + */ +typedef void (*GhosttyTerminalProgressReportFn)( + GhosttyTerminal terminal, + void* userdata, + const GhosttyTerminalProgressReport* report); /** * Callback function type for color scheme queries (CSI ? 996 n). @@ -524,16 +968,18 @@ typedef GhosttyString (*GhosttyTerminalEnquiryFn)(GhosttyTerminal terminal, void* userdata); /** - * Callback function type for size queries (XTWINOPS). + * Callback function type for terminal size reports. * - * Called in response to XTWINOPS size queries (CSI 14/16/18 t). + * Called in response to XTWINOPS size queries (CSI 14/16/18 t) and when VT + * input enables in-band size reports (mode 2048). * Return true and fill *out_size with the current terminal geometry, - * or return false to silently ignore the query. + * or return false to suppress the report. * * @param terminal The terminal handle * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA * @param[out] out_size Pointer to store the terminal size information - * @return true if size was filled, false to ignore the query + * @return true if size was filled, false to suppress the XTWINOPS response or + * mode 2048 report * * @ingroup terminal */ @@ -585,9 +1031,9 @@ typedef void (*GhosttyTerminalPwdChangedFn)(GhosttyTerminal terminal, * Callback function type for write_pty. * * Called when the terminal needs to write data back to the pty, for - * example in response to a device status report or mode query. The - * data is only valid for the duration of the call; callers must copy - * it if it needs to persist. + * example in response to a device status report, mode query, or VT-driven + * mode 2048 enable. The data is only valid for the duration of the call; + * callers must copy it if it needs to persist. * * @param terminal The terminal handle * @param userdata The userdata pointer set via GHOSTTY_TERMINAL_OPT_USERDATA @@ -619,6 +1065,24 @@ typedef GhosttyString (*GhosttyTerminalXtversionFn)(GhosttyTerminal terminal, void* userdata); /** + * A terminal mode and boolean value used for mode configuration and queries. + * + * For GHOSTTY_TERMINAL_DATA_MODE, initialize `mode` before calling + * ghostty_terminal_get(). On success, `value` contains the current mode value. + * + * This struct has a frozen layout and will not gain fields in future versions. + * + * @ingroup terminal + */ +typedef struct { + /** Mode to configure or query. */ + GhosttyMode mode; + + /** Value to set, or the current value returned by a query. */ + bool value; +} GhosttyTerminalModeConfig; + +/** * Terminal option identifiers. * * These values are used with ghostty_terminal_set() to configure @@ -636,8 +1100,9 @@ typedef enum GHOSTTY_ENUM_TYPED { /** * Callback invoked when the terminal needs to write data back - * to the pty (e.g. in response to a DECRQM query or device - * status report). Set to NULL to ignore such sequences. + * to the pty (e.g. in response to a DECRQM query, device status + * report, or VT-driven mode 2048 enable). Set to NULL to ignore such + * sequences. * * Input type: GhosttyTerminalWritePtyFn */ @@ -785,12 +1250,13 @@ typedef enum GHOSTTY_ENUM_TYPED { GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_FILE = 16, /** - * Enable or disable Kitty image loading via the temporary file medium. + * Enable Kitty image loading via the temporary file medium, restricted to + * the provided directory. The string data is copied into the terminal. * - * A NULL value pointer is a no-op. Has no effect when Kitty graphics - * are disabled at build time. + * A NULL value pointer disables the temporary file medium. Has no effect + * when Kitty graphics are disabled at build time. * - * Input type: bool* + * Input type: GhosttyString* */ GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_TEMP_FILE = 17, @@ -879,14 +1345,206 @@ typedef enum GHOSTTY_ENUM_TYPED { /** * Callback invoked when the running program performs a clipboard write. - * OSC 52 and iTerm2 OSC 1337 Copy writes are normalized to an atomic set - * of decoded MIME representations. Set to NULL to ignore clipboard writes. - * Clipboard read requests are always ignored; see - * GhosttyTerminalClipboardWriteFn. + * OSC 52, iTerm2 OSC 1337 Copy, and Kitty clipboard (OSC 5522) writes + * are normalized to an atomic set of decoded MIME representations. Set + * to NULL to ignore clipboard writes (Kitty clipboard writes are then + * refused with ENOSYS). Clipboard read requests are delivered to + * GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ instead. * * Input type: GhosttyTerminalClipboardWriteFn */ GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE = 26, + + /** + * Set the maximum scrollback allocation in bytes. + * + * This is an estimate. Internally, libghostty only prunes bytes up + * to a "page"-granularity. A page is the minimum allocated unit of + * grid space within Ghostty. A page at the time of writing these docs + * is about 400KB, so the byte limit will be within this delta. + * + * This works alongside the line limit configuration. If both are set, + * the first-reached limit is used first. Both limits are dependent + * on external state (byte limit can be reached with less lines if + * more styles are used for example, line limit can be reached with + * a narrower terminal viewport). So, they are useful together. + * + * Lowering the limit immediately removes eligible complete historical + * pages. A value of zero disables scrollback and erases retained history. + * A NULL value pointer removes the byte limit. + * + * Input type: size_t* + */ + GHOSTTY_TERMINAL_OPT_SCROLLBACK_MAX_BYTES = 27, + + /** + * Set the maximum number of physical lines retained in scrollback. + * + * This is an estimate. Internally, libghostty only prunes lines up + * to a "page"-granularity. A page is the minimum allocated unit of + * grid space within Ghostty. As a result, the actual available scrollback + * lines will almost always be higher than configured. The magnitude + * of the difference depends on the number of used styles, graphemes, etc. + * since the row-count in a page is dynamic based on that. In general, + * it ranges from dozens to a hundred or so lines. + * + * This works alongside the line limit configuration. If both are set, + * the first-reached limit is used first. Both limits are dependent + * on external state (byte limit can be reached with less lines if + * more styles are used for example, line limit can be reached with + * a narrower terminal viewport). So, they are useful together. + * + * Lowering the limit immediately removes eligible complete historical + * pages. A NULL value pointer removes the line limit. + * + * Input type: size_t* + */ + GHOSTTY_TERMINAL_OPT_SCROLLBACK_MAX_LINES = 28, + + /** + * Callback invoked when the running program requests a desktop + * notification via OSC 9 or OSC 777. Set to NULL to ignore desktop + * notification requests. + * + * Input type: GhosttyTerminalDesktopNotificationFn + */ + GHOSTTY_TERMINAL_OPT_DESKTOP_NOTIFICATION = 29, + + /** + * Callback invoked when the running program reports progress via OSC 9;4. + * Set to NULL to ignore progress reports. + * + * Input type: GhosttyTerminalProgressReportFn + */ + GHOSTTY_TERMINAL_OPT_PROGRESS_REPORT = 30, + + /** + * Set the maximum number of replay-safe VT continuation bytes retained. + * + * Continuation bytes reconstruct an escape sequence or UTF-8 codepoint + * which was unfinished at the end of the most recent + * VT write call. They are used automatically by terminal snapshots and may + * also be exported directly with the continuation APIs. + * + * Tracking is disabled by default. A nonzero value enables tracking and + * sets its byte limit. Passing NULL or a pointer to zero disables tracking. + * Lowering the limit below an already-retained + * continuation, or enabling tracking while the parser is already + * unfinished, makes the current continuation unavailable because earlier + * bytes cannot be reconstructed. Tracking recovers automatically after a + * later write reaches the ground state or contains a fresh replay start. + * + * Input type: size_t* + */ + GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES = 31, + + /** + * Enable window title reports in response to CSI 21 t. + * + * This is disabled by default because a running program can set a title and + * query it back into the pty input stream, potentially injecting commands + * that execute after user interaction. Passing NULL or a pointer to false + * disables title reporting. + * + * Input type: bool* + */ + GHOSTTY_TERMINAL_OPT_TITLE_REPORT = 32, + + /** + * Set the reset default for a terminal mode. + * + * This unconditionally updates both the current value and the value restored + * by a full terminal reset (RIS). + * + * Some recognized modes represent transitions or mirror additional terminal + * state and cannot safely be configured as reset defaults. Those modes return + * GHOSTTY_INVALID_VALUE. A NULL value pointer also returns + * GHOSTTY_INVALID_VALUE. + * + * Input type: GhosttyTerminalModeConfig* + */ + GHOSTTY_TERMINAL_OPT_MODE_DEFAULT = 33, + + /** + * Set the current value of a terminal mode. + * + * This does not change the value restored by a full terminal reset (RIS). + * A NULL value pointer or unknown mode returns GHOSTTY_INVALID_VALUE. + * + * Input type: GhosttyTerminalModeConfig* + */ + GHOSTTY_TERMINAL_OPT_MODE = 34, + + /** + * Callback invoked for unsupported terminal sequence identifiers. Set to + * NULL to ignore unsupported sequences. Capture must also be enabled with + * GHOSTTY_TERMINAL_OPT_UNKNOWN_MAX_BYTES. + * + * Input type: GhosttyTerminalUnknownSequenceFn + */ + GHOSTTY_TERMINAL_OPT_UNKNOWN_SEQUENCE = 35, + + /** + * Set the maximum content bytes retained for each unsupported terminal + * sequence. A NULL value pointer or zero disables capture and prevents + * unknown-sequence callbacks. + * + * When this limit is hit, the unknown sequence callback will still + * be invoked but `truncated` will be set to true. + * + * Input type: size_t* + */ + GHOSTTY_TERMINAL_OPT_UNKNOWN_MAX_BYTES = 36, + + /** + * Set the name of the terminfo entry this terminal runs as, reported + * in response to an XTGETTCAP query for "TN" (e.g. "xterm-256color"). + * + * The string data is copied into the terminal. A NULL value pointer + * clears the name (equivalent to setting an empty string). A name + * longer than 128 bytes returns GHOSTTY_INVALID_VALUE. + * + * If this is unset then we don't report anything for an XTGETTCAP + * TN query, because we don't know what the embedding terminal around + * libghostty is advertising itself as. + * + * Input type: GhosttyString* + */ + GHOSTTY_TERMINAL_OPT_TERMINFO_NAME = 37, + + /** + * Callback invoked when the running program requests clipboard contents + * via OSC 52 with a "?" payload or a Kitty clipboard (OSC 5522) read. The + * read is synchronous and must be answered before the callback returns. + * Set to NULL (the default) to ignore OSC 52 read requests and refuse + * OSC 5522 reads with EPERM. + * + * Input type: GhosttyTerminalClipboardReadFn + */ + GHOSTTY_TERMINAL_OPT_CLIPBOARD_READ = 38, + + /** + * Set the maximum total decoded bytes a single Kitty clipboard protocol + * (OSC 5522) write transaction may accumulate. The limit is captured + * when a transaction begins; an in-flight transaction keeps the limit + * it started with. + * + * Data beyond the limit fails the whole transaction with EFBIG. The + * transaction is discarded, later write-related packets are ignored + * until a new write begins, and nothing reaches the clipboard write + * callback. + * + * Transactions are buffered in memory, so this limit bounds how much + * memory a single write can make the terminal allocate. Pass SIZE_MAX + * to remove the limit. A NULL value pointer reverts to the built-in + * default of 64MiB, the minimum required by the protocol. + * + * This limit doesn't apply to OSC 52 writes, which are bounded by the + * maximum length of an escape sequence instead. + * + * Input type: size_t* + */ + GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE_MAX_BYTES = 39, GHOSTTY_TERMINAL_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyTerminalOption; @@ -998,9 +1656,9 @@ typedef enum GHOSTTY_ENUM_TYPED { /** * The terminal title as set by escape sequences (e.g. OSC 0/2). * - * Returns a borrowed string. The pointer is valid until the next call - * to ghostty_terminal_vt_write() or ghostty_terminal_reset(). An empty - * string (len=0) is returned when no title has been set. + * Returns a borrowed string. The pointer is valid until the next mutating + * terminal call. An empty string (len=0) is returned when no title has been + * set. * * Output type: GhosttyString * */ @@ -1010,9 +1668,9 @@ typedef enum GHOSTTY_ENUM_TYPED { * The terminal's current working directory as set by escape sequences * (e.g. OSC 7). * - * Returns a borrowed string. The pointer is valid until the next call - * to ghostty_terminal_vt_write() or ghostty_terminal_reset(). An empty - * string (len=0) is returned when no pwd has been set. + * Returns a borrowed string. The pointer is valid until the next mutating + * terminal call. An empty string (len=0) is returned when no pwd has been + * set. * * Output type: GhosttyString * */ @@ -1139,12 +1797,13 @@ typedef enum GHOSTTY_ENUM_TYPED { GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_FILE = 27, /** - * Whether the temporary file medium is enabled for Kitty image loading - * on the active screen. + * The directory allowed for Kitty image loading via the temporary file + * medium on the active screen. The string is empty when the medium is + * disabled. * * Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time. * - * Output type: bool * + * Output type: GhosttyString * */ GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_TEMP_FILE = 28, @@ -1199,27 +1858,129 @@ typedef enum GHOSTTY_ENUM_TYPED { GHOSTTY_TERMINAL_DATA_VIEWPORT_ACTIVE = 32, /** + * Whether VT processing encountered a non-gracefully handled error that may + * have prevented a terminal-owned semantic update. + * + * Processing remains best-effort, and ghostty_terminal_reset() does not + * clear it. Gracefully handled protocol failures, configured limits, + * malformed or unsupported input, and failures limited to external effects + * or query responses do not set it. + * + * This can't currently be unset. This is purely informational to consumers + * if there was some error that happened at some point during VT processing. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_VT_PROCESSING_ERROR = 33, + + /** + * The configured maximum scrollback allocation in bytes. + * + * This always reports the primary screen's configured value, including + * while an alternate screen is active. Returns GHOSTTY_NO_VALUE when the + * configured byte limit is unlimited. + * + * Output type: size_t * + */ + GHOSTTY_TERMINAL_DATA_SCROLLBACK_MAX_BYTES = 34, + + /** + * The configured maximum number of physical scrollback lines. + * + * This always reports the primary screen's configured value, including + * while an alternate screen is active. Returns GHOSTTY_NO_VALUE when the + * configured line limit is unlimited. + * + * Output type: size_t * + */ + GHOSTTY_TERMINAL_DATA_SCROLLBACK_MAX_LINES = 35, + + /** + * The configured maximum retained VT continuation size in bytes. + * + * A value of zero means continuation tracking is disabled. This reports the + * configured limit even when a current unfinished continuation is + * temporarily unavailable. + * + * Output type: size_t * + */ + GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES = 36, + + /** + * Get the current value of a terminal mode. + * + * The caller must initialize the `mode` field. On success, the `value` field + * is updated with the current value. A NULL pointer or unknown mode returns + * GHOSTTY_INVALID_VALUE. + * + * Input/output type: GhosttyTerminalModeConfig * + */ + GHOSTTY_TERMINAL_DATA_MODE = 37, + + /** + * Whether VT processing is at ground. + * + * Ground is when the stream isn't in the middle of any type of sequence: + * UTF-8, ESC, CSI, OSC, etc. It is the stateless point of the stream. + * + * This is useful to know because it is a point at which you can + * safely insert out-of-band VT sequences. For example, while reading + * from a pty if you want to make your own changes, you can wait until + * the pty input reaches ground, then write yours. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_VT_GROUND = 38, + + /** + * Whether the cursor is currently at a semantic shell prompt or input area. + * + * This depends on semantic prompt markers such as OSC 133. Returns false + * when semantic prompt information is unavailable or the alternate screen + * is active. + * + * Output type: bool * + */ + GHOSTTY_TERMINAL_DATA_CURSOR_AT_PROMPT = 39, + + /** + * The configured maximum decoded bytes per Kitty clipboard protocol + * (OSC 5522) write transaction. See + * GHOSTTY_TERMINAL_OPT_CLIPBOARD_WRITE_MAX_BYTES. + * + * Output type: size_t * + */ + GHOSTTY_TERMINAL_DATA_CLIPBOARD_WRITE_MAX_BYTES = 40, + + /** * Whether xterm modifyOtherKeys mode 2 is enabled. * * Output type: bool * */ - GHOSTTY_TERMINAL_DATA_MODIFY_OTHER_KEYS = 33, + GHOSTTY_TERMINAL_DATA_MODIFY_OTHER_KEYS = 41, + GHOSTTY_TERMINAL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyTerminalData; /** * Create a new terminal instance. * + * The terminal starts with various reasonable defaults e.g. around + * scrollback limits. Use ghostty_terminal_set() to change any options + * prior to using the terminal. + * * @param allocator Pointer to allocator, or NULL to use the default allocator * @param terminal Pointer to store the created terminal handle - * @param options Terminal initialization options + * @param cols Terminal width in cells (must be greater than zero) + * @param rows Terminal height in cells (must be greater than zero) * @return GHOSTTY_SUCCESS on success, or an error code on failure * * @ingroup terminal */ GHOSTTY_API GhosttyResult ghostty_terminal_new(const GhosttyAllocator* allocator, - GhosttyTerminal* terminal, - GhosttyTerminalOptions options); + GhosttyTerminal* terminal, + uint16_t cols, + uint16_t rows); /** * Free a terminal instance. @@ -1280,11 +2041,13 @@ GHOSTTY_API GhosttyResult ghostty_terminal_resize(GhosttyTerminal terminal, * write_pty callback and userdata pointer. The value is passed * directly for pointer types (callbacks, userdata) or as a pointer * to the value for non-pointer types (e.g. GhosttyString*). - * NULL clears the option to its default. + * The behavior of a NULL value is specific to each option and is + * documented by the corresponding GhosttyTerminalOption value. * - * Callbacks are invoked synchronously during ghostty_terminal_vt_write(). - * Callbacks must not call ghostty_terminal_vt_write() on the same - * terminal (no reentrancy). + * Callbacks are invoked synchronously during VT writes. Callbacks must not + * call ghostty_terminal_vt_write() or + * ghostty_terminal_vt_write_until_ground() on the same terminal + * (no reentrancy). * * @param terminal The terminal handle (may be NULL, in which case this is a no-op) * @param option The option to set @@ -1323,6 +2086,130 @@ GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal, size_t len); /** + * Write VT-encoded data, but only the shortest prefix needed to reach ground. + * + * Ground is when the stream isn't in the middle of any type of sequence: + * UTF-8, ESC, CSI, OSC, etc. It is the stateless point of the stream. + * + * This is useful to know because it is a point at which you can + * safely insert out-of-band VT sequences. For example, while reading + * from a pty if you want to make your own changes, you can wait until + * the pty input reaches ground, then write yours. + * + * If the stream is already at ground then this consumes nothing and returns + * GHOSTTY_SUCCESS. On success, out_consumed is the number of bytes consumed + * before reaching ground, including the byte that reaches it. + * GHOSTTY_NO_VALUE means the full slice was consumed without reaching ground. + * + * @param terminal The terminal handle (must not be NULL) + * @param data Pointer to the data to write, or NULL when len is zero + * @param len Length of the data in bytes + * @param[out] out_consumed Number of bytes consumed (must not be NULL) + * @return GHOSTTY_SUCCESS if ground was reached, GHOSTTY_NO_VALUE if all input + * was consumed without reaching ground, or GHOSTTY_INVALID_VALUE if + * an argument is invalid + * + * @ingroup terminal + */ +GHOSTTY_API GhosttyResult ghostty_terminal_vt_write_until_ground( + GhosttyTerminal terminal, + const uint8_t* data, + size_t len, + size_t* out_consumed); + +/** + * Write the terminal's replay-safe VT continuation to a callback writer. + * + * The continuation is the exact byte suffix needed to reconstruct unfinished + * VT parser or UTF-8 decoder state in an equivalent terminal. It is empty + * when the stream is at ground. The callback is invoked synchronously and + * may be called more than once. It must not call terminal APIs with the same + * terminal handle. + * + * Continuation tracking must have been enabled by setting + * GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to a nonzero value before the + * input that produced the continuation was written. + * + * The caller must serialize this operation with both VT write functions and + * all other access to the same terminal. + * + * @param terminal Terminal to read from (must not be NULL) + * @param writer Destination writer whose write callback must not be NULL + * @return GHOSTTY_SUCCESS on success, GHOSTTY_IO_ERROR if the callback rejects + * a write, GHOSTTY_LIMIT_EXCEEDED if output accounting overflows, or + * GHOSTTY_INVALID_VALUE if an argument is invalid, tracking is + * disabled, or the current continuation is unavailable + * + * @ingroup terminal + */ +GHOSTTY_API GhosttyResult ghostty_terminal_continuation_write( + GhosttyTerminal terminal, + GhosttyWriter writer); + +/** + * Copy the terminal's replay-safe VT continuation into a caller buffer. + * + * Pass NULL for buf with buf_len zero to query the required size. A size query + * returns GHOSTTY_OUT_OF_SPACE and stores the required size in out_written, + * including zero when the stream is at ground. If a non-NULL buffer is too + * small, the function has the same result and reports the full required size. + * Continuation tracking must have been enabled by setting + * GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to a nonzero value before the + * input that produced the continuation was written. + * + * The caller must serialize this operation with all other access to the same + * terminal. + * + * @param terminal Terminal to read from (must not be NULL) + * @param buf Destination buffer, or NULL when buf_len is zero + * @param buf_len Destination buffer capacity in bytes + * @param[out] out_written Bytes written, or required size on + * GHOSTTY_OUT_OF_SPACE (must not be NULL) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_SPACE for a size query or + * insufficient buffer, or GHOSTTY_INVALID_VALUE if an argument is + * invalid, tracking is disabled, or the current continuation is + * unavailable + * + * @ingroup terminal + */ +GHOSTTY_API GhosttyResult ghostty_terminal_continuation_buf( + GhosttyTerminal terminal, + uint8_t* buf, + size_t buf_len, + size_t* out_written); + +/** + * Return an allocated copy of the terminal's replay-safe VT continuation. + * + * The returned bytes are allocated with allocator, or the default allocator + * when allocator is NULL. The caller must release them with ghostty_free(), + * passing the same allocator and returned length. An empty continuation is a + * successful result with *out_ptr set to NULL and *out_len set to zero, + * which can also be passed to ghostty_free(). + * Continuation tracking must have been enabled by setting + * GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES to a nonzero value before the + * input that produced the continuation was written. + * + * The caller must serialize this operation with all other access to the same + * terminal. + * + * @param terminal Terminal to read from (must not be NULL) + * @param allocator Allocator for the output, or NULL for the default allocator + * @param[out] out_ptr Allocated continuation bytes (must not be NULL) + * @param[out] out_len Number of continuation bytes (must not be NULL) + * @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY on allocation + * failure, or GHOSTTY_INVALID_VALUE if an argument is invalid, + * tracking is disabled, or the current continuation is unavailable + * + * @ingroup terminal + */ +GHOSTTY_API GhosttyResult ghostty_terminal_continuation_alloc( + GhosttyTerminal terminal, + const GhosttyAllocator* allocator, + uint8_t** out_ptr, + size_t* out_len); + +/** * Scroll the terminal viewport. * * Scrolls the terminal's viewport according to the given behavior. @@ -1396,41 +2283,6 @@ GHOSTTY_API GhosttyResult ghostty_terminal_compress( GhosttyTerminalCompressionResult* out_result); /** - * Get the current value of a terminal mode. - * - * Returns the value of the mode identified by the given mode. - * - * @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE) - * @param mode The mode identifying the mode to query - * @param[out] out_value On success, set to true if the mode is set, false - * if it is reset - * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the terminal - * is NULL or the mode does not correspond to a known mode - * - * @ingroup terminal - */ -GHOSTTY_API GhosttyResult ghostty_terminal_mode_get(GhosttyTerminal terminal, - GhosttyMode mode, - bool* out_value); - -/** - * Set the value of a terminal mode. - * - * Sets the mode identified by the given mode to the specified value. - * - * @param terminal The terminal handle (NULL returns GHOSTTY_INVALID_VALUE) - * @param mode The mode identifying the mode to set - * @param value true to set the mode, false to reset it - * @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the terminal - * is NULL or the mode does not correspond to a known mode - * - * @ingroup terminal - */ -GHOSTTY_API GhosttyResult ghostty_terminal_mode_set(GhosttyTerminal terminal, - GhosttyMode mode, - bool value); - -/** * Get data from a terminal instance. * * Extracts typed data from the given terminal based on the specified diff --git a/vendor/libghostty-vt/include/ghostty/vt/types.h b/vendor/libghostty-vt/include/ghostty/vt/types.h index 214d2822..059bc5c8 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/types.h +++ b/vendor/libghostty-vt/include/ghostty/vt/types.h @@ -39,18 +39,21 @@ * The Zig side backs all C enums with c_int, so the C declarations * must use int as their underlying type to maintain ABI compatibility. * - * C23 (detected via __STDC_VERSION__ >= 202311L) supports explicit - * enum underlying types with `enum : int { ... }`. For pre-C23 - * compilers, which are free to choose any type that can represent - * all values (C11 §6.7.2.2), we add an INT_MAX sentinel as the last - * entry to force the compiler to use int. + * C++11 and C23 support explicit enum underlying types with + * `enum : int { ... }`. Clang and GCC 13+ also support this syntax as + * an extension in older C language modes, so use it when available. + * + * Other pre-C23 C compilers are free to choose any type that can + * represent all values (C11 §6.7.2.2). For those compilers, we add an + * INT_MAX sentinel as the last entry so the compatible type must be + * able to represent INT_MAX. The exact compatible type and its + * signedness remain implementation-defined in this fallback. * * INT_MAX is used rather than a fixed constant like 0xFFFFFFFF - * because enum constants must have type int (which is signed). - * Values above INT_MAX overflow signed int and are a constraint - * violation in standard C; compilers that accept them interpret them - * as negative values via two's complement, which can collide with - * legitimate negative enum values. + * because enum constants must have type int in pre-C23 C. Values above + * INT_MAX are a constraint violation there; compilers that accept them + * may interpret them as negative values via two's complement, which can + * collide with legitimate negative enum values. * * Usage: * @code @@ -61,7 +64,18 @@ * } Foo; * @endcode */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#if defined(__cplusplus) && \ + (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700)) +#define GHOSTTY_ENUM_TYPED : int +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define GHOSTTY_ENUM_TYPED : int +#elif defined(__clang__) + #if __has_extension(c_fixed_enum) + #define GHOSTTY_ENUM_TYPED : int + #else + #define GHOSTTY_ENUM_TYPED + #endif +#elif defined(__GNUC__) && __GNUC__ >= 13 #define GHOSTTY_ENUM_TYPED : int #else #define GHOSTTY_ENUM_TYPED @@ -82,6 +96,16 @@ typedef enum GHOSTTY_ENUM_TYPED { GHOSTTY_OUT_OF_SPACE = -3, /** The requested value has no value */ GHOSTTY_NO_VALUE = -4, + /** Operation failed while reading from or writing to external I/O */ + GHOSTTY_IO_ERROR = -5, + /** Operation failed because encoded input exceeded a configured limit */ + GHOSTTY_LIMIT_EXCEEDED = -6, + /** + * Operation was rejected by a safety check (e.g. pasted text that could + * inject commands). Nothing was done. Confirm with the user and retry + * with the operation's allow flag set. + */ + GHOSTTY_REJECTED = -7, GHOSTTY_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE, } GhosttyResult; @@ -95,6 +119,13 @@ typedef enum GHOSTTY_ENUM_TYPED { typedef struct GhosttyTerminalImpl* GhosttyTerminal; /** + * Opaque handle to an incremental terminal snapshot decoder. + * + * @ingroup snapshot + */ +typedef struct GhosttySnapshotDecoderImpl* GhosttySnapshotDecoder; + +/** * Opaque handle to a tracked grid reference. * * A tracked grid reference is owned by the caller and must be freed with @@ -158,6 +189,19 @@ typedef struct GhosttyRenderStateRowIteratorImpl* GhosttyRenderStateRowIterator; typedef struct GhosttyRenderStateRowCellsImpl* GhosttyRenderStateRowCells; /** + * Opaque handle to a terminal search. + * + * A search is bound to the terminal it was created with. It borrows the + * terminal, so it never frees it, and the search must be freed with + * ghostty_search_free(). If the terminal is freed first, the search + * detects this: calls that need the terminal fail cleanly and the + * search can still be freed. + * + * @ingroup search + */ +typedef struct GhosttySearchImpl* GhosttySearch; + +/** * Opaque handle to an SGR parser instance. * * This handle represents an SGR (Select Graphic Rendition) parser that can @@ -218,6 +262,8 @@ typedef enum GHOSTTY_ENUM_TYPED { * * The memory is not owned by this struct. The pointer is only valid * for the lifetime documented by the API that produces or consumes it. + * Empty strings produced by the library have a non-NULL pointer to valid + * storage. */ typedef struct { /** Pointer to the string bytes. */ @@ -294,33 +340,64 @@ typedef struct { * opts.trim = true; * @endcode */ +#ifdef __cplusplus +#define GHOSTTY_INIT_SIZED(type) \ + ([]() noexcept { \ + type value{}; \ + value.size = sizeof(value); \ + return value; \ + }()) +#else #define GHOSTTY_INIT_SIZED(type) \ ((type){ .size = sizeof(type) }) +#endif /** - * Return a pointer to a null-terminated JSON string describing the - * layout of every C API struct for the current target. + * Return the versioned libghostty-vt C type manifest for the current target. + * + * The manifest defines all the public types available in the linked + * build. The types contain their layouts, enum values, union fields, and more. + * + * Language bindings, such as WebAssembly hosts, should obtain offsets, + * sizes, alignments, array shapes, enum constants, and tagged-union arms from + * this manifest rather than hardcoding them. Consumers should reject unknown + * schema versions and verify the descriptors they require at initialization. + * + * Packed type descriptors define fields using `lsb` and `width`. `lsb` is + * relative to bit zero of the containing numerical value; for nested packed + * layouts it is relative to the immediate containing field. Tagged packed + * unions select an inline arm layout using the named tag field. These layouts + * describe the current linked build and are not a cross-version stability + * promise. * - * This is primarily useful for language bindings that can't easily - * set C struct fields and need to do so via byte offsets. For example, - * WebAssembly modules can't share struct definitions with the host. + * The formal format is defined by the + * <a href="types.schema.json">libghostty-vt ABI manifest JSON Schema</a>. * * Example (abbreviated): * @code{.json} * { - * "GhosttyMouseEncoderSize": { - * "size": 40, - * "align": 8, - * "fields": { - * "size": { "offset": 0, "size": 8, "type": "u64" }, - * "screen_width": { "offset": 8, "size": 4, "type": "u32" }, - * "screen_height": { "offset": 12, "size": 4, "type": "u32" }, - * "cell_width": { "offset": 16, "size": 4, "type": "u32" }, - * "cell_height": { "offset": 20, "size": 4, "type": "u32" }, - * "padding_top": { "offset": 24, "size": 4, "type": "u32" }, - * "padding_bottom": { "offset": 28, "size": 4, "type": "u32" }, - * "padding_right": { "offset": 32, "size": 4, "type": "u32" }, - * "padding_left": { "offset": 36, "size": 4, "type": "u32" } + * "schema": 1, + * "abi": { + * "target": "wasm32", "os": "freestanding", "environment": "none", + * "pointer_size": 4, "usize_size": 4, "max_alignment": 16, + * "endian": "little" + * }, + * "types": { + * "GhosttyRenderStateData": { + * "kind": "enum", "size": 4, "align": 4, + * "underlying": "i32", "prefix": "GHOSTTY_RENDER_STATE_DATA_", + * "values": { "INVALID": 0, "DIRTY": 3, "MAX_VALUE": 2147483647 } + * }, + * "GhosttyStyleColor": { + * "kind": "struct", "size": 16, "align": 8, + * "fields": { + * "tag": { "offset": 0, "size": 4, + * "type": "GhosttyStyleColorTag" }, + * "value": { "offset": 8, "size": 8, + * "type": "GhosttyStyleColorValue", "tag": "tag", + * "arms": { "NONE": null, "PALETTE": "palette", + * "RGB": "rgb" } } + * } * } * } * } diff --git a/vendor/libghostty-vt/include/ghostty/vt/wasm.h b/vendor/libghostty-vt/include/ghostty/vt/wasm.h index e2b63e2c..2f9ff5df 100644 --- a/vendor/libghostty-vt/include/ghostty/vt/wasm.h +++ b/vendor/libghostty-vt/include/ghostty/vt/wasm.h @@ -10,12 +10,12 @@ #ifdef __wasm__ #include <stddef.h> -#include <stdint.h> #include <ghostty/vt/types.h> /** @defgroup wasm WebAssembly Utilities * - * Convenience functions for allocating various types in WebAssembly builds. + * Convenience functions for working with the low-level C ABI in WebAssembly + * builds. * **These are only available the libghostty-vt wasm module.** * * Ghostty relies on pointers to various types for ABI compatibility, and @@ -28,37 +28,64 @@ * your custom allocator. This is a very rare use case in the WebAssembly * world so these are optimized for simplicity. * + * Use ghostty_wasm_alloc() and ghostty_wasm_free() for host-owned scratch + * buffers and ABI values. Dynamic-language hosts can use ghostty_type_json() + * to discover pointer and size_t widths, maximum alignment, byte order, and + * the size and alignment of public C structs. Do not mix allocation families: + * buffers returned by libghostty-vt allocating APIs must still be released + * with ghostty_free(), and opaque handles must be released with their + * type-specific destructor. + * + * ## Memory growth + * + * An exported function may grow Wasm linear memory when it allocates. Numeric + * pointers and handles remain valid, but JavaScript ArrayBuffer, DataView, and + * typed-array objects created before the growth may no longer cover the live + * memory. Reacquire `exports.memory.buffer` immediately before every host-side + * memory access. A host that caches views should recreate them whenever either + * the buffer identity or its byte length changes. + * * ## Example Usage * - * Here's a simple example of using the Wasm utilities with the key encoder: + * Here's a simple example that creates a terminal, writes bytes, and safely + * handles memory growth: * * @code * const { exports } = wasmInstance; - * const view = new DataView(wasmMemory.buffer); - * - * // Create key encoder - * const encoderPtr = exports.ghostty_wasm_alloc_opaque(); - * exports.ghostty_key_encoder_new(null, encoderPtr); - * const encoder = view.getUint32(encoder, true); - * - * // Configure encoder with Kitty protocol flags - * const flagsPtr = exports.ghostty_wasm_alloc_u8(); - * view.setUint8(flagsPtr, 0x1F); - * exports.ghostty_key_encoder_setopt(encoder, 5, flagsPtr); - * - * // Allocate output buffer and size pointer - * const bufferSize = 32; - * const bufPtr = exports.ghostty_wasm_alloc_u8_array(bufferSize); - * const writtenPtr = exports.ghostty_wasm_alloc_usize(); - * - * // Encode the key event - * exports.ghostty_key_encoder_encode( - * encoder, eventPtr, bufPtr, bufferSize, writtenPtr - * ); - * - * // Read encoded output - * const bytesWritten = view.getUint32(writtenPtr, true); - * const encoded = new Uint8Array(wasmMemory.buffer, bufPtr, bytesWritten); + * const memory = exports.memory; + * let cachedBuffer = null; + * let cachedLength = 0; + * let cachedBytes = null; + * + * function bytes() { + * const buffer = memory.buffer; + * if (buffer !== cachedBuffer || buffer.byteLength !== cachedLength) { + * cachedBuffer = buffer; + * cachedLength = buffer.byteLength; + * cachedBytes = new Uint8Array(buffer); + * } + * return cachedBytes; + * } + * + * function check(result) { + * if (result !== 0) throw new Error(`libghostty-vt error: ${result}`); + * } + * + * // One slot can be reused for every opaque-handle constructor. + * const slot = exports.ghostty_wasm_alloc_opaque(); + * if (slot === 0) throw new Error("out of memory"); + * check(exports.ghostty_terminal_new(0, slot, 80, 24)); + * const terminal = exports.ghostty_wasm_take_opaque(slot); + * + * const input = new TextEncoder().encode("Hello, world!"); + * const inputPtr = exports.ghostty_wasm_alloc(input.length); + * if (inputPtr === 0) throw new Error("out of memory"); + * bytes().set(input, inputPtr); // Acquires the current memory after alloc. + * exports.ghostty_terminal_vt_write(terminal, inputPtr, input.length); + * + * exports.ghostty_wasm_free(inputPtr, input.length); + * exports.ghostty_terminal_free(terminal); + * exports.ghostty_wasm_free_opaque(slot); * @endcode * * @remark The code above is pretty ugly! This is the lowest level interface @@ -69,89 +96,62 @@ */ /** - * Allocate an opaque pointer. This can be used for any opaque pointer - * types such as GhosttyKeyEncoder, GhosttyKeyEvent, etc. - * - * @return Pointer to allocated opaque pointer, or NULL if allocation failed - * @ingroup wasm - */ -GHOSTTY_API void** ghostty_wasm_alloc_opaque(void); - -/** - * Free an opaque pointer allocated by ghostty_wasm_alloc_opaque(). - * - * @param ptr Pointer to free, or NULL (NULL is safely ignored) - * @ingroup wasm - */ -GHOSTTY_API void ghostty_wasm_free_opaque(void **ptr); - -/** - * Allocate an array of uint8_t values. + * Allocate caller-owned storage for a Wasm ABI value or scratch buffer. * - * @param len Number of uint8_t elements to allocate - * @return Pointer to allocated array, or NULL if allocation failed - * @ingroup wasm - */ -GHOSTTY_API uint8_t* ghostty_wasm_alloc_u8_array(size_t len); - -/** - * Free an array allocated by ghostty_wasm_alloc_u8_array(). + * The returned address is aligned to the target's maximum C ABI alignment, + * reported as `abi.max_alignment` by ghostty_type_json(). The memory is + * uninitialized. A zero-length request returns NULL. * - * @param ptr Pointer to the array to free, or NULL (NULL is safely ignored) - * @param len Length of the array (must match the length passed to alloc) - * @ingroup wasm - */ -GHOSTTY_API void ghostty_wasm_free_u8_array(uint8_t *ptr, size_t len); - -/** - * Allocate an array of uint16_t values. + * The returned pointer must be released with ghostty_wasm_free() using the + * exact same length. * - * @param len Number of uint16_t elements to allocate - * @return Pointer to allocated array, or NULL if allocation failed + * @param len Number of bytes to allocate + * @return Pointer to allocated storage, or NULL if len is zero or allocation + * failed * @ingroup wasm */ -GHOSTTY_API uint16_t* ghostty_wasm_alloc_u16_array(size_t len); +GHOSTTY_API void* ghostty_wasm_alloc(size_t len); /** - * Free an array allocated by ghostty_wasm_alloc_u16_array(). + * Free storage allocated by ghostty_wasm_alloc(). * - * @param ptr Pointer to the array to free, or NULL (NULL is safely ignored) - * @param len Length of the array (must match the length passed to alloc) + * @param ptr Pointer to free, or NULL (NULL is safely ignored) + * @param len Original allocation length passed to ghostty_wasm_alloc() * @ingroup wasm */ -GHOSTTY_API void ghostty_wasm_free_u16_array(uint16_t *ptr, size_t len); +GHOSTTY_API void ghostty_wasm_free(void *ptr, size_t len); /** - * Allocate a single uint8_t value. + * Allocate an opaque pointer. This can be used for any opaque pointer + * types such as GhosttyKeyEncoder, GhosttyKeyEvent, etc. The allocated slot + * is initialized to NULL and may be reused across constructors. * - * @return Pointer to allocated uint8_t, or NULL if allocation failed + * @return Pointer to allocated opaque pointer, or NULL if allocation failed * @ingroup wasm */ -GHOSTTY_API uint8_t* ghostty_wasm_alloc_u8(void); +GHOSTTY_API void** ghostty_wasm_alloc_opaque(void); /** - * Free a uint8_t allocated by ghostty_wasm_alloc_u8(). + * Free an opaque pointer allocated by ghostty_wasm_alloc_opaque(). * * @param ptr Pointer to free, or NULL (NULL is safely ignored) * @ingroup wasm */ -GHOSTTY_API void ghostty_wasm_free_u8(uint8_t *ptr); +GHOSTTY_API void ghostty_wasm_free_opaque(void **ptr); /** - * Allocate a single size_t value. + * Take an opaque handle from an out-parameter slot. * - * @return Pointer to allocated size_t, or NULL if allocation failed - * @ingroup wasm - */ -GHOSTTY_API size_t* ghostty_wasm_alloc_usize(void); - -/** - * Free a size_t allocated by ghostty_wasm_alloc_usize(). + * Returns the handle currently stored in @p slot and resets the slot to NULL. + * This function does not allocate, free the returned handle, or free the slot. + * Always check the GhosttyResult returned by the function that populated the + * slot before calling this function. * - * @param ptr Pointer to free, or NULL (NULL is safely ignored) + * @param slot Pointer to an opaque out-parameter slot, or NULL + * @return Stored opaque handle, or NULL if slot or its value is NULL * @ingroup wasm */ -GHOSTTY_API void ghostty_wasm_free_usize(size_t *ptr); +GHOSTTY_API void* ghostty_wasm_take_opaque(void **slot); /** @} */ |
