Ghostty 源码拆解

全景架构:共享核心与平台外壳

Ghostty 如何把终端核心、窗口系统和渲染后端分层

全景架构:共享核心与平台外壳

Ghostty 最重要的架构判断是:终端 surface 不是窗口src/Surface.zig 只知道自己是一个能接收输入、拥有 PTY、维护终端状态并绘制内容的“表面”;它不决定这个表面最后是 macOS 窗口里的 View、GTK 窗口中的 widget、tab 还是 split。

四层边界

1. 终端核心

src/terminal/ 维护协议语义和可观察状态:Parser 识别 VT 字节,Terminal 执行控制序列,Screen/Page 保存字符与样式,Selectionsearch、Kitty graphics 和 scrollback 依附在这层。它不应该依赖 Cocoa、GTK 或 GPU。

2. 终端 IO

src/termio/ 把“字节流”和“终端模型”接上:backend 可以是 PTY、手工输入或嵌入环境;StreamHandler 把 parser 产生的语义事件送给 Terminal,并把 clipboard、title、pwd、bell、renderer wakeup 等副作用送到 mailbox。

3. Surface / Renderer

Surface.zig 聚合一张终端表面的资源:

Surface
├── renderer.State
├── renderer.Thread + renderer implementation
├── termio.Termio + termio.Thread
├── keyboard / mouse / selection / search
├── runtime App + runtime Surface
└── derived config + size

renderer/generic.zig 只依赖 Graphics API 的接口;真正的 Metal/OpenGL/WebGL 资源放在对应后端。这样,字体和 cell 组织逻辑可以共享,GPU 命令的 API 差异留在薄适配层。

4. Application runtime

src/apprt/ 提供运行时契约。当前 Runtime 枚举包含 nonegtk;macOS 通过 Xcode/Swift 宿主链接 libghostty,再用 C API 创建 app/surface。运行时负责窗口、焦点、剪贴板、菜单、系统通知、进程级事件循环等,核心负责终端语义。

App、Surface、Terminal 的关系

一个进程
└── App
    ├── surfaces: []*apprt.Surface
    ├── font_grid_set: SharedGridSet
    ├── app mailbox
    └── config conditional state
        ├── Surface A
        │   ├── terminal + PTY
        │   ├── renderer + font grid
        │   └── IO / render threads
        └── Surface B ...

App 只管理 app 级事实:surface 注册、最后聚焦的 surface、全局配置更新和 app mailbox。Surface 管理窗口内一个 terminal 的完整资源,并把绝大多数事件转成 core action 或线程消息。Terminal 则是 renderer-agnostic 的纯终端状态。

为什么不把所有东西放进一个 event loop?

终端输出有三个互相冲突的时间尺度:PTY 读和 VT parser 希望尽快消费字节;GPU 帧要在显示刷新节奏下提交;窗口事件必须遵守平台主线程规则。Ghostty 选择把它们分成 IO thread、renderer thread 和 app/runtime thread,再用 xev async/timer 与 mailbox 连接。

好处是热路径可以连续处理输出,窗口系统不用等待 parser;代价是每一个跨线程指针都要有明确的 owner、生命周期和唤醒策略。后文所有复杂设计——renderer.State、SPSC mailbox、dirty flags、deinit 顺序——都是在支付这笔复杂度。

读源码时最容易误判的地方

  • App 不是 SwiftUI 的 App 协议,它是 Zig core 的 app manager;
  • Surface 不是 drawable view,而是“终端对象 + runtime surface + 线程资源”的聚合体;
  • libghostty 不是另一个终端实现,它把同一套核心状态暴露给宿主;
  • README 的“跨平台”不意味着所有功能都在同一 runtime 中实现,none、GTK、macOS embedded 的能力集合不同。

On this page