Ghostty 开发日志 003
原文由 Mitchell Hashimoto 于 发布,订阅该博客
大家好!欢迎阅读 Ghostty 的第三期官方开发日志 👻!
如果你错过了之前的开发日志,或想进一步了解 Ghostty 是什么,请访问本网站上的 Ghostty 介绍页面。
社区动态:公开 Discord 服务器上线!
很高兴地告诉大家,我们现在有了公开的 Discord 服务器!欢迎任何人加入,在里面咨询关于 Ghostty 的问题、聊聊终端模拟器、聊聊系统编程、GPU 编程等等。我们也会通过 Discord 来扩大内测规模,所以如果你想加入内测,请加入 Discord 服务器并保持一定的活跃度。
另外,我将在 9 月 5 日在 Zig Showtime 上做一场关于 Ghostty 的分享。请关注 Zig Showtime 官网、Twitter、Mastodon 等渠道,获取具体时间和链接的更新。分享内容仍在准备中,大致会是对 Ghostty 实现的一次快速巡览,并重点介绍几个有意思的细节。毕竟是 Zig Showtime,所以我会更侧重于 Ghostty 中与 Zig 相关的部分。
键盘输入处理恨透了你(#282)1
我不过是个用着破烂美式标准键盘布局的、傻乎乎的美国人。在美式标准键盘上,你按下 a 就得到“a”,按下 '(单引号)就得到“'”。你按下 shift+a 就得到“A”。简单得很。你不过是坐在谎言的王座上罢了。
麻烦的第一个苗头是那些非标准但仍以英语为中心的键盘布局,比如 Dvorak 或 Colemak。在美式标准物理键盘上,Dvorak 用户按下 s 期待得到“o”,按下 ctrl+i 则期待触发 ctrl+c 的行为。
麻烦的第二个苗头是 Unicode 序列。在 Mac 上,使用美式标准布局时,alt+a 会产生 å。最新一批内测用户中有一位挪威用户指出,这一点尤其重要,因为他更偏好美式标准布局,但会用 alt 组合来输入挪威语单词。
麻烦的第三个苗头是非标准键盘布局。你知道什么是死键序列吗?我之前就不知道(别忘了,我就是个傻乎乎的美式标准用户)。在美式标准布局上,我们习惯了所有按键都是无状态的。要输入大写字母,你需要同时按下 shift+A。而有了死键序列,你可以先按下 shift,松开,再按 a,然后就会看到 A(这不是真实存在的死键序列,只是用来说明死键序列的工作方式)。一个真实的死键序列,在美式国际布局上:先按 ',松开,再按 a,你就会得到 á。
当一个人在键盘上按下某个键时,会发生几件事。键盘会发送一个物理扫描码。软件层面的键盘布局处理(比如在 QWERTY 物理键盘上使用 Dvorak 布局)会把这个扫描码翻译成另一个扫描码(在 Dvorak 下“i”的作用相当于“c”)。修饰键(ctrl、alt 等)与其他按键(比如字母键)的组合可能会产生一个或多个字符,也可能不会(例如在 macOS 上 alt+p 会产生“π”,但在美式物理键盘上使用 Dvorak 时,同样的效果需要按 alt+r)。
最后,并非所有修饰键都会被消耗掉,这一点对快捷键绑定至关重要。例如,如果你在美式布局上绑定了 ctrl+=,这在挪威布局上相当于 ctrl+shift+0,你必须知道 shift+0 产生了 =,但并没有消耗掉 ctrl,所以最终的输入是 ctrl+=。但如果用户同时还绑定了 ctrl+shift+0,那到底该用哪一个呢?🤔
这些解释都非常粗略,缺乏细节。我打算专门写一篇博文,来详述键盘布局、输入处理、终端键盘“协议”以及操作系统层面 API 这些宛如克苏鲁般的恐怖之处,所以在这篇开发日志里就先略过细节了。敬请期待!
Ghostty 不仅要处理这些用例(因为事实证明,非美式标准键盘的用户是真实存在的),还必须跨平台地处理这些问题,因为 Ghostty 已经可以在 macOS 和 Linux 上运行,未来还希望支持 Windows。😰 好吧,Ghostty 现在已经做到了。
唯一能直观展示的是死键状态的处理,现在会把最后一个待处理的死键渲染出来。其余部分是否正常工作,就请相信我吧。
除此之外,Ghostty 现在还支持可选的显式物理按键映射。如果你将快捷键指定为 ctrl+physical:0(注意 physical: 前缀),那么只有物理位置上的 0 键才会触发该绑定。在某些键盘布局下,这可能会让该快捷键无法触发。默认情况下,快捷键绑定基于逻辑(经转换后的)按键。
Kitty 键盘协议及其他(#292、#295)
如果你已经听腻了键盘输入的话题,直接跳过这一节就好。
除了把键盘按键映射为字符输入外,键盘事件还必须映射为转义序列,这样在终端中运行的程序才能知道你按了什么键。
最初的终端协议会按原样发送键盘按键产生的字符。你输入“a”,就得到“a”;你输入“A”(shift+A),就得到“A”。这有很多局限性。例如,终端程序无法区分 ctrl-i 和 ctrl-shift-i 这样的修饰键组合,因为它们产生的数据是相同的。
为了解决这些不足,人们开发了许多额外的键盘协议。xterm 开发了修饰键序列,Paul Evans 开发了 fixterms 协议,Kovid Goyal(Kitty 的作者)则开发了 Kitty 键盘协议。这些协议的核心,就是定义当按键被按下时,终端应该向正在运行的程序发送什么内容。
这些并非假设性的问题。tmux、neovim 等主流程序都已经实现了这些协议,它们能让你绑定比以往多出数百种的快捷键。
Ghostty 现在已全部实现了这些协议。
据我所知,Ghostty 是少数几个全部实现了这些协议的终端之一。我认为只有 iTerm、Foot 以及可能还有 WezTerm 同时实现了 xterm 修饰键和 Kitty 协议。我想也只有它们加上 Kitty 本身实现了 Kitty 协议。大多数终端(Terminal.app、Windows Terminal、Alacritty、Warp)则一个键盘协议都没有实现。2
下面是 Kitty 键盘协议工作效果的视频:
下面是 xterm 修饰键协议工作效果的视频:
Linux 桌面之年(GTK)
最新一批内测用户让我们的全职 Linux 用户数量从之前的两三人增加到了五六人,随之而来的是 Linux 体验上的诸多改进。
首先,GTK 单实例模式(#247)现已启用并得到支持。在 GTK 版本中,Ghostty 现在以单进程运行。第二次启动 Ghostty 时,会在已有的 Ghostty 进程中打开一个新窗口。这能很好地与标准图形桌面环境协作,也让 Ghostty 在首次启动之后的再次启动变得飞快。
其次,Ghostty 现在支持主(选择)剪贴板(#266)。Linux 桌面有多个剪贴板。“主”剪贴板(或称“选择”剪贴板)会在选中文本时自动复制内容,通常通过鼠标中键粘贴。这是一个与 ctrl+shift+c 和 ctrl+shift+v 所用的剪贴板相互独立的剪贴板,因此你可以同时在剪贴板中保存多个内容。这个功能有时颇具争议,不过别担心,你可以通过 copy-on-select 配置项将其关闭。你可以在下面的视频中看到它的效果。请注意,我没有按下任何快捷键,这完全是通过选择剪贴板实现的。
Linux 现在支持自动 shell 集成(#245)。我在开发日志 001 的 shell 集成部分中谈到过这一点。此前,Linux 用户必须设置一个环境变量来指向 Ghostty 的 share 目录。现在 Linux 版应用会从 ghostty 可执行文件所在位置向上查找具有正确目录结构的 share 目录。如果你使用标准的 FHS 目录布局(Ghostty 打包所采用的标准布局)安装 Ghostty,那么 Ghostty 将会自动找到并配置你的 shell。
还有更多改进,Ghostty 的应用图标现在能正常显示了(#251),GTK 版应用能处理非美式键盘布局了(#271),GTK 版应用支持半透明窗口背景了(#264),等等。
我非常希望 Ghostty 能成为基于 GTK 的顶级 Linux 终端。我自己并不是全职使用 GTK,所以非常依赖测试者们提供反馈来把体验做得更好。很高兴地告诉大家,Ghostty 在 GTK 上的体验现在已经非常不错了!GTK 版应用在体验上仍落后于 macOS 版,但我期待着能让两者达到同等水平!
尾声
最近一批内测用户真的太棒了。我要感谢 @hovsater,是他暴露了所有明显的键盘布局和映射问题,并推动我一一修复。感谢 @cryptocode 发现了十几个与宽字符、多码点字形簇及其与选区和软换行交互相关的边缘情况缺陷。还有许多为 Linux 改进做出贡献的新测试者值得感谢。谢谢大家!
如果你想及时了解最新动态,请在 Twitter 或 Mastodon 上关注我(链接在页脚)。本博客也提供 RSS 订阅。
Boo. 👻
脚注
这是对优秀博文 Text Rendering Hates You 的致敬,强烈推荐阅读。↩
我非常尊重这些终端模拟器的作者,希望这不会被视为任何攻击。我只是想客观陈述它们支持和不支持的功能。如果我说错了,请告诉我,我会立即更正。↩
随机一篇博客
评论
登录后参与讨论