WezTerm kitty graphics 显示不出图片?把这四个坑都排掉
WezTerm 支持 kitty graphics 协议——能在终端里直接渲染图片。yazi 的图片预览、终端里看 PDF/视频,乃至把整个 GUI 应用渲染进终端的工具,都依赖它。我满心欢喜配好想用,结果:先是终端吐了一行乱码,改完又一片空白。折腾一圈下来,发现是四个坑叠在一起。这篇把排查过程和每个坑的解法记下来,免得你再踩一遍。
背景很简单:本地 WezTerm,ssh 连远程 Linux 机器,想在远程发 kitty graphics 序列、由本地 WezTerm 渲染出图。
坑 1:序列开头漏了下划线(APC)
症状:跑测试脚本,终端把一坨 base64 当文字打印出来,显示一长行乱码,没有图。
根因:kitty graphics 协议规定,所有图形指令的 escape 序列形如:
<ESC>_G<key=value,...>;<base64><ESC>\
注意开头是 ESC + 下划线 + G(\x1b_G),这叫 APC(Application Programming Command)。我一开始写成了 \x1bG(漏了下划线):
# 错误:漏了下划线,不是 APC
sys.stdout.write("\x1bGa=T,i=1,s=16,v=16,f=24;" + b64 + "\x1b\\")
# 正确:ESC _ G 三件套
sys.stdout.write("\x1b_Ga=T,i=1,s=16,v=16,f=24;" + b64 + "\x1b\\")
漏了 _,\x1bG 不再是 APC,WezTerm 压根不把它当图形命令,于是分号后面的 base64 被当作普通文字打印出来——这就是“一行乱码”。
这坑最阴的地方:\x1b_G里那个下划线,在很多等宽字体里非常不显眼,抄代码 / 写代码时极易漏。记住口诀:kitty 图形序列开头是ESC _ G三件套。
协议细节见 官方文档。
坑 2:stable 版本的 kitty graphics 实现是坏的
症状:修好序列后,变成什么都不显示,一片空白。
我按网上教程在配置里加 enable_kitty_graphics = true、重启 WezTerm——还是不行。
排查链路(见下面“诊断方法”)排除了 ssh、tmux、TERM,最后定位到:WezTerm 的 stable 版本 20240203 的 kitty graphics 实现本身是坏的。
20240203是 WezTerm 最后一个 stable 版本(2024 年 2 月,到我用时已经一年半没更新)。- GitHub 上相关 issue 一大把:#3817 “Kitty graphics protocol horribly non-conformant”、#2716 “Kitty protocol produces garbled or no output in specific cases”——“no output” 正是我的症状。
- 这些 bug 的修复只进了 nightly,stable 从未跟进。所以无论怎么配,stable 就是不出图。
诊断方法:APC query 探测(强烈推荐)
怎么确认是“终端不支持”,而不是“序列写错了”或“中间层(ssh/tmux)把序列吞了”?用 kitty graphics 的 query action 配合 DA1 来探测。
协议规定:发一个 query(a=q),支持的终端会回 OK 或错误信息;紧接着发一个 DA1(Primary Device Attributes,几乎所有终端都回)。如果你只收到 DA1、没收到 query 的回包,就说明终端不支持 kitty graphics。
# 在被测终端的交互 shell 里跑
python3 -c '
import sys,os,select,time,termios,tty
fd=sys.stdin.fileno()
old=termios.tcgetattr(fd)
tty.setraw(fd)
sys.stdout.write("\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\") # kitty query
sys.stdout.write("\x1b[c") # DA1
sys.stdout.flush(); time.sleep(0.15)
resp=b""
while select.select([sys.stdin],[],[],0.4)[0]:
c=os.read(fd,4096)
if not c: break
resp+=c
termios.tcsetattr(fd,termios.TCSADRAIN,old); sys.stdout.write("\r\n")
print(repr(resp))
print("kitty supported:", b"_G" in resp)
'
判读:
- 回包里有
\x1b_Gi=31;OK→ 支持。 - 只有
\x1b[?...c(DA1)、没有_G→ 不支持 / 未启用。
我那台 stable WezTerm 跑出来就是只有 DA1、没有 kitty 回包——铁证。
两个小坑:
- python 读响应要用
termios(Unix)。别用python3 - <<'EOF'这种 heredoc 喂代码——heredoc 会占用 stdin,导致tcgetattr报ENOTTY (25, Inappropriate ioctl for device)。要用python3 -c '...',让 stdin 保持是 tty。 - 先
echo "$TMUX $STY"排除 tmux/screen(它们默认会吞掉 APC 序列,需要set -g allow-passthrough on,tmux ≥ 3.3)。再在本地直连(不经 ssh)测一遍,排除 ssh 链路。
Windows 本地(PowerShell)直连测试,不依赖 python/bash:
$e=[char]27; [Console]::Write("${e}_Ga=T,f=24,s=80,v=80,c=4,r=4;" + [Convert]::ToBase64String([byte[]](@(255,0,0)*6400)) + "${e}\")
我本地直连也是空白——彻底排除 ssh/tmux,坐实是 WezTerm 自身的问题。
解法:换 nightly
WezTerm 没有“正式稳定版”的发布节奏,主力是 nightly(每天从 main 分支构建),stable 已经一年半没动。kitty graphics 的修复都在 nightly。
Windows 便携版下载(GitHub releases 的 nightly tag):
https://github.com/wezterm/wezterm/releases/download/nightly/WezTerm-windows-nightly.zip
下载、解压、跑里面的 wezterm-gui.exe(它和 stable 共用 ~/.wezterm.lua 配置)。再跑一遍上面的 APC query 探测:回包变成 \x1b_Gi=31;OK、kitty supported: True,红块也出来了。
如果你用 scoop,注意 scoop 的 extras bucket 只有 stable;nightly 要么手动下便携版,要么找别的途径。我是把 nightly 便携版解压到固定目录、加进 PATH、建好快捷方式,再卸掉了 scoop 的 stable。
坑 3:enable_kitty_graphics 的默认值(你以为要开,其实新版默认就开)
排查时我一直纠结 enable_kitty_graphics 默认到底是 true 还是 false——很多老文章说“要手动开”,也有人说“2021 年底就默认开了”,众说纷纭。
我的实测结论:
- 在坏的 stable 上,设不设都一样(版本 bug,开了也没用)。
- 在 nightly 上,默认就是开启的,这行是冗余的。
佐证:去翻新版官方文档(wezterm.org),enable_kitty_graphics 这个选项的页面直接 404,配置选项总览里也不再列它——说明新版已经把它移除了(默认开启、不可关闭)。我把这行从配置里注释掉,nightly 照样出图。
所以:用 nightly 的话,.wezterm.lua 里不用写 enable_kitty_graphics = true。
坑 4:term 默认是 xterm-256color,不是 wezterm
顺带一个容易混的点:WezTerm 的 term 配置项(决定发给子进程的 $TERM)默认是 xterm-256color,不是 wezterm。
如果你想要 TERM=wezterm(启用彩色 / 样式下划线、undercurl、斜体等高级 terminfo 特性),需要:
- 在远程机器装 wezterm 的 terminfo:
curl -o /tmp/w https://raw.githubusercontent.com/wezterm/wezterm/main/termwiz/data/wezterm.terminfo tic -x -o ~/.terminfo /tmp/w - 配置里设
config.term = 'wezterm'。
kitty graphics 协议本身不依赖 TERM/terminfo(它是 APC escape 序列,终端直接解析)。term=wezterm 影响的是 ncurses 那类程序的高级显示能力,和出不出图没关系——别把它们混为一谈。
检查清单
把上面四个坑浓缩成一张排查表:
| 症状 | 查什么 |
|---|---|
| 终端打印出一行 base64 / 乱码 | 序列开头是不是 \x1b_G(APC,下划线别漏),不是 \x1bG |
| 什么都不显示、空白 | ① tmux/screen 是否吞了 APC(echo $TMUX)② WezTerm 版本是不是停在 stable 20240203(换 nightly) |
| 想确认终端到底支不支持 | 跑 APC query 探测,看回包有没有 \x1b_Gi=31;OK |
配了 enable_kitty_graphics=true 没用 | stable 上是版本 bug;nightly 默认就开,这行多余 |
| 远程程序样式 / 下划线不对 | term 默认 xterm-256color;要 wezterm 得在远程装 terminfo |
一句话总结:WezTerm 出不了 kitty graphics 图,先查序列的 _,再查版本别停在老 stable,剩下都是配置细节。