mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-08-05 15:18:40 +00:00
libghostty-vt: add C API for snapshotting functions
Expose terminal snapshot through the libghostty-vt C API and add
a new C example that runs in CI to verify this stuff works!
## Example
```c
size_t continuation_limit = 1024;
assert(ghostty_terminal_set(
terminal,
GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES,
&continuation_limit) == GHOSTTY_SUCCESS);
uint8_t *bytes = NULL;
size_t len = 0;
assert(ghostty_snapshot_encode_alloc(
terminal, NULL, &bytes, &len) == GHOSTTY_SUCCESS);
GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new_buf(
NULL, &decoder, bytes, len) == GHOSTTY_SUCCESS);
GhosttyTerminal restored = NULL;
assert(ghostty_snapshot_decoder_decode(
decoder, &restored) == GHOSTTY_SUCCESS);
ghostty_snapshot_decoder_free(decoder);
ghostty_free(NULL, bytes, len);
```
Streaming decode:
```c
GhosttyReader reader = {
.read = read_snapshot,
.userdata = source,
};
GhosttySnapshotDecoder decoder = NULL;
assert(ghostty_snapshot_decoder_new(
NULL, &decoder, reader) == GHOSTTY_SUCCESS);
GhosttyTerminal terminal = NULL;
assert(ghostty_snapshot_decoder_ready(
decoder, &terminal) == GHOSTTY_SUCCESS);
GhosttyResult result;
while ((result = ghostty_snapshot_decoder_next(decoder)) ==
GHOSTTY_SUCCESS) {
size_t rows = 0;
assert(ghostty_snapshot_decoder_get(
decoder,
GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS,
&rows) == GHOSTTY_SUCCESS);
render(terminal);
}
assert(result == GHOSTTY_NO_VALUE);
```
This commit is contained in:
@@ -31,12 +31,14 @@
|
||||
* - @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 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 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:
|
||||
@@ -140,6 +142,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>
|
||||
@@ -153,6 +156,7 @@ extern "C" {
|
||||
#include <ghostty/vt/screen.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>
|
||||
|
||||
|
||||
107
include/ghostty/vt/io.h
Normal file
107
include/ghostty/vt/io.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @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>
|
||||
|
||||
/** @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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* GHOSTTY_VT_IO_H */
|
||||
525
include/ghostty/vt/snapshot.h
Normal file
525
include/ghostty/vt/snapshot.h
Normal file
@@ -0,0 +1,525 @@
|
||||
/**
|
||||
* @file snapshot.h
|
||||
*
|
||||
* Encode and restore complete terminal snapshots.
|
||||
*/
|
||||
|
||||
#ifndef GHOSTTY_VT_SNAPSHOT_H
|
||||
#define GHOSTTY_VT_SNAPSHOT_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, authenticated record stream. Its READY checkpoint
|
||||
* contains enough state to render and resume the terminal, including any
|
||||
* unfinished VT parser input. Older scrollback pages follow READY and the
|
||||
* FINISH checkpoint authenticates the complete snapshot.
|
||||
*
|
||||
* End-of-file before an operation's required READY or FINISH checkpoint 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 -----------------+
|
||||
* | BLAKE3-256 of every preceding byte | ready() returns here
|
||||
* +----------------- HISTORY ----------------+ repeated per screen
|
||||
* | scrollback manifest |
|
||||
* +------------------ PAGE ------------------+ next() consumes one page
|
||||
* | older screen rows |
|
||||
* +------------------ FINISH ----------------+
|
||||
* | BLAKE3-256 of every preceding byte | next() returns NO_VALUE
|
||||
* +------------------------------------------+
|
||||
* | trailing transport bytes (not consumed) |
|
||||
* +------------------------------------------+
|
||||
* @endcode
|
||||
*
|
||||
* READY authenticates the renderable prefix through CONTINUATION. FINISH
|
||||
* authenticates READY and every history record as well as the earlier prefix.
|
||||
* Thus record CRC32C detects local corruption while the BLAKE3 checkpoints
|
||||
* also bind the ordering and completeness of the record stream.
|
||||
*
|
||||
* 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 an input validation limit only. It does not configure continuation
|
||||
* tracking on a terminal returned by the decoder.
|
||||
*
|
||||
* Input type: size_t *
|
||||
*/
|
||||
GHOSTTY_SNAPSHOT_DECODER_OPT_MAX_CONTINUATION_BYTES = 0,
|
||||
|
||||
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 authenticated 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,
|
||||
|
||||
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 checkpoint.
|
||||
* 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 checkpoint 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 authenticate 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, but terminal continuation
|
||||
* tracking is disabled; GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES returns
|
||||
* zero. The decoder's continuation option is an input limit, not terminal
|
||||
* runtime policy.
|
||||
*
|
||||
* 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 authenticates 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
|
||||
* authenticated 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 authenticate 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 and GHOSTTY_TERMINAL_DATA_CONTINUATION_MAX_BYTES
|
||||
* returns zero. 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 */
|
||||
@@ -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>
|
||||
@@ -1027,6 +1028,26 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
* 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
|
||||
* ghostty_terminal_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,
|
||||
GHOSTTY_TERMINAL_OPT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyTerminalOption;
|
||||
|
||||
@@ -1376,6 +1397,17 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
* 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,
|
||||
GHOSTTY_TERMINAL_DATA_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyTerminalData;
|
||||
|
||||
@@ -1501,6 +1533,97 @@ GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal,
|
||||
const uint8_t* data,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* 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 ghostty_terminal_vt_write()
|
||||
* 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 zero-length allocation.
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -82,6 +82,10 @@ 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,
|
||||
GHOSTTY_RESULT_MAX_VALUE = GHOSTTY_ENUM_MAX_VALUE,
|
||||
} GhosttyResult;
|
||||
|
||||
@@ -94,6 +98,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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user