Ghostty 开发日志 005
大家好!欢迎来到 Ghostty 👻 的官方开发日志!
距离上一篇开发日志已经过去两个多月了,Ghostty 有了大量更新。我一直忙着当新手爸爸,并且把更多空闲的电脑时间投入到改进 Ghostty 上,所以有点冷落了开发日志。抱歉!
如果你错过了之前的开发日志,或者想了解更多关于 Ghostty 的信息,请查看本网站上的 Ghostty 页面。
社区动态
在过去两个月里,Beta 测试组从 100 人增长到了超过 350 人!哇!两个月前 Beta 组还不到 50 人。我非常感谢大家对这个终端项目的兴趣,也感谢我们的 Beta 测试者。这个庞大且不断增长的测试者群体,将真正确保 Ghostty 在公开发布时成为一个强大而稳定的项目。
我之所以能在每一波中大幅扩充 Beta 组,唯一的原因是 Ghostty 的功能已经越来越完整、越来越稳定。以前每波只有 5 个人,却会产生十几个以上的 bug 或功能请求。现在,我们可以邀请 50 人得到同样数量的反馈。而且这个数字还会继续增加。看到这个项目变得如此真实、如此稳定,真是令人满足。
如果你有兴趣加入 Beta 计划,请加入 Ghostty Discord 以便被考虑纳入下一波 Beta。在撰写本文时,Discord 社区约有 20% 的成员是 Beta 测试者。
终端检查器(#728)
终端是一个应用平台。我认为“平台”这个词被人们滥用得太厉害了——他们想让自己的工作显得比实际更重要(或者至少想让投资者相信它比实际更重要)——但终端确实是一个用于文本交互的平台1。
没有运行在其中的应用程序,终端就毫无趣味。更进一步说,没有运行在其中的应用程序,终端是毫无意义的。提高终端应用程序数量和质量的一种方法,就是让为终端开发变得更简单。
看看 Web 领域,对我来说,开发便利性的一次飞跃来自 Firebug 的出现,如今它更常被称为“Web 检查器”,类似的功能已存在于每个主流浏览器中。我想把类似的体验带到终端上,于是 Ghostty 现在有了终端检查器(terminal inspector)。
终端检查器的工作方式类似于 Web 检查器:它是每个终端一个的面板,包含关于正在运行的终端的实时更新信息。你可以看到键盘输入(及其编码方式)、终端模式、字体大小、网格大小、单元格元数据、调色板颜色等等。
目标是让终端检查器成为终端开发者不可或缺的工具,使他们更容易开发和调试终端应用程序(可在任何终端中运行,而不仅仅是 Ghostty)。
终端检查器目前仍处于非常早期、非常实验性的阶段。今天它是一个纯只读界面,但我计划在未来让它支持读写,这样你就可以修改单元格、终端模式、创建合成输入事件等等。
终端检查器的最初想法来自 Beta 社区的一位成员,包括我在内的 Beta 社区中的一小群人迅速行动起来,把它发展成了一个非常切实的想法。感谢所有参与的人!
亚洲语言输入(#八百八十八)
在最近的几波 Beta 中,我有意邀请了来自更多不同时区的人。这带来了更多使用中文、日文和韩文等语言输入的测试者。这些测试者报告了近十几个与亚洲语言输入和渲染相关的问题。因此,Ghostty 现在对这些语言的支持非常好。
在开发日志 003 中,我有一个专门讨论键盘输入的章节,标题是“键盘输入处理讨厌你”。我当时大谈 Ghostty 中一切运作得多么好,现在我要兑现那个承诺,进一步强调这一切是多么复杂,并再次声明 Ghostty 全都处理得很好2。
更复杂的输入状态
在开发日志 003 中,我介绍了死键状态(dead key state)的概念。当时的 Ghostty 只处理单个码点的死键状态。我用带重音符号的英文字母作为例子。在那个例子中,你先输入 '(撇号),然后输入像 a 这样的字母,就会得到 á。
在日语等语言中,你输入一些字符后会得到建议的输入(可能还有一个包含更多候选的下拉列表),然后可以按回车或 Tab 等键来完成候选选择。
Ghostty 此前不处理多码点候选,并且在显示候选时错误地处理了回车或 Tab 等字符。这些问题现在都已修复,你可以正常输入日文(以及其他语言)了。
这种多码点候选状态还引入了几个边缘情况:在行尾输入,以及在过窄的窗口中输入。这两个问题现在也都由 Ghostty 处理好了。注意,上面的视频是在修复另一个 bug 之前录制的(如果你能发现的话),不过那个问题现在也已解决。
汉字对齐(#982)
在等宽终端网格中以整洁、协调的方式渲染多种字体(加上 Emoji)是一项挑战,而之前汉字看起来不太对劲:

注意以 草 开头的词渲染得偏低。这是因为在混合某些字体时使用了错误的字体度量计算(在这个例子中:单元格基线)。这个问题现已在 Ghostty 中修复。

Linux IME 输入(#919)
macOS 内置了对输入法编辑器(IME)的优秀支持。而在使用 GTK 的 Linux 上,IME 由可选安装的插件提供。正因如此,它们与 Ghostty 的测试不够充分,对中文、日文和韩文完全无法工作。
幸运的是,我在 Ghostty 的共享核心中做了大量支持 IME 的工作,所以在 Linux 上使其正常工作只需要 +47/-1 的改动来衔接 GTK 和 Ghostty 的 API。结果是,IME 在 Linux 上可以工作了:
注意:这段视频是在上述修复之前录制的,所以你能看到渲染错误的字符。
自定义着色器(#903)
你有没有想过让你的终端看起来像一台经典的 CRT 显示器?或者像一盘损坏的 VHS 录像带?我到底在说什么鬼话?我是不是疯了?总之,你现在可以在 Ghostty 中通过指定自定义着色器(custom shaders)实现所有这些以及更多效果。
抛开实用性不谈,这类功能看起来似乎会严重消耗电池续航。大多数现代软件的低效已经把我们所有人都当成了傻瓜!CPU 和 GPU 超级快,而这几乎没做什么事。在我的机器上,上述效果大约占用 ~1% CPU 和 ~2% GPU。它肯定比你不用这个功能时耗电更多(当然!)但还不至于让风扇转起来什么的。
Ghostty 是一个 GPU 驱动的终端模拟器。它在 macOS 上使用原生 Metal,在 Linux 上使用 OpenGL。GPU 通过调用着色器(shader)来渲染东西。着色器,含糊地挥挥手,就是运行在 GPU 上的程序。
着色器通常用特定技术相关的编程语言编写。例如,macOS 上的 Metal 使用 MSL(Metal Shading Language),OpenGL 使用 GLSL(GL Shading Language),等等。Ghostty 接受用 GLSL 编写的着色器,并在 macOS 上即时将其转换为 MSL。为此,Ghostty 使用 glslang 将 GLSL 转换为 SPIR-V(大致是一种中间表示),然后用 SPIRV-Cross 将 SPIR-V 转换为目标格式,比如 MSL。我们通过这两个工具暴露的 C API 来编译和绑定它们。
关于这个功能的另一个超酷细节是,Ghostty 暴露了 Shadertoy 接口,所以你可以把许多 Shadertoy 着色器直接放进 Ghostty 而无需任何修改。更重要的是,你还可以把 Shadertoy 本身当作 Ghostty 自定义着色器的实时交互式开发工具!
别担心,这个功能不会对性能产生负面影响。Ghostty 使用多线程渲染架构,帧与帧之间有大量的空闲时间。当使用自定义着色器时,空闲时间会减少,但它对其他线程(IO 和 GUI)几乎没有影响。当不使用自定义着色器时,Ghostty 使用与以往相同的渲染管线,不会给现有执行添加任何新资源(除了一个用于自定义着色器的布尔值检查,以及表示空的自定义着色器列表所需的最小内存占用)。
好吧,但这一切都完全没有意义,对吧?最常见的用途是好玩,那当然不是必需的(是啊,去他的乐趣,对吧?)。然而,自定义着色器的主要实际用途是无障碍性。自定义着色器是一种非常好的方式,可以有针对性地解决某些类型的色盲、调整对比度或亮度以提高可读性、创建“放大镜”效果等等。
我不是想把无障碍性问题推给别人,我也愿意把各种无障碍功能做成一等公民,但在 Ghostty 尚未具备某些功能的场景下,这是一种让用户自己动手解决问题的好方式。
Xterm 兼容性审计与行为文档化(#632)
终端模拟器是模拟器,但它们在模拟什么?传统上,终端模拟器模拟的是物理终端,如 VT52、VT100、VT220 等。实际上,由于这些硬件早已不再容易获得或实际使用,大多数现代终端模拟器只是彼此之间互相模仿行为。这导致了各种在不同终端模拟器之间不完全一致的行为,而且许多终端模拟器在边缘情况上存在 bug3。
xterm 是 X 窗口系统的标准终端模拟器,经常被誉为历史终端行为的黄金标准。此外,xterm 的使用足够广泛,以至于即使不考虑历史终端,其行为也可以被视为其所支持功能的事实标准。
我已经决定,只要可能且合理,Ghostty 就应当精确匹配 xterm 的行为。这让项目对“为什么 <feature> 会这样表现?”有一个一致的答案4。
我说的“只要可能且合理”是什么意思?我的意思是,默认情况下在实现某个功能时,我们应该匹配 xterm。如果某个功能报告了 bug,我们总是问“xterm 在这种情况下会怎么做?”如果有令人信服的理由偏离该行为,那么它就是一个例外,而不是常态。
为此,我开始慢慢地审计 xterm 的每一个有文档记录的功能,并将其与 Ghostty 进行比较。在这个过程中,我也一直在构建我们自己的文档,尽量做到尽可能详细。文档中还包含带有预期输出的 shell 脚本验证用例,以便编写端到端测试来确保兼容性。它看起来是这样的:

终端支持许多功能,xterm 的代码库很复杂,这项任务非常繁琐,所以我不会在一夜之间完成它。但在过去几个月里,我一直在逐个功能慢慢核对,到目前为止,这已经在 Ghostty 中修复了几十个 bug,也在其他终端中发现了几十个 bug(有些已上报,有些我稍后会上报,都不是关键问题)。
这并不意味着 Ghostty 会支持 xterm 支持的每一个功能。相反,对于 Ghostty 拥有的、xterm 也支持的功能,Ghostty 将力求与 xterm 最大程度兼容(允许在有充分理由的情况下例外)。
结语
对于开发日志,我只聚焦于少数我觉得有趣并想要分享的改动。开发日志不是变更日志,我也不希望它们读起来只是一份枯燥的变更列表。话虽如此,我想指出的是,在过去两个月里,Ghostty 项目还有超过 100 个 bug 修复和改进,Ghostty 正一天天接近公开可用。
随着 Beta 规模的增长,贡献者的数量也在增长。已有近 50 人为 Ghostty 做出贡献(几乎每 6 名 Beta 测试者中就有 1 人不只是报告 bug,还贡献代码!)。这真的太棒了,我非常感谢大家对这个项目投入的时间和关注。
如果你想保持最新动态,请在 Twitter 或 Mastodon 上关注我(链接在页脚)。本博客也有 RSS feed。
Boo. 👻
脚注
我不是在寻找投资者。我希望没有人以那种方式解读这一段,但我觉得必须说明这一点。这不是那种“眨眨眼说我不找”的说法,我是真的不感兴趣。↩
处理输入很复杂,我确信还有更多 bug 存在,但现在它的状态已经好到讲日语、韩语和中文的 Beta 测试者能够全天候使用 Ghostty 了。↩
几乎没有终端模拟器能正确处理拆分宽字符(如
草)的转义序列。这里的“正确”是有争议的,因为没有规范,所以我想它们想怎么做都可以。换句话说,这种行为极不一致。↩当然,这只适用于 xterm 实现的功能。有些功能如 Kitty Graphics Protocol 由 Kitty 明确定义,在这种情况下我们会尽量精确匹配 Kitty。↩
随机一篇博客