首页
/ Ghostty 示例工程实战:example 目录如何驱动 libghostty-vt 的 C/Zig 两种 API

Ghostty 示例工程实战:example 目录如何驱动 libghostty-vt 的 C/Zig 两种 API

2026-09-05 13:34:33作者:裘旻烁

本文以仓库 example/README.md 为主线,系统讲解 Ghostty 官方的 example/ 示例工程集合:每个示例都是独立的库 API 演示项目,按 c-/zig- 前缀区分所用语言,统一通过 zig build / zig build run 构建运行。读完后你将掌握:如何在任何示例目录下用一条命令编译运行、示例工程 build.zig 中 lazy dependency 与静态/动态链接的取舍、CMake/Swift/WASM 等变体构建方式,以及如何从最小示例源码读懂 libghostty-vt 的实际调用链。

example 目录是什么:独立可构建的库 API 演示工程

example/README.md 对示例目录的定位非常明确:

Standalone projects demonstrating the Ghostty library APIs. The directories starting with c- use the C API and the directories starting with zig- use the Zig API.

example/ 下每个目录都是一个独立工程(standalone project),专门演示 Ghostty 库 API 的用法;目录名前缀 c- 表示使用 C API,zig- 表示使用 Zig API。结合 example/AGENTS.md 的约定,可以确认每个示例的标准目录结构为:

  • build.zig —— Zig 构建脚本(所有示例共用一套模板);
  • build.zig.zon —— Zig 包清单,声明对 Ghostty 的依赖;
  • README.md —— 该示例的说明文档;
  • src/main.csrc/main.zig —— 示例主程序。

以最小的 C 示例 example/c-vt 为例,其完整文件集就是上述四件套(README.mdbuild.zigbuild.zig.zonsrc/main.c)。

从仓库实际内容看,当前示例已扩展到约 30 个,除文档明确列举的 c-/zig- 两大类外,还包括面向其他构建体系的变体:

类别 代表目录 说明
C API 基础演示 c-vtc-vt-streamc-vt-sgrc-vt-colorsc-vt-formatter OSC/SGR 解析、VT 流处理、颜色、格式化等
C API 进阶演示 c-vt-renderc-vt-searchc-vt-selectionc-vt-pastec-vt-kitty-graphicsc-vt-effects 渲染状态、搜索、选区、粘贴、Kitty 图形协议、终端效果回调
C API 编码类演示 c-vt-encode-keyc-vt-encode-mousec-vt-encode-focusc-vt-size-reportc-vt-color-scheme 按键/鼠标/焦点事件、尺寸与配色报告编码为转义序列
构建方式变体 c-vt-staticc-vt-cmakec-vt-cmake-staticc-vt-cmake-cross 静态链接、CMake FetchContent、跨平台交叉编译
Zig API 演示 zig-vtzig-vt-streamzig-formatter 直接使用 ghostty-vt Zig 模块
其他语言/平台 cpp-vt-streamswift-vt-xcframeworkwasm-vtwasm-sgrwasm-key-encode C++ 兼容性验证、Swift XCFramework、WebAssembly

每个示例自身的 README 还反复强调一点:示例虽然用 Zig 构建系统来构建,但 Ghostty 对外发布的是标准 C 库,可以用任意 C 工具链消费,Zig 构建只是为了复用仓库的构建逻辑并直接依赖源码树。

构建与运行:zig build run 的统一入口

example/README.md 给出的运行方式是全部 30 余个示例(Zig 构建的那部分)的统一入口:

cd example/<dir>
zig build run

其中 <dir> 替换为目标示例目录名。原文特别指出:即便是 C API 示例,使用的也是 Zig 构建系统(而非 Zig 语言)来构建项目。这句话的底层依据可以直接在 example/c-vt/build.zig 中看到:

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"));
}

这段构建脚本传达了三个关键信息:

  1. addCSourceFiles 说明构建的是 C 源码src/main.c),Zig 在这里只是充当跨平台 C 编译器与链接器的角色;
  2. b.lazyDependency("ghostty", ...) 表示 Ghostty 库以 lazy 方式引入——只有真正需要构建时才拉取/编译依赖,随后 linkLibrary(dep.artifact("ghostty-vt")) 链接 ghostty-vt 构件。example/AGENTS.md 将“所有 C 示例均通过 lazyDependency("ghostty", ...) 链接 ghostty-vt”列为硬约定;
  3. .simd = false 选项的取舍:开启后会产生纯静态、甚至不依赖 libc 的构建,但源码注释明确说明这会带来显著性能惩罚;如果你的宿主应用本来就需要 libc,应始终保持 simd 启用。

依赖声明在 example/c-vt/build.zig.zon 中:

.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",
// },

示例仓库内用 .path = "../../" 指向源码树自身,保证始终测试与当前检出配套的代码;而注释中给出的 URL 形式(配合 .hash 校验和)才是外部项目消费 Ghostty 库的标准姿势。清单中还有两个与 example/AGENTS.md 约定对应的字段:.minimum_zig_version(当前为 0.15.1,各示例保持一致)和 .fingerprint(新增示例时必须生成新的唯一 u64 随机值)。

最小 C 示例源码走读:OSC 解析器

c-vt 是整个示例集合中最能体现“库 API 长什么样”的工程,其 src/main.c 用约 40 行演示了 OSC(Operating System Command)解析的完整生命周期:

#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>

int main() {
  GhosttyOscParser parser;
  if (ghostty_osc_new(NULL, &parser) != GHOSTTY_SUCCESS) {
    return 1;
  }

  // Setup change window title command to change the title to "hello"
  ghostty_osc_next(parser, '0');
  ghostty_osc_next(parser, ';');
  const char *title = "hello";
  for (size_t i = 0; i < strlen(title); i++) {
    ghostty_osc_next(parser, title[i]);
  }

  // End parsing and get command
  GhosttyOscCommand command = ghostty_osc_end(parser, 0);

  // Get and print command type
  GhosttyOscCommandType type = ghostty_osc_command_type(command);
  printf("Command type: %d\n", type);

  // Extract and print the title
  if (ghostty_osc_command_data(command, GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR, &title)) {
    printf("Extracted title: %s\n", title);
  } else {
    printf("Failed to extract title\n");
  }

  ghostty_osc_free(parser);
  return 0;
}

调用链可以概括为四步:

  1. ghostty_osc_new(NULL, &parser) 创建解析器,返回值与 GHOSTTY_SUCCESS 比较判断成败;
  2. 逐字符喂入 ghostty_osc_next(parser, byte)——这里模拟的是 OSC 0 ; hello(改变窗口标题)序列;
  3. ghostty_osc_end(parser, 0) 结束解析并得到 GhosttyOscCommand,再通过 ghostty_osc_command_type 取命令类型、ghostty_osc_command_data 按数据种类(此处为 GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR)提取字符串载荷;
  4. ghostty_osc_free(parser) 释放资源。

所有符号的声明都在公共头文件 include/ghostty/vt.h 及其包含的 include/ghostty/vt/ 目录下。example/AGENTS.md 还说明了一个文档与示例之间的隐性关联:include/ghostty/vt/ 中的头文件通过 Doxygen @snippet 标签引用示例源码(用 //! [snippet-name] 标记包裹),因此示例代码本身就是官方 API 文档的一部分——这也是为什么修改示例代码时必须保持 snippet 标记与头文件同步。

对应的 Zig 版本 example/zig-vt/src/main.zig 则展示了同一库的 Zig 模块形态:

const std = @import("std");
const ghostty_vt = @import("ghostty-vt");

pub fn main(init: std.process.Init) !void {
    // Initialize a terminal.
    var t: ghostty_vt.Terminal = try .init(init.io, init.gpa, .{
        .cols = 6,
        .rows = 40,
    });
    defer t.deinit(init.gpa);

    // Write some text. It'll wrap because this is too long for our
    // columns size above (6).
    try t.printString("Hello, World!");

    // Get the plain string view of the terminal screen.
    const str = try t.plainString(init.gpa);
    defer init.gpa.free(str);
    std.debug.print("{s}\n", .{str});
}

对比 C 版本可以看出 Zig 模块直接导出类型化 API(ghostty_vt.Terminal),创建 6 列终端、写入文本、再取纯文本视图三步完成,无需逐字节驱动解析器。

其他构建方式:CMake、静态链接、交叉编译、XCFramework 与 WASM

zig build run 是默认路径,但 example/ 中另有一组示例专门演示脱离 Zig 构建系统的消费方式,它们的 README 中给出了完整的可复制命令:

CMake + FetchContent

c-vt-cmake 演示从 CMake 工程消费 libghostty-vt(FetchContent 拉取,创建终端、写入 VT 序列、格式化屏幕为纯文本):

cd example/c-vt-cmake
cmake -B build
cmake --build build
./build/c_vt_cmake

c-vt-cmake-static 是同一模式的静态库版本。两者都支持通过 -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../.. 改为直接构建本地检出,而不从远端抓取:

cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build

交叉编译

c-vt-cmake-cross 演示使用 CMake 函数 ghostty_vt_add_target() 以静态链接方式交叉编译 libghostty-vt,目标平台按主机自动选择(可用 -DZIG_TARGET=... 覆盖):

Host Target
Linux Windows (MinGW)
Windows Linux (glibc)
macOS Linux (glibc)
cd example/c-vt-cmake-cross
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build
file build/c_vt_cmake_cross

Zig 内静态链接

c-vt-static 的 README 说明它与 c-vt 的唯一区别是链接 ghostty-vt-static 构件而非普通 ghostty-vt 构件,用于展示静态链接形态。

Swift XCFramework

swift-vt-xcframework 演示通过预构建 XCFramework 让 Swift Package 消费 libghostty-vt,构建顺序是先在仓库根目录生成 XCFramework,再进入示例目录构建:

zig build -Demit-lib-vt

cd example/swift-vt-xcframework
swift build
swift run

WebAssembly

wasm-vtwasm-sgrwasm-key-encode 三个浏览器示例共享同一条前置构建命令(在仓库根目录执行):

zig build -Demit-lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall

产物为 zig-out/bin/ghostty-vt.wasm。README 特别强调:必须通过 HTTP 访问(如 python3 -m http.server 8000 从仓库根目录启动),浏览器禁止从 file:// URL 加载 WASM 文件。三个示例分别对应:初始化终端并写入 VT 数据后格式化为纯文本、解析 SGR 样式序列、按键事件编码。

值得注意的边界:C++ 与 WASM 示例不是 zig build run 成员

cpp-vt-streamc-vt-stream 的 C++ 移植,其 README 明确指出 libghosttyC 库,该示例的存在目的是让 CI 验证库可在 C++ 模式下编译;而三个 wasm-* 示例的入口是浏览器页面(各自目录下的 index.html),不是可执行工程。选择示例时注意这一区别。

从源码结构看:示例背后的公共能力面

浏览 example/ 中各示例 README 的一句话定位,可以归纳出 libghostty-vt 对外暴露的能力面,这也是 include/ghostty/vt/ 头文件组织的镜像:

  • 终端状态机c-vt-streamzig-vt-streamghostty_terminal_vt_write / vtStream 演示“只读终端”模式——解析 VT 序列、更新终端状态,但对需要应答的查询(如设备状态查询)不予响应,适用于回放工具、CI 日志查看器、PaaS 构建输出等场景;
  • 屏幕读取与呈现c-vt-formatter(格式化纯文本)、c-vt-grid-traverse(逐格遍历 codepoint/行状态/样式)、c-vt-grid-ref-tracked(长生命周期跟踪单元格引用)、c-vt-render(渲染状态迭代、脏区跟踪);
  • 交互事件c-vt-selection / c-vt-selection-gesture(选区与合成手势)、c-vt-paste(普通/bracketed/unsafe-paste 确认/Kitty 剪贴板协议)、c-vt-search(查找栏式匹配导航);
  • 协议编码:按键(Kitty 协议,示例产出 \x1b[57442;5:3u)、鼠标(SGR 格式,示例产出形如 \x1b[<0;6;3M 的序列)、焦点、尺寸报告、配色报告,分别对应 c-vt-encode-keyc-vt-encode-mousec-vt-encode-focusc-vt-size-reportc-vt-color-scheme
  • 系统接口与高级特性c-vt-effectswrite_pty/bell/title_changed/clipboard_write 回调,剪贴板收到的是解码后的二进制安全 MIME 表示)、c-vt-kitty-graphics(通过 ghostty_sys_set 安装 PNG 解码回调后接收 Kitty 图形协议图像)、c-vt-compression(回滚区压缩:库本身不创建定时器或后台线程,嵌入方负责在空闲后调度增量压缩并串行化访问)、c-vt-snapshot(快照编码与一次性/增量两种解码路径)、c-vt-modes(模式标识打包/解包与 DECRPM 应答编码)、c-vt-colors(默认颜色、OSC 覆盖的叠加行为)。

新增一个示例:仓库维护者的标准流程

如果你要为 libghostty-vt 增加新的 API 演示,example/AGENTS.md 给出了维护者视角的完整流程,可帮助读者理解示例工程之间的同构性来源:

  1. 复制一个现有示例目录(如 c-vt-encode-focus/)作为起点;
  2. 修改 build.zig.zon:改 .name,生成新的唯一 .fingerprint(随机 u64 十六进制字面量),保持 .minimum_zig_version 与其他示例一致;
  3. 修改 build.zig 中可执行文件的 .name 与目录名一致;
  4. 按既有格式撰写 README.md

三条硬约定同样值得记住:可执行文件名使用下划线(c_vt_encode_focus 而非连字符);所有 C 示例通过 lazyDependency("ghostty", ...) 链接 ghostty-vtbuild.zig 遵循统一模板。此外 CI 通过 example/*/build.zig.zon 通配自动发现新示例,无需修改任何 workflow 文件——这解释了为什么每个目录必须自带 build.zig.zon

小结

example/README.md 篇幅不长,但它定义了 Ghostty 库 API 学习路径的基本规则:目录前缀决定 API 语言,cd example/<dir> && zig build run 是统一入口,Zig 构建系统只是手段、标准 C 库才是目标产物。配合 example/AGENTS.md 的工程约定和 example/c-vtexample/zig-vt 等最小示例源码,你可以快速判断某个能力(OSC 解析、VT 流处理、选区、快照……)对应的示例与头文件位置;而 CMake/静态/交叉编译/XCFramework/WASM 五组变体示例则覆盖了真实嵌入场景中几乎所有主流构建路径。

登录后查看全文
热门项目推荐
相关项目推荐