This example demonstrates how to use the system interface to install a PNG decoder callback and send a Kitty Graphics Protocol image.
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
bool decode_png(void* userdata,
const uint8_t* data,
size_t data_len,
int* count = (int*)userdata;
(*count)++;
printf(" decode_png called (size=%zu, call #%d)\n", data_len, *count);
const size_t pixel_len = 4;
if (!pixels) return false;
pixels[0] = 255;
pixels[1] = 0;
pixels[2] = 0;
pixels[3] = 255;
return true;
}
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");
}
#define MIN_FRAME_INTERVAL_MS 8
uint64_t next_ms = 0;
return -1;
}
uint64_t delay_ms = next_ms > now_ms ? next_ms - now_ms : 0;
if (delay_ms < MIN_FRAME_INTERVAL_MS) delay_ms = MIN_FRAME_INTERVAL_MS;
return (int64_t)delay_ms;
}
const uint8_t* pixels = NULL;
uint64_t generation = 0;
&pixels);
&generation);
printf("pixel=(%u,%u,%u) generation=%llu", pixels[0], pixels[1], pixels[2],
(unsigned long long)generation);
}
const char* cmds =
"\x1b_Ga=T,f=32,i=2,s=1,v=1,q=2;/wAA/w==\x1b\\"
"\x1b_Ga=f,f=32,i=2,s=1,v=1,z=40,q=2;AAD//w==\x1b\\"
"\x1b_Ga=a,i=2,r=1,z=30,q=2\x1b\\"
"\x1b_Ga=a,i=2,s=3,q=2\x1b\\";
fprintf(stderr, "Failed to get kitty graphics storage\n");
return 1;
}
uint64_t now_ms = 0;
for (int i = 0; i < 3; i++) {
int64_t delay_ms = tick_animations(graphics, now_ms);
printf(" t=%llu ms: ", (unsigned long long)now_ms);
print_image(graphics, 2);
printf(", next redraw in %lld ms\n", (long long)delay_ms);
now_ms += (uint64_t)delay_ms;
}
const char* stop = "\x1b_Ga=a,i=2,s=1,q=2\x1b\\";
&graphics);
if (tick_animations(graphics, now_ms) < 0) {
printf(" t=%llu ms: stopped, no redraw needed\n",
(unsigned long long)now_ms);
}
return 0;
}
switch (kind) {
default: return "unknown";
}
}
switch (layer) {
default: return "unknown";
}
}
fprintf(stderr, "Failed to get image iterator\n");
return -1;
}
fprintf(stderr, "Failed to get kitty graphics storage\n");
return -1;
}
int count = 0;
uint32_t image_id, placement_id, cols, rows, offset_x, offset_y;
uint32_t width, height, src_x, src_y, src_width, src_height;
uint64_t generation;
int32_t col, row, z;
};
void* values[] = {
&image_id, &generation, &kind, &placement_id, &layer, &z, &col, &row,
&cols, &rows, &offset_x, &offset_y, &width, &height,
&src_x, &src_y, &src_width, &src_height,
};
size_t key_count = sizeof(keys) / sizeof(keys[0]);
fprintf(stderr, "Failed to read image placement\n");
return -1;
}
count++;
printf(" image #%d: kind=%s image_id=%u placement_id=%u\n", count,
kind_name(kind), image_id, placement_id);
printf(" layer=%s z=%d cell=(%d,%d) cells=%ux%u offset=(%u,%u)\n",
layer_name(layer), z, col, row, cols, rows, offset_x, offset_y);
printf(" size=%ux%u source=(%u,%u %ux%u)\n", width, height, src_x,
src_y, src_width, src_height);
if (!image) continue;
uint32_t image_width = 0, image_height = 0;
const uint8_t* pixels = NULL;
size_t data_len = 0;
},
(void*[]){ &image_width, &image_height, &format, &pixels, &data_len },
NULL);
printf(" pixels: %ux%u format=%d data_len=%zu generation=%llu\n",
image_width, image_height, format, data_len,
(unsigned long long)generation);
}
return count;
}
fprintf(stderr, "Failed to create render state\n");
return 1;
}
fprintf(stderr, "Failed to create image iterator\n");
return 1;
}
uint64_t last_generation = 0;
bool have_draw_list = false;
int result = 0;
for (int frame = 1; frame <= 3; frame++) {
if (frame == 3) {
const char* scroll = "\x1b[24;1H\r\n\r\n";
strlen(scroll));
}
fprintf(stderr, "Failed to update render state\n");
result = 1;
break;
}
uint64_t generation = 0;
&generation);
if (have_draw_list && generation == last_generation) {
printf("\nFrame %d: no image changed, keep drawing the last list\n",
frame);
continue;
}
last_generation = generation;
have_draw_list = true;
printf("\nFrame %d: images changed, reading the list:\n", frame);
int count = draw_images(state, images, terminal);
if (count < 0) {
result = 1;
break;
}
printf("Total images: %d\n", count);
}
return result;
}
int main() {
int decode_count = 0;
fprintf(stderr, "Failed to create terminal\n");
return 1;
}
uint64_t storage_limit = 64 * 1024 * 1024;
&storage_limit);
(const void*)on_write_pty);
const char* move = "\x1b[10;1H";
printf("Sending Kitty graphics PNG image:\n");
const char* kitty_cmd =
"\x1b_Ga=T,f=100,q=1;"
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA"
"DUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg=="
"\x1b\\";
strlen(kitty_cmd));
printf("PNG decode calls: %d\n", decode_count);
const char* placeholder_cmds =
"\x1b_Ga=T,U=1,f=32,i=3,s=1,v=1,c=2,r=1,q=2;AP8A/w==\x1b\\"
"\r\n\x1b[38;5;3m"
"\xf4\x8e\xbb\xae\xcc\x85\xcc\x85"
"\xf4\x8e\xbb\xae\xcc\x85\xcc\x8d"
"\x1b[39m";
strlen(placeholder_cmds));
fprintf(stderr, "Failed to get kitty graphics storage\n");
return 1;
}
uint64_t storage_generation = 0;
&storage_generation);
printf("\nStorage generation: %llu\n",
(unsigned long long)storage_generation);
if (run_frames(terminal) != 0) return 1;
printf("\nPlaying an animation:\n");
if (run_animation(terminal) != 0) return 1;
return 0;
}
GHOSTTY_API uint8_t * ghostty_alloc(const GhosttyAllocator *allocator, size_t len)
struct GhosttyKittyGraphicsImpl * GhosttyKittyGraphics
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_animation_tick(GhosttyKittyGraphics graphics, uint64_t now_ms, uint64_t *next_ms)
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_get(GhosttyKittyGraphics graphics, GhosttyKittyGraphicsData data, void *out)
GHOSTTY_API GhosttyKittyGraphicsImage ghostty_kitty_graphics_image(GhosttyKittyGraphics graphics, uint32_t image_id)
GhosttyKittyGraphicsImageData
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_image_get_multi(GhosttyKittyGraphicsImage image, size_t count, const GhosttyKittyGraphicsImageData *keys, void **values, size_t *out_written)
GHOSTTY_API GhosttyResult ghostty_kitty_graphics_image_get(GhosttyKittyGraphicsImage image, GhosttyKittyGraphicsImageData data, void *out)
const struct GhosttyKittyGraphicsImageImpl * GhosttyKittyGraphicsImage
@ GHOSTTY_KITTY_IMAGE_DATA_DATA_PTR
@ GHOSTTY_KITTY_IMAGE_DATA_GENERATION
@ GHOSTTY_KITTY_IMAGE_DATA_HEIGHT
@ GHOSTTY_KITTY_IMAGE_DATA_WIDTH
@ GHOSTTY_KITTY_IMAGE_DATA_DATA_LEN
@ GHOSTTY_KITTY_IMAGE_DATA_FORMAT
@ GHOSTTY_KITTY_GRAPHICS_DATA_GENERATION
GhosttyRenderStateKittyPlacementKind
GHOSTTY_API GhosttyResult ghostty_render_state_image_get_multi(GhosttyRenderStateImageIterator iterator, size_t count, const GhosttyRenderStateImageData *keys, void **values, size_t *out_written)
GHOSTTY_API GhosttyResult ghostty_render_state_update(GhosttyRenderState state, GhosttyTerminal terminal)
GHOSTTY_API void ghostty_render_state_image_iterator_free(GhosttyRenderStateImageIterator iterator)
GhosttyRenderStateImageData
struct GhosttyRenderStateImpl * GhosttyRenderState
GHOSTTY_API GhosttyResult ghostty_render_state_new(const GhosttyAllocator *allocator, GhosttyRenderState *state)
GHOSTTY_API GhosttyResult ghostty_render_state_image_iterator_new(const GhosttyAllocator *allocator, GhosttyRenderStateImageIterator *out_iterator)
GHOSTTY_API void ghostty_render_state_free(GhosttyRenderState state)
GhosttyRenderStateImageLayer
GHOSTTY_API GhosttyResult ghostty_render_state_get(GhosttyRenderState state, GhosttyRenderStateData data, void *out)
GHOSTTY_API bool ghostty_render_state_image_iterator_next(GhosttyRenderStateImageIterator iterator)
GHOSTTY_API GhosttyResult ghostty_render_state_image_get(GhosttyRenderStateImageIterator iterator, GhosttyRenderStateImageData data, void *out)
struct GhosttyRenderStateImageIteratorImpl * GhosttyRenderStateImageIterator
@ GHOSTTY_IMAGE_PROTOCOL_KITTY
@ GHOSTTY_RENDER_STATE_DATA_IMAGE_ITERATOR
@ GHOSTTY_RENDER_STATE_DATA_IMAGE_PLACEMENTS_GENERATION
@ GHOSTTY_RENDER_STATE_KITTY_PLACEMENT_KIND_FIXED
@ GHOSTTY_RENDER_STATE_KITTY_PLACEMENT_KIND_VIRTUAL
@ GHOSTTY_RENDER_STATE_KITTY_PLACEMENT_KIND_RELATIVE
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_Z
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_PIXEL_HEIGHT
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_PROTOCOL
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_IMAGE_ID
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_VIEWPORT_COL
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_CELL_OFFSET_X
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_IMAGE_GENERATION
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_SOURCE_Y
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_KITTY_PLACEMENT_ID
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_VIEWPORT_ROW
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_PIXEL_WIDTH
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_GRID_ROWS
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_CELL_OFFSET_Y
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_SOURCE_HEIGHT
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_KITTY_KIND
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_SOURCE_X
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_GRID_COLS
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_LAYER
@ GHOSTTY_RENDER_STATE_IMAGE_DATA_SOURCE_WIDTH
@ GHOSTTY_RENDER_STATE_IMAGE_LAYER_BELOW_TEXT
@ GHOSTTY_RENDER_STATE_IMAGE_LAYER_BELOW_BACKGROUND
@ GHOSTTY_RENDER_STATE_IMAGE_LAYER_ABOVE_TEXT
GHOSTTY_API GhosttyResult ghostty_sys_set(GhosttySysOption option, const void *value)
@ GHOSTTY_SYS_OPT_DECODE_PNG
@ GHOSTTY_SYS_OPT_USERDATA
GHOSTTY_API GhosttyResult ghostty_terminal_set(GhosttyTerminal terminal, GhosttyTerminalOption option, const void *value)
GHOSTTY_API GhosttyResult ghostty_terminal_get(GhosttyTerminal terminal, GhosttyTerminalData data, void *out)
struct GhosttyTerminalImpl * GhosttyTerminal
GHOSTTY_API void ghostty_terminal_free(GhosttyTerminal terminal)
GHOSTTY_API void ghostty_terminal_vt_write(GhosttyTerminal terminal, const uint8_t *data, size_t len)
GHOSTTY_API GhosttyResult ghostty_terminal_resize(GhosttyTerminal terminal, uint16_t cols, uint16_t rows, uint32_t cell_width_px, uint32_t cell_height_px)
GHOSTTY_API GhosttyResult ghostty_terminal_new(const GhosttyAllocator *allocator, GhosttyTerminal *terminal, uint16_t cols, uint16_t rows)
@ GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS
@ GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT
@ GHOSTTY_TERMINAL_OPT_WRITE_PTY