mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-14 03:25:50 +00:00
example: add c-vt-kitty-graphics
Demonstrates the sys interface for Kitty Graphics Protocol PNG support. The example installs a PNG decode callback via ghostty_sys_set, creates a terminal with image storage enabled, and sends an inline 1x1 PNG image through vt_write. Snippet markers are wired up to the sys.h doxygen docs.
This commit is contained in:
18
example/c-vt-kitty-graphics/README.md
Normal file
18
example/c-vt-kitty-graphics/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Example: `ghostty-vt` Kitty Graphics Protocol
|
||||
|
||||
This contains a simple example of how to use the system interface
|
||||
(`ghostty_sys_set`) to install a PNG decoder callback, then send
|
||||
a Kitty Graphics Protocol image via `ghostty_terminal_vt_write`.
|
||||
|
||||
This uses a `build.zig` and `Zig` to build the C program so that we
|
||||
can reuse a lot of our build logic and depend directly on our source
|
||||
tree, but Ghostty emits a standard C library that can be used with any
|
||||
C tooling.
|
||||
|
||||
## Usage
|
||||
|
||||
Run the program:
|
||||
|
||||
```shell-session
|
||||
zig build run
|
||||
```
|
||||
42
example/c-vt-kitty-graphics/build.zig
Normal file
42
example/c-vt-kitty-graphics/build.zig
Normal file
@@ -0,0 +1,42 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
|
||||
const exe_mod = b.createModule(.{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
exe_mod.addCSourceFiles(.{
|
||||
.root = b.path("src"),
|
||||
.files = &.{"main.c"},
|
||||
});
|
||||
|
||||
// You'll want to use a lazy dependency here so that ghostty is only
|
||||
// downloaded if you actually need it.
|
||||
if (b.lazyDependency("ghostty", .{
|
||||
// Setting simd to false will force a pure static build that
|
||||
// doesn't even require libc, but it has a significant performance
|
||||
// penalty. If your embedding app requires libc anyway, you should
|
||||
// always keep simd enabled.
|
||||
// .simd = false,
|
||||
})) |dep| {
|
||||
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
|
||||
}
|
||||
|
||||
// Exe
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "c_vt_kitty_graphics",
|
||||
.root_module = exe_mod,
|
||||
});
|
||||
b.installArtifact(exe);
|
||||
|
||||
// Run
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
24
example/c-vt-kitty-graphics/build.zig.zon
Normal file
24
example/c-vt-kitty-graphics/build.zig.zon
Normal file
@@ -0,0 +1,24 @@
|
||||
.{
|
||||
.name = .c_vt_kitty_graphics,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x432d40ecc8f15589,
|
||||
.minimum_zig_version = "0.15.1",
|
||||
.dependencies = .{
|
||||
// Ghostty dependency. In reality, you'd probably use a URL-based
|
||||
// dependency like the one showed (and commented out) below this one.
|
||||
// We use a path dependency here for simplicity and to ensure our
|
||||
// examples always test against the source they're bundled with.
|
||||
.ghostty = .{ .path = "../../" },
|
||||
|
||||
// Example of what a URL-based dependency looks like:
|
||||
// .ghostty = .{
|
||||
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
|
||||
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
|
||||
// },
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
125
example/c-vt-kitty-graphics/src/main.c
Normal file
125
example/c-vt-kitty-graphics/src/main.c
Normal file
@@ -0,0 +1,125 @@
|
||||
#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 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);
|
||||
|
||||
/* 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]
|
||||
@@ -98,6 +98,11 @@
|
||||
* grid refs to inspect cell codepoints, row wrap state, and cell styles.
|
||||
*/
|
||||
|
||||
/** @example c-vt-kitty-graphics/src/main.c
|
||||
* This example demonstrates how to use the system interface to install a
|
||||
* PNG decoder callback and send a Kitty Graphics Protocol image.
|
||||
*/
|
||||
|
||||
#ifndef GHOSTTY_VT_H
|
||||
#define GHOSTTY_VT_H
|
||||
|
||||
|
||||
@@ -28,6 +28,14 @@
|
||||
* an implementation. Passing NULL as the value clears the implementation
|
||||
* and disables the corresponding feature.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* ### Defining a PNG decode callback
|
||||
* @snippet c-vt-kitty-graphics/src/main.c kitty-graphics-decode-png
|
||||
*
|
||||
* ### Installing the callback and sending a PNG image
|
||||
* @snippet c-vt-kitty-graphics/src/main.c kitty-graphics-main
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user