libghostty-vt 0.0.0
Loading...
Searching...
No Matches
c-vt-snapshot/src/main.c

Encode a terminal's full state and restore it three ways: in one shot, with scrollback kept compressed, and page by page. The page by page path gives you a terminal you can draw before the old history arrives.

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} BufferReader;
// GhosttyReader callbacks are synchronous. A successful zero-byte read is
// permanent EOF; returning false would report an I/O error.
static bool buffer_read(void *userdata,
uint8_t *buffer,
size_t capacity,
size_t *out_read) {
BufferReader *reader = userdata;
size_t remaining = reader->len - reader->offset;
size_t count = remaining < capacity ? remaining : capacity;
// Deliberately return short reads to demonstrate that the decoder retries.
if (count > 64) count = 64;
memcpy(buffer, reader->data + reader->offset, count);
reader->offset += count;
*out_read = count;
return true;
}
int main(void) {
GhosttyResult result;
GhosttyTerminal source = NULL;
// A wide, shallow screen fills backing pages quickly enough to leave older
// PAGE records after READY for the incremental decoder to demonstrate.
result = ghostty_terminal_new(NULL, &source, 215, 2);
assert(result == GHOSTTY_SUCCESS);
// Snapshot encoding requires continuation tracking if the input can stop
// partway through a sequence, and it has to be enabled before feeding
// input. The limit bounds unfinished VT sequence retention.
const size_t continuation_limit = 1024;
source,
&continuation_limit);
assert(result == GHOSTTY_SUCCESS);
// Keep enough scrollback to demonstrate incremental history restoration.
size_t unlimited = SIZE_MAX;
assert(result == GHOSTTY_SUCCESS);
const char *line = "snapshot history line\r\n";
for (size_t i = 0; i < 1000; i++) {
source, (const uint8_t *)line, strlen(line));
}
// Leave an SGR sequence unfinished so its continuation is snapshotted too.
const char *unfinished = "\x1b[31";
source, (const uint8_t *)unfinished, strlen(unfinished));
uint8_t *snapshot = NULL;
size_t snapshot_len = 0;
source, NULL, &snapshot, &snapshot_len);
assert(result == GHOSTTY_SUCCESS);
printf("encoded %zu snapshot bytes\n", snapshot_len);
GhosttySnapshotDecoder full_decoder = NULL;
NULL, &full_decoder, snapshot, snapshot_len);
assert(result == GHOSTTY_SUCCESS);
GhosttyTerminal full_terminal = NULL;
result = ghostty_snapshot_decoder_decode(full_decoder, &full_terminal);
assert(result == GHOSTTY_SUCCESS);
ghostty_terminal_free(full_terminal);
GhosttySnapshotDecoder compressed_decoder = NULL;
NULL, &compressed_decoder, snapshot, snapshot_len);
assert(result == GHOSTTY_SUCCESS);
// Options must be set before decoding starts. With this set, each history
// page is compressed as soon as it is restored, so the full scrollback is
// never held uncompressed in memory.
const bool compress_history = true;
compressed_decoder,
&compress_history);
assert(result == GHOSTTY_SUCCESS);
GhosttyTerminal compressed_terminal = NULL;
compressed_decoder, &compressed_terminal);
assert(result == GHOSTTY_SUCCESS);
ghostty_snapshot_decoder_free(compressed_decoder);
ghostty_terminal_free(compressed_terminal);
BufferReader reader_state = {
.data = snapshot,
.len = snapshot_len,
.offset = 0,
};
GhosttyReader reader = {
.read = buffer_read,
.userdata = &reader_state,
};
GhosttySnapshotDecoder incremental_decoder = NULL;
NULL, &incremental_decoder, reader);
assert(result == GHOSTTY_SUCCESS);
// READY returns a validated, renderable terminal before old history.
GhosttyTerminal incremental_terminal = NULL;
incremental_decoder, &incremental_terminal);
assert(result == GHOSTTY_SUCCESS);
uint64_t history_rows = 0;
incremental_decoder,
&history_rows);
assert(result == GHOSTTY_SUCCESS);
printf("snapshot advertises %llu primary history rows\n",
(unsigned long long)history_rows);
size_t page_count = 0;
while ((result = ghostty_snapshot_decoder_next(incremental_decoder)) ==
// Each call decodes either a history page or the pixels of one Kitty
// graphics image. This terminal has no images, but a real client should
// check the kind before reading the page values.
incremental_decoder,
&kind);
assert(result == GHOSTTY_SUCCESS);
uint32_t image_id = 0;
incremental_decoder,
&image_id);
assert(result == GHOSTTY_SUCCESS);
printf("restored the pixels of image %u\n", image_id);
continue;
}
size_t rows = 0;
uint32_t remaining = 0;
const GhosttySnapshotDecoderData keys[] = {
};
void *values[] = {&screen, &rows, &remaining};
size_t written = 0;
incremental_decoder,
sizeof(keys) / sizeof(keys[0]),
keys,
values,
&written);
assert(result == GHOSTTY_SUCCESS);
assert(written == sizeof(keys) / sizeof(keys[0]));
printf("restored %zu rows to screen %d (%u pages remain)\n",
rows, (int)screen, remaining);
page_count++;
}
// NO_VALUE means FINISH validated successfully and is idempotent.
assert(result == GHOSTTY_NO_VALUE);
assert(page_count > 0);
assert(ghostty_snapshot_decoder_next(incremental_decoder) ==
ghostty_snapshot_decoder_free(incremental_decoder);
ghostty_terminal_free(incremental_terminal);
ghostty_free(NULL, snapshot, snapshot_len);
return 0;
}
GHOSTTY_API void ghostty_free(const GhosttyAllocator *allocator, uint8_t *ptr, size_t len)
GhosttyResult
Definition types.h:143
@ GHOSTTY_NO_VALUE
Definition types.h:160
@ GHOSTTY_SUCCESS
Definition types.h:145
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new_buf(const GhosttyAllocator *allocator, GhosttySnapshotDecoder *decoder, const uint8_t *ptr, size_t len)
GHOSTTY_API void ghostty_snapshot_decoder_free(GhosttySnapshotDecoder decoder)
GHOSTTY_API GhosttyResult ghostty_snapshot_encode_alloc(GhosttyTerminal terminal, const GhosttyAllocator *allocator, uint8_t **out_ptr, size_t *out_len)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get_multi(GhosttySnapshotDecoder decoder, size_t count, const GhosttySnapshotDecoderData *keys, void **values, size_t *out_written)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_decode(GhosttySnapshotDecoder decoder, GhosttyTerminal *terminal)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_set(GhosttySnapshotDecoder decoder, GhosttySnapshotDecoderOption option, const void *value)
GhosttySnapshotDecoderProgressKind
Definition snapshot.h:155
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_next(GhosttySnapshotDecoder decoder)
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_get(GhosttySnapshotDecoder decoder, GhosttySnapshotDecoderData data, void *out)
struct GhosttySnapshotDecoderImpl * GhosttySnapshotDecoder
Definition types.h:203
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_new(const GhosttyAllocator *allocator, GhosttySnapshotDecoder *decoder, GhosttyReader reader)
GhosttySnapshotDecoderData
Definition snapshot.h:178
GHOSTTY_API GhosttyResult ghostty_snapshot_decoder_ready(GhosttySnapshotDecoder decoder, GhosttyTerminal *terminal)
@ GHOSTTY_SNAPSHOT_DECODER_OPT_COMPRESS_HISTORY
Definition snapshot.h:125
@ GHOSTTY_SNAPSHOT_DECODER_PROGRESS_KIND_IMAGE
Definition snapshot.h:167
@ GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_IMAGE_ID
Definition snapshot.h:306
@ GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_ROWS
Definition snapshot.h:244
@ GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_KIND
Definition snapshot.h:295
@ GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_SCREEN
Definition snapshot.h:233
@ GHOSTTY_SNAPSHOT_DECODER_DATA_HISTORY_ROWS_PRIMARY
Definition snapshot.h:210
@ GHOSTTY_SNAPSHOT_DECODER_DATA_PROGRESS_REMAINING
Definition snapshot.h:255
GHOSTTY_API GhosttyResult ghostty_terminal_set(GhosttyTerminal terminal, GhosttyTerminalOption option, const void *value)
struct GhosttyTerminalImpl * GhosttyTerminal
Definition types.h:196
GHOSTTY_API void ghostty_terminal_free(GhosttyTerminal terminal)
GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal, const uint8_t *data, size_t len)
GhosttyTerminalScreen
Definition terminal.h:177
GHOSTTY_API GhosttyResult ghostty_terminal_new(const GhosttyAllocator *allocator, GhosttyTerminal *terminal, uint16_t cols, uint16_t rows)
@ GHOSTTY_TERMINAL_OPT_SCROLLBACK_MAX_BYTES
Definition terminal.h:2339
@ GHOSTTY_TERMINAL_OPT_CONTINUATION_MAX_BYTES
Definition terminal.h:2403