如何在 C 程序中为 Ghostty 终端注册 PNG 解码器并发送 Kitty 图形协议图片【免费下载链接】ghostty Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration.项目地址: https://gitcode.com/GitHub_Trending/gh/ghostty当你在自己的 C 程序里嵌入 Ghostty 的 C 库ghostty-vt时Kitty Graphics Protocol 的 PNG 图片默认无法被解码——库把图片解码实现留给了宿主程序。要完成“收到f100的 Kitty 图片命令后真正存入图像”这件事需要走三步用ghostty_sys_set注册一个 PNG 解码回调、创建一个开启了 Kitty 图形存储的终端、再通过ghostty_terminal_vt_write发送带 base64 PNG 的转义序列最后查询图形存储验证图片是否落库。本文基于仓库自带的示例 c-vt-kitty-graphics 讲解这条完整路径。准备条件示例工程与构建方式示例工程位于 example/c-vt-kitty-graphics包含源码 src/main.c 和构建文件 build.zig、build.zig.zon。仓库中每个 example 都是一个独立工程文档明确说明即使是 C API 示例也统一用 Zig 构建系统而不是 C 语言本身来构建从示例目录内执行即可cd example/c-vt-kitty-graphics zig build run构建逻辑上有两个值得注意的点都写在 build.zig 的注释里工程通过b.lazyDependency(ghostty, ...)以 lazy 依赖方式链接ghostty-vt目标只有真正需要时才拉取 Ghostty注释提示.simd false可以强制出一个纯静态、不依赖 libc 的构建但有明显的性能代价如果宿主程序反正要用 libc应保持 simd 开启。如果你想用任意 C 工具链而不是 Zig 构建README 指出 Ghostty 输出的是标准 C 库可以直接配合现有 C 构建系统使用。本文以仓库示例自带的zig build run为主路径。定义 PNG 解码回调PNG 解码回调的签名由 include/ghostty/vt/sys.h 中的GhosttySysDecodePngFn定义输入是原始 PNG 字节流输出是填好的GhosttySysImage结构width、height、data、data_len其中data是指向解码后 RGBA 像素的指针。两条硬性约束写在头文件里输出的像素缓冲区必须通过回调参数allocator提供的分配器分配库会取得该缓冲区的所有权并用同一个分配器释放它解码成功返回true失败返回false。sys 接口是进程级全局设置必须在依赖它的任何终端功能之前配置设置 PNG 解码器即开启 Kitty Graphics Protocol 的 PNG 图片支持传NULL清除后 PNG 数据会被拒绝。仓库示例为节省篇幅没有真正解码 PNG而是针对已知要发送的那张 1x1 图片硬编码了一个红色像素。示例注释明确警告WARNING: This is only an example for providing a callback, it DOES NOT actually decode the PNG it is passed. It hardcodes a response.真实实现应使用 PNG 库如 libpng、stb_image解码。回调完整代码如下userdata里放一个调用计数器方便之后验证回调是否被触发#include stdbool.h #include stddef.h #include stdint.h #include stdio.h #include string.h #include ghostty/vt.h 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 (R255, G0, B0, A255). */ 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 图形的终端在main()里用ghostty_sys_set注册 userdata 和解码函数然后按顺序配置终端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; if (ghostty_terminal_new(NULL, terminal, 80, 24) ! 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);各行的用途GHOSTTY_SYS_OPT_USERDATA设置所有 sys 回调收到的 userdata 指针GHOSTTY_SYS_OPT_DECODE_PNG安装解码函数。ghostty_terminal_resize设置单元像素尺寸Kitty 图形协议需要它来计算图片占用的网格大小示例用 80x24 的终端、8x16 的单元像素。GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_STORAGE_LIMIT是开启 Kitty 图形的关键开关输入类型为uint64_t*零值等价于禁用terminal.h 中说明它会删除所有已存图片和 placement。示例取 64 MiB。GHOSTTY_TERMINAL_OPT_WRITE_PTY安装一个回调来捕获终端回写到 pty 的协议应答可选用于观察。Kitty 图形协议在图片加载成功时会向 pty 发回一个 APC 应答除非用q2抑制。示例的on_write_pty回调把这个应答打印出来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 图形协议图片图片通过一条 APC 转义序列发送格式按 main.c 中的注释为ESC _G aT,f100,q1; base64 PNG 数据 ESC \其中aT表示传输并显示f100表示 PNG 格式q1表示请求应答q0则抑制。示例代码把序列拼成字符串后交给ghostty_terminal_vt_writeprintf(Sending Kitty graphics PNG image:\n); const char* kitty_cmd ESC _G aT,f100,q1; iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAA DUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg ESC \\; ghostty_terminal_vt_write(terminal, (const uint8_t*)kitty_cmd, strlen(kitty_cmd)); printf(PNG decode calls: %d\n, decode_count);注意两点源文件里ESC位置是 C 转义字符\x1b本文为了可读性写成了占位词ESC照抄时请替换回\x1b字符串末尾的\x1b\\即 ESC 加反斜杠上面写作ESC \\。序列中的 base64 段是那张 1x1 红色 PNG与解码回调硬编码的像素对应。ghostty_terminal_vt_write会把原始字节送入终端的 VT 流解析器由解析过程触发你注册的 PNG 解码回调。验证查询 Kitty 图形存储图片是否真正存入不能只看打印还要查询存储本体。示例分三步验证用ghostty_terminal_get取GHOSTTY_TERMINAL_DATA_KITTY_GRAPHICS得到GhosttyKittyGraphics graphics句柄取不到或为空说明图片没有落库。读取GHOSTTY_KITTY_GRAPHICS_DATA_GENERATION整个存储的代际号每次图片/放置变更都会变渲染器可以拿它和上一帧比较未变化就跳过放置遍历。遍历 placement放置迭代器逐个取出图片 ID、placement ID、is_virtual、z值再用ghostty_kitty_graphics_image反查图片属性编号、宽高、格式、数据长度、单图代际号并计算实际渲染的像素尺寸和网格尺寸/* 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); 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; 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; }程序结束时按示例做清理ghostty_terminal_free释放终端然后向GHOSTTY_SYS_OPT_DECODE_PNG和GHOSTTY_SYS_OPT_USERDATA传NULL清除全局回调。运行结果按主路径运行zig build run运行后终端应依次出现解码回调的调用打印、write_pty捕获的协议应答response (N bytes): ...即 Kitty 协议回发的 APC 应答、PNG decode calls: 1、Kitty graphics storage is available.、存储代际号以及一个 placement 块里的 image_id、图片属性、rendered pixel size和grid size。以上均为程序运行时打印的实际值不同输入图片下数值会不同不要把它们当成固定预期。限制与适用边界回调本身必须自己分配内存示例用ghostty_alloc(allocator, ...)走库提供的分配器这是 sys.h 的明确要求用malloc之类的其他分配器会导致库用错误的分配器释放缓冲区。sys 接口是进程级全局设置只能在全进程范围配置一次且必须早于依赖它的终端功能它不是某个终端实例的属性。未设置 PNG 解码器时Kitty 图形协议中的 PNG 数据会被直接拒绝把解码器设为NULL等价于禁用 PNG 支持。KITTY_IMAGE_STORAGE_LIMIT设为零时 Kitty 图形协议被禁用GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_STORAGE_LIMIT文档零值表示禁用。示例的解码回调只适用于它自己发送的那张 1x1 图片换成任意真实 PNG 前必须先替换为基于 PNG 库libpng、stb_image 等的真实解码实现回调签名和内存分配约定保持不变。完整的可运行参考实现见 example/c-vt-kitty-graphics/src/main.c接口定义见 include/ghostty/vt/sys.h 与 include/ghostty/vt/kitty_graphics.h。【免费下载链接】ghostty Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration.项目地址: https://gitcode.com/GitHub_Trending/gh/ghostty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考