Skip to content

【Zig 日报】⚡zt — the fastest terminal emulator #317

@jiacai2050

Description

@jiacai2050

zt 是一款用 Zig 编写的极简终端模拟器。它直接渲染到 Linux 帧缓冲区,通过共享内存渲染到 X11,通过纯 Zig 线协议渲染到 Wayland,或通过 Cocoa/AppKit 渲染到 macOS。无需 GPU。

专为 HackberryPi Zero (RPi Zero 2W + 720x720 HyperPixel4) 构建,但可在任何 Linux 系统上运行。

Image

特性

  • 四重后端 – 帧缓冲区直接渲染(无需 X11/Wayland),XCB + SHM 在 X11 下,纯 Zig Wayland 客户端(无需 libwayland),或 macOS 上的 Cocoa/AppKit
  • 编译时一切 – 后端、字体、调色板、像素缩放都在编译时确定。未使用代码路径没有运行时开销
  • 像素缩放 – 使用 -Dscale=2-Dscale=4 进行 HiDPI/PC 显示。整数缩放将每个位图像素渲染为 NxN 块。相同的字体块,无质量损失
  • 行映射滚动 – 通过行间接表进行 O(1) 滚动,而不是单元格复制。60K 滚动移动 44MB 的指针,而不是 880MB 的单元格数据
  • 脏区域跟踪 – 单元格级别的脏位图,具有 O(1) 标志、行级别跳过、脏区域存在。滚动仅将回收的行标记为脏
  • 双缓冲 SHM – 延迟初始化第二缓冲区的无撕裂 X11 渲染(无启动开销)
  • XKB 键盘布局 – 通过 libxkbcommon 自动支持任何 X11/Wayland 键盘布局(美国、日本、德国、法国等)
  • 输入法 – X11 下的 XIM,Wayland 下的 text-input-v3。通过 fcitx5、ibus 等进行日语/中文/韩语输入
  • xterm-256color + 24 位 TrueColor – 完全支持 SGR 属性(粗体、斜体、下划线、反转、暗淡),DEC 模式,备用屏幕
  • CJK 宽字符支持 – 正确的双宽度渲染,在擦除/删除时修复宽字符边界
  • 59,635 个字形 – UFO 位图字体 + Nerd Fonts 图标,嵌入为二进制块
  • 自适应帧限制器 – 基于输出量,4 级自适应 FPS (120→60→15→5)。在极端输出期间,降至 5fps 以获得最大的解析吞吐量。动态 epoll 超时,实现零浪费的空闲状态
  • 批量 ASCII 快速路径 – VT 解析器使用 SIMD 范围检查 (@vector 16 字节) 和基于范围的脏标记直接写入单元格数组
  • UTF-8 批量路径 – 直接解码基态多字节字符,绕过逐字节解析状态机
  • 滚动像素 memmove – 滚动时,通过 memmove 移动像素缓冲区,并仅重新渲染回收的行。饱和滚动回退到使用全局背景填充的完全重新渲染
  • PTY 排水循环 – 在渲染之前读取所有可用数据(可配置缓冲区,-Dpty_buf_kb,默认 1MB),从而减少大容量输出期间的帧数
  • 写入缓冲 – 在反压下缓冲 PTY 写入,并使用 EPOLLOUT 重试
  • ConfigureNotify 合并 – 拖动调整大小仅处理最终大小,跳过中间重新分配
  • 无 libc (fbdev) – 纯 std.posix 系统调用,单个静态二进制文件
  • 73 个单元测试,分布在 7 个模块中

状态

  • fbdev: 稳定
  • X11: 稳定
  • Wayland: 进行中 – 窗口拖动到边缘时崩溃
  • macOS: 实验性

数字

fbdev X11 Wayland
二进制文件 (包含 59K 字形字体) 2.8 MB 2.8 MB 2.8 MB
运行时依赖 libxcb, libxcb-shm, libxcb-xkb, libxkbcommon, libxcb-imdkit libxkbcommon
构建时间 < 1s < 1s < 1s
源代码 ~10K 行,19 个文件

基准测试

在 Intel i5-12450H、1 个 CPU 核心、真实显示器 (:0, 硬件 GPU) 上测量,-Doptimize=ReleaseFast。请参阅 zt-bench 以获取完整的基准测试套件和方法。

启动时间 (hyperfine, 30 次运行)

时间 vs zt
zt 5.6ms 1.0x
xterm 26ms 4.6x
foot 47ms 8.4x
st 52ms 9.3x
alacritty 136ms 24x
kitty 202ms 36x
ghostty 492ms 87x

吞吐量:密集 ASCII (4.7MB, 5 次运行)

时间 MB/s vs zt
zt 64ms 73 1.0x
foot 134ms 35 2.1x
st 174ms 27 2.7x
xterm 196ms 24 3.1x
alacritty 252ms 19 3.9x
kitty 354ms 13 5.5x
ghostty 704ms 7 11x

吞吐量:TrueColor (292KB, 5 次运行)

时间 MB/s vs zt
zt 2ms 95 1.0x
xterm 32ms 6 16x
st 52ms 4 26x
foot 60ms 3 30x
alacritty 140ms 1 70x
kitty 250ms <1 125x
ghostty 496ms <1 248x

吞吐量:Unicode/CJK (300KB, 5 次运行)

时间 MB/s vs zt
zt 4ms 49 1.0x
xterm 30ms 7 7.5x
st 58ms 3 15x
foot 62ms 3 16x
alacritty 144ms 1 36x
kitty 250ms <1 63x
ghostty 490ms <1 123x

空闲内存 (PSS)

RSS PSS vs zt
zt 4.9 MB 2.2 MB 1.0x
xterm 11 MB 4.5 MB 2.1x
foot 24 MB 11 MB 5.0x
st 29 MB 15 MB 6.8x
alacritty 110 MB 35 MB 16x
kitty 139 MB 53 MB 24x
ghostty 221 MB 96 MB 44x

构建

需要 Zig 0.15+。

构建配置

  • PC – X11: zig build -Dbackend=x11 -Doptimize=ReleaseFast
  • PC – Wayland (Sway, Hyprland, GNOME, KDE 等): zig build -Dbackend=wayland -Doptimize=ReleaseFast
  • HackberryPi (最小尺寸): zig build -Doptimize=ReleaseSmall

ReleaseFast 启用激进的内联、循环展开和 SIMD 自动矢量化。ReleaseSmall 最小化二进制文件大小,适用于受限设备(512MB RAM)。

示例

# fbdev (默认) – 在裸 Linux 控制台上运行
zig build -Doptimize=ReleaseSmall

# X11 – 在 X11 窗口管理器下运行
zig build -Dbackend=x11 -Doptimize=ReleaseFast

# Wayland – 在 Wayland 合成器下运行 (Sway, Hyprland, GNOME, KDE 等)
zig build -Dbackend=wayland -Doptimize=ReleaseFast

# Wayland 带有 2x 像素缩放,用于 HiDPI 显示器
zig build -Dbackend=wayland -Dscale=2 -Doptimize=ReleaseFast

# X11 带有 2x 像素缩放,用于 PC/HiDPI 显示器
zig build -Dbackend=x11 -Dscale=2 -Doptimize=ReleaseFast

# X11 带有 4x 像素缩放,用于 4K 显示器
zig build -Dbackend=x11 -Dscale=4 -Doptimize=ReleaseFast

# X11/Wayland 带有 60fps 限制 (省电)
zig build -Dbackend=x11 -Dmax_fps=60 -Doptimize=ReleaseFast

# X11/Wayland 带有无限帧率 (无限制)
zig build -Dbackend=x11 -Dmax_fps=0 -Doptimize=ReleaseFast

# X11/Wayland 带有较小的 PTY 缓冲区 (在 RPi 上节省内存)
zig build -Dbackend=x11 -Dpty_buf_kb=256 -Doptimize=ReleaseSmall

# fbdev 带有 JIS 键盘布局 (默认: us)
zig build -Dkeymap=jp -Doptimize=ReleaseSmall

# 交叉编译为 aarch64 (fbdev, 静态二进制文件)
zig build -Dtarget=aarch64-linux -Doptimize=ReleaseSmall

# 交叉编译为 aarch64 (X11, 需要目标 sysroot 包含 XCB 库/头文件)
zig build -Dtarget=aarch64-linux-gnu.2.38 -Dbackend=x11 -Doptimize=ReleaseFast \
  --search-prefix /path/to/aarch64-sysroot

# macOS (实验性 – 请参阅下面的说明)
zig build -Dbackend=macos -Dshell=/bin/zsh -Doptimize=ReleaseFast

# 自定义 shell (默认: /bin/sh)
zig build -Dbackend=x11 -Dshell=/bin/fish -Doptimize=ReleaseFast

# 运行测试
zig build test

Wayland 后端

Wayland 后端直接实现线协议,纯 Zig 编写 – 没有 libwayland-client 依赖。仅需要 libxkbcommon 用于键盘布局支持。

支持的协议:xdg-shell (窗口管理),wl_shm (渲染),text-input-v3 (IME),wl_data_device + primary selection (剪贴板),xdg-decoration (服务器端装饰),wp_cursor_shape_manager_v1 (光标)。

可在任何符合 xdg-shell 规范的合成器上运行:Sway、Hyprland、GNOME、KDE、river 等。

macOS 后端 (实验性)

注意: macOS 后端是在没有 macOS 硬件访问权限的情况下开发的,尚未在真正的 Mac 上进行测试。它使用 Cocoa/AppKit 通过 objc_msgSend 从 Zig,使用 CGBitmapContext 进行像素渲染,使用 NSTextInputClient 进行 IME 支持。欢迎提交错误报告和补丁。

需要 macOS SDK (Xcode 或命令行工具)。

配置

编辑 config.zig 并重新构建 – st 风格,没有运行时配置文件。

pub const backend: Backend = .fbdev;  // .fbdev, .x11, .wayland, or .macos (set via -Dbackend)
pub const keymap: Keymap = .us;       // .us or .jp (set via -Dkeymap; fbdev only, X11 uses XKB)
pub const default_fg: u8 = 7;        // white
pub const default_bg: u8 = 0;        // black
pub const font_width: u32 = 8;       // bitmap glyph width (half-width)
pub const font_height: u32 = 16;     // bitmap glyph height
pub const scale: u32 = 1;            // pixel scale factor: 1, 2, or 4 (set via -Dscale)
pub const max_fps: u32 = 120;        // max frame rate: 0 = unlimited (set via -Dmax_fps)
pub const frame_min_ns: u64 = ...;   // computed: 1_000_000_000 / max_fps (0 if unlimited)
pub const pty_buf_size: u32 = ...;   // PTY read buffer in bytes (set via -Dpty_buf_kb, default 1024)
pub const cell_width = font_width * scale;   // screen cell width
pub const cell_height = font_height * scale; // screen cell height
// shell: set via -Dshell= (default: /bin/sh)

字体

zt 嵌入了一个预编译的二进制字体块,在编译时通过 @embedFile。默认字体包含 62,595 个字形(日语、Nerd Fonts 图标、表情符号)。

# 下载预构建字体 (推荐)
curl -Lo src/fonts/ufo-nf.bin https://github.com/midasdf/zt-fonts/raw/main/ufo-nf.bin

请参阅 zt-fonts 以获取 BDF 源代码、构建脚本、替代字体和自定义字体创建。

架构

  • epoll 事件循环 (单线程,动态超时)
    • PTY 读取器 (1MB 缓冲区,排水循环)
      • VT 解析器 (逐字节状态机)
        • ASCII 快速路径:SIMD 16 字节范围检查 + 批量写入 cells[]
        • UTF-8 快速路径:直接解码,绕过解析器状态机
        • 操作执行器 → Cell 网格突变通过 row_map
    • 输入处理程序
      • evdev (fbdev) – 原始键盘事件,编译时 keymap (US/JP)
      • X11 – XKB + XIM (延迟初始化于第一个按键按下)
      • Wayland – XKB + text-input-v3 (IME),客户端侧按键重复
    • Term 网格
      • row_map[逻辑] → 物理:O(1) 滚动通过指针旋转
      • 脏位图 (逻辑顺序) 具有 O(1) hasDirty 标志
      • TrueColor 稀疏映射 (物理键,滚动时不移动)
    • 渲染器 (帧率限制,-Dmax_fps=120 默认)
      • hasDirty() O(1) 标志检查 → 如果没有更改则跳过
      • 帧限制器:如果自上一帧以来小于 frame_min_ns 则跳过渲染
      • isRowDirty() → 跳过干净的行
      • 逐单元格:字形查找 → 缩放像素组合 (memcpy 行复制)
    • 后端
      • fbdev:影子缓冲区 → 脏行 memcpy 到 /dev/fb0 mmap
      • X11:双缓冲 SHM → 脏区域 xcb_shm_put_image
      • Wayland:双缓冲 wl_shm → 脏区域 damage_buffer + commit
    • 信号处理 (signalfd: SIGCHLD, SIGTERM, SIGINT, SIGHUP)
    • VT 切换 (fbdev: SIGUSR1/2 用于控制台切换)
    • 光标闪烁 (timerfd, 500ms 间隔)
    • 写入缓冲区 (4KB, EPOLLOUT 驱动的反压重试)

源文件

文件 行数 目的
src/vt.zig 1,859 VT 解析器状态机 + 操作执行器,SIMD ASCII 快速路径,UTF-8 批量路径
src/backend/x11.zig 950 XCB 窗口,双缓冲 SHM,XKB + XIM (延迟初始化),ConfigureNotify 合并
src/term.zig 1,080 Cell 网格,具有 row_map 间接,O(1) 脏标志,滚动,擦除,BCE,TrueColor 稀疏映射
src/main.zig 559 事件循环,帧限制器,信号/定时器设置,PTY 排水,写入缓冲,渲染编排
src/input.zig 527 Keymap (US/JP),evdev 代码转换,修饰符处理
src/render.zig 389 像素渲染,编译时缩放 (BGRA32/RGB565/RGB24),memcpy 行复制
src/backend/wayland.zig 1,099 纯 Zig Wayland 客户端:wl_shm 双缓冲,xdg-shell,事件分发,内部 epoll
src/backend/wayland/*.zig 2,028 线协议,core/xdg-shell/seat/text-input-v3/clipboard/decoration 模块
src/backend/fbdev.zig 319 帧缓冲区 mmap,影子缓冲区,evdev 键盘扫描,VT 切换
src/font.zig 353 二进制块加载器,编译时 ASCII 缓存,256 个槽运行时字形缓存
src/pty.zig 221 PTY 生成,非阻塞 I/O,调整大小 (TIOCSWINSZ)
config.zig 38 编译时配置 (后端、keymap、字体、颜色、缩放、max_fps)
build.zig 72 构建系统,具有后端、keymap、缩放和 max_fps 选择

支持的转义序列

CSI 序列

序列 名称 描述
CSI n A CUU 光标上移
CSI n B/e CUD/VPR 光标下移
CSI n C/a CUF/HPR 光标右移
CSI n D CUB 光标左移
CSI n E CNL 光标下一行
CSI n F CPL 光标前一行
`CSI n G/`` CHA/HPA 光标水平绝对位置
CSI n;m H/f CUP 光标位置
CSI n I CHT 光标前向制表符
CSI n J ED 擦除显示 (0: 下方, 1: 上方, 2/3: 全部)
CSI n K EL 擦除行 (0: 右侧, 1: 左侧, 2: 全部)
CSI n L IL 插入行
CSI n M DL 删除行
CSI n P DCH 删除字符
CSI n X ECH 擦除字符
CSI n @ ICH 插入字符
CSI n Z CBT 光标后退制表符
CSI n b REP 重复前一个图形字符
CSI n S SU 向上滚动
CSI n T SD 向下滚动
CSI n d VPA 垂直位置绝对位置
CSI n g TBC 制表符清除 (0: 当前, 3: 全部)
CSI t;b r DECSTBM 设置滚动区域 (重置光标到首页)
CSI s 保存光标位置
CSI u 恢复光标位置
CSI 5 n DSR 设备状态报告 (OK)
CSI 6 n DSR 设备状态报告 (光标位置)
CSI c DA1 设备属性 (报告 VT220)
CSI > c DA2 二级设备属性
CSI > 0 q XTVERSION 终端标识 (响应 zt 版本)
CSI ! p DECSTR 软终端重置
CSI Ps SP q DECSCUSR 设置光标样式
CSI Ps $ p DECRQM 模式查询 (响应模式状态)
CSI ! p DECSTR 软终端重置
CSI " p DECSCL 符合性级别 (静默接受)
CSI " q DECSCA 设置字符保护属性
CSI Ps t XTWINOPS 窗口操作 (静默接受)
CSI i MC 媒体复制 (静默接受)
CSI 4 h/l IRM 插入/替换模式
CSI 20 h/l LNM 行馈/换行模式
CSI ... m SGR 选择图形渲染 (见下文)

所有 CSI 私有标记 (?, >, <, =) 都正确解析。未知的私有标记序列被静默忽略。

SGR (选择图形渲染)

代码 效果
0 重置全部
1 粗体
2 变暗
3 斜体
4 下划线
5, 6 闪烁
7 反转视频
8 不可见
9 划掉
21 双下划线
22 正常强度
23 非斜体
24 非下划线
25 非闪烁
27 非反转
28 非不可见
29 非划掉
30-37 前景色 (标准)
38;5;n 前景色 256 色
38;2;r;g;b 前景色 24 位 TrueColor
39 默认前景色
40-47 背景色 (标准)
48;5;n 背景色 256 色
48;2;r;g;b 背景色 24 位 TrueColor
49 默认背景色
90-97 前景色明亮
100-107 背景色明亮

DEC 私有模式

模式 名称 描述
?1 DECCKM 应用程序光标键
?6 DECOM 起源模式
?7 DECAWM 自动换行模式
?25 DECTCEM 光标可见
?47 备用屏幕缓冲区
?1047 备用屏幕缓冲区
?1048 保存/恢复光标
?1049 备用屏幕 + 保存/恢复光标
?2 DECANM VT52/VT100 模式切换
?67 DECBKM 退格键模式 (BS vs DEL)
?2004 括号粘贴模式
?2026 同步更新
?1000-1006 鼠标跟踪模式 (静默接受)
?1004 焦点事件 (发送 CSI I/O)

VT52 模式

通过 CSI ? 2 l (DECANM 重置) 进入。通过 ESC < 退出。

序列 描述
ESC A-D 光标移动 (上/下/右/左)
ESC H 首页
ESC I 反向换行
ESC J 擦除到屏幕末尾
ESC K 擦除到行末尾
ESC F/G 进入/退出图形模式
ESC Z 标识 (响应 ESC / Z)
ESC < 退出 VT52,进入 VT100

转义序列

序列 名称 描述
ESC 7 DECSC 保存光标 + 属性 + 字符集
ESC 8 DECRC 恢复光标 + 属性 + 字符集
ESC D IND 索引 (换行,底部滚动)
ESC E NEL 下一行
ESC H HTS 水平制表符停止
ESC M RI 反向索引
ESC Z DECID 标识终端
ESC F 光标移动到左下角
ESC c RIS 完全重置
ESC n LS2 锁定 Shift 2 (激活 G2)
ESC o LS3 锁定 Shift 3 (激活 G3)
ESC = DECKPAM 应用程序键盘模式
ESC > DECKPNM 正常键盘模式
ESC ( 0 G0 字符集 = DEC 特殊图形 (线条绘制)
ESC ( B G0 字符集 = US ASCII
ESC % G 选择 UTF-8 字符集 (静默接受)
ESC # 8 DECALN 屏幕对齐测试

OSC 序列

序列 描述
OSC 0 ; Pt 设置窗口标题 + 图标名称
OSC 2 ; Pt 设置窗口标题
OSC 10 ; ? 查询前景色
OSC 11 ; ? 查询背景色
OSC 12 ; ? 查询光标颜色
OSC 52 剪贴板 (静默接受)
OSC 104 重置颜色 (静默接受)

DCS 序列

序列 描述
DCS + q XTVGETTCAP – 查询终端功能
DCS $ q DECRQSS – 查询状态字符串 (SGR, DECSTBM, DECSCUSR)

已测试的应用程序

以下应用程序已在 Xvfb 集成测试下验证工作:vim, nano, micro, less, bat, top, btop, man, git (log/diff/status), eza, tree, ripgrep, python3 REPL, fish (补全、历史记录、Ctrl+C、Ctrl+L), Claude Code (Anthropic CLI)

限制

  • 没有滚动缓冲区 – 仅保留当前视口
  • fbdev/X11 没有剪贴板 (Wayland 支持 Ctrl+Shift+V 粘贴和主选择)
  • 没有鼠标支持
  • fbdev keymap 仅在编译时,(US/JP); X11 使用 XKB 进行任何布局
  • 没有内联预编辑显示 – IME 候选窗口由输入法处理 (fcitx5 默认)
  • 没有字体回退链 – 单个嵌入字体
  • 没有连字
  • 没有六边形/图像协议支持

许可证

MIT

加入我们

Zig 中文社区是一个开放的组织,我们致力于推广 Zig 在中文群体中的使用,有多种方式可以参与进来:

  1. 供稿,分享自己使用 Zig 的心得
  2. 改进 ZigCC 组织下的开源项目
  3. 加入微信群Telegram 群组

Metadata

Metadata

Assignees

No one assigned

    Labels

    日报daily report

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions