bd44b5aa08
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
233 lines
7.8 KiB
C
233 lines
7.8 KiB
C
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ghostty/vt.h>
|
|
|
|
//! [kitty-graphics-decode-png]
|
|
/**
|
|
* Minimal PNG decoder callback for the sys interface.
|
|
*
|
|
* A real implementation would use a PNG library (libpng, stb_image, etc.)
|
|
* to decode the PNG data. This example uses a hardcoded 1x1 red pixel
|
|
* since we know exactly what image we're sending.
|
|
*
|
|
* WARNING: This is only an example for providing a callback, it DOES NOT
|
|
* actually decode the PNG it is passed. It hardcodes a response.
|
|
*/
|
|
bool decode_png(void* userdata,
|
|
const GhosttyAllocator* allocator,
|
|
const uint8_t* data,
|
|
size_t data_len,
|
|
GhosttySysImage* out) {
|
|
int* count = (int*)userdata;
|
|
(*count)++;
|
|
printf(" decode_png called (size=%zu, call #%d)\n", data_len, *count);
|
|
|
|
/* Allocate RGBA pixel data through the provided allocator. */
|
|
const size_t pixel_len = 4; /* 1x1 RGBA */
|
|
uint8_t* pixels = ghostty_alloc(allocator, pixel_len);
|
|
if (!pixels) return false;
|
|
|
|
/* Fill with red (R=255, G=0, B=0, A=255). */
|
|
pixels[0] = 255;
|
|
pixels[1] = 0;
|
|
pixels[2] = 0;
|
|
pixels[3] = 255;
|
|
|
|
out->width = 1;
|
|
out->height = 1;
|
|
out->data = pixels;
|
|
out->data_len = pixel_len;
|
|
return true;
|
|
}
|
|
//! [kitty-graphics-decode-png]
|
|
|
|
//! [kitty-graphics-write-pty]
|
|
/**
|
|
* write_pty callback to capture terminal responses.
|
|
*
|
|
* The Kitty graphics protocol sends an APC response back to the pty
|
|
* when an image is loaded (unless suppressed with q=2).
|
|
*/
|
|
void on_write_pty(GhosttyTerminal terminal,
|
|
void* userdata,
|
|
const uint8_t* data,
|
|
size_t len) {
|
|
(void)terminal;
|
|
(void)userdata;
|
|
printf(" response (%zu bytes): ", len);
|
|
fwrite(data, 1, len, stdout);
|
|
printf("\n");
|
|
}
|
|
//! [kitty-graphics-write-pty]
|
|
|
|
//! [kitty-graphics-main]
|
|
int main() {
|
|
/* Install the PNG decoder via the sys interface. */
|
|
int decode_count = 0;
|
|
ghostty_sys_set(GHOSTTY_SYS_OPT_USERDATA, &decode_count);
|
|
ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, (const void*)decode_png);
|
|
|
|
/* Create a terminal with Kitty graphics enabled. */
|
|
GhosttyTerminal terminal = NULL;
|
|
GhosttyTerminalOptions opts = {
|
|
.cols = 80,
|
|
.rows = 24,
|
|
.max_scrollback = 0,
|
|
};
|
|
if (ghostty_terminal_new(NULL, &terminal, opts) != GHOSTTY_SUCCESS) {
|
|
fprintf(stderr, "Failed to create terminal\n");
|
|
return 1;
|
|
}
|
|
|
|
/* Set cell pixel dimensions so kitty graphics can compute grid sizes. */
|
|
ghostty_terminal_resize(terminal, 80, 24, 8, 16);
|
|
|
|
/* Set a storage limit to enable Kitty graphics. */
|
|
uint64_t storage_limit = 64 * 1024 * 1024; /* 64 MiB */
|
|
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT,
|
|
&storage_limit);
|
|
|
|
/* Install write_pty to see the protocol response. */
|
|
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_WRITE_PTY,
|
|
(const void*)on_write_pty);
|
|
|
|
/*
|
|
* Send a Kitty graphics command with an inline 1x1 PNG image.
|
|
*
|
|
* The escape sequence is:
|
|
* ESC _G a=T,f=100,q=1; <base64 PNG data> ESC \
|
|
*
|
|
* Where:
|
|
* a=T — transmit and display
|
|
* f=100 — PNG format
|
|
* q=1 — request a response (q=0 would suppress it)
|
|
*/
|
|
printf("Sending Kitty graphics PNG image:\n");
|
|
const char* kitty_cmd =
|
|
"\x1b_Ga=T,f=100,q=1;"
|
|
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA"
|
|
"DUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg=="
|
|
"\x1b\\";
|
|
ghostty_terminal_vt_write(terminal, (const uint8_t*)kitty_cmd,
|
|
strlen(kitty_cmd));
|
|
|
|
printf("PNG decode calls: %d\n", decode_count);
|
|
|
|
/* Query the kitty graphics storage to verify the image was stored. */
|
|
GhosttyKittyGraphics graphics = NULL;
|
|
if (ghostty_terminal_get(terminal, GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS,
|
|
&graphics) != GHOSTTY_SUCCESS || !graphics) {
|
|
fprintf(stderr, "Failed to get kitty graphics storage\n");
|
|
return 1;
|
|
}
|
|
printf("\nKitty graphics storage is available.\n");
|
|
|
|
/*
|
|
* The storage-wide generation changes on every image/placement
|
|
* mutation. Renderers can compare it against the value from the
|
|
* previous frame: if unchanged, placement iteration and image
|
|
* staleness checks can be skipped entirely.
|
|
*/
|
|
uint64_t generation = 0;
|
|
ghostty_kitty_graphics_get(graphics, GHOSTTY_KITTY_GRAPHICS_DATA_GENERATION,
|
|
&generation);
|
|
printf("Storage generation: %llu\n", (unsigned long long)generation);
|
|
|
|
/* Iterate placements to find the image ID. */
|
|
GhosttyKittyGraphicsPlacementIterator iter = NULL;
|
|
if (ghostty_kitty_graphics_placement_iterator_new(NULL, &iter) != GHOSTTY_SUCCESS) {
|
|
fprintf(stderr, "Failed to create placement iterator\n");
|
|
return 1;
|
|
}
|
|
if (ghostty_kitty_graphics_get(graphics,
|
|
GHOSTTY_KITTY_GRAPHICS_DATA_PLACEMENT_ITERATOR, &iter) != GHOSTTY_SUCCESS) {
|
|
fprintf(stderr, "Failed to get placement iterator\n");
|
|
return 1;
|
|
}
|
|
|
|
int placement_count = 0;
|
|
while (ghostty_kitty_graphics_placement_next(iter)) {
|
|
placement_count++;
|
|
uint32_t image_id = 0;
|
|
uint32_t placement_id = 0;
|
|
bool is_virtual = false;
|
|
int32_t z = 0;
|
|
|
|
ghostty_kitty_graphics_placement_get_multi(iter, 4,
|
|
(GhosttyKittyGraphicsPlacementData[]){
|
|
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IMAGE_ID,
|
|
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_PLACEMENT_ID,
|
|
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_IS_VIRTUAL,
|
|
GHOSTTY_KITTY_GRAPHICS_PLACEMENT_DATA_Z,
|
|
},
|
|
(void*[]){ &image_id, &placement_id, &is_virtual, &z },
|
|
NULL);
|
|
|
|
printf(" placement #%d: image_id=%u placement_id=%u virtual=%s z=%d\n",
|
|
placement_count, image_id, placement_id,
|
|
is_virtual ? "true" : "false", z);
|
|
|
|
/* Look up the image and print its properties. */
|
|
GhosttyKittyGraphicsImage image =
|
|
ghostty_kitty_graphics_image(graphics, image_id);
|
|
if (!image) {
|
|
fprintf(stderr, "Failed to look up image %u\n", image_id);
|
|
return 1;
|
|
}
|
|
|
|
uint32_t width = 0, height = 0, number = 0;
|
|
GhosttyKittyImageFormat format = 0;
|
|
size_t data_len = 0;
|
|
uint64_t image_generation = 0;
|
|
|
|
/*
|
|
* The per-image generation changes on every add/replace of this
|
|
* image ID, so it detects retransmissions even when the size and
|
|
* format are unchanged. Texture caches should key staleness on it.
|
|
*/
|
|
ghostty_kitty_graphics_image_get_multi(image, 6,
|
|
(GhosttyKittyGraphicsImageData[]){
|
|
GHOSTTY_KITTY_IMAGE_DATA_NUMBER,
|
|
GHOSTTY_KITTY_IMAGE_DATA_WIDTH,
|
|
GHOSTTY_KITTY_IMAGE_DATA_HEIGHT,
|
|
GHOSTTY_KITTY_IMAGE_DATA_FORMAT,
|
|
GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN,
|
|
GHOSTTY_KITTY_IMAGE_DATA_GENERATION,
|
|
},
|
|
(void*[]){ &number, &width, &height, &format, &data_len,
|
|
&image_generation },
|
|
NULL);
|
|
|
|
printf(" image: number=%u size=%ux%u format=%d data_len=%zu "
|
|
"generation=%llu\n",
|
|
number, width, height, format, data_len,
|
|
(unsigned long long)image_generation);
|
|
|
|
/* Compute the rendered pixel size and grid size. */
|
|
uint32_t px_w = 0, px_h = 0, cols = 0, rows = 0;
|
|
if (ghostty_kitty_graphics_placement_pixel_size(iter, image, terminal,
|
|
&px_w, &px_h) == GHOSTTY_SUCCESS) {
|
|
printf(" rendered pixel size: %ux%u\n", px_w, px_h);
|
|
}
|
|
if (ghostty_kitty_graphics_placement_grid_size(iter, image, terminal,
|
|
&cols, &rows) == GHOSTTY_SUCCESS) {
|
|
printf(" grid size: %u cols x %u rows\n", cols, rows);
|
|
}
|
|
}
|
|
printf("Total placements: %d\n", placement_count);
|
|
ghostty_kitty_graphics_placement_iterator_free(iter);
|
|
|
|
/* Clean up. */
|
|
ghostty_terminal_free(terminal);
|
|
|
|
/* Clear the sys callbacks. */
|
|
ghostty_sys_set(GHOSTTY_SYS_OPT_DECODE_PNG, NULL);
|
|
ghostty_sys_set(GHOSTTY_SYS_OPT_USERDATA, NULL);
|
|
|
|
return 0;
|
|
}
|
|
//! [kitty-graphics-main]
|