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 实现本身是坏的

诊断方法: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)
'

判读:

我那台 stable WezTerm 跑出来就是只有 DA1、没有 kitty 回包——铁证。

两个小坑:

  1. python 读响应要用 termios(Unix)。别用 python3 - <<'EOF' 这种 heredoc 喂代码——heredoc 会占用 stdin,导致 tcgetattrENOTTY (25, Inappropriate ioctl for device)。要用 python3 -c '...',让 stdin 保持是 tty。
  2. 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;OKkitty supported: True,红块也出来了。

如果你用 scoop,注意 scoop 的 extras bucket 只有 stable;nightly 要么手动下便携版,要么找别的途径。我是把 nightly 便携版解压到固定目录、加进 PATH、建好快捷方式,再卸掉了 scoop 的 stable。

坑 3:enable_kitty_graphics 的默认值(你以为要开,其实新版默认就开)

排查时我一直纠结 enable_kitty_graphics 默认到底是 true 还是 false——很多老文章说“要手动开”,也有人说“2021 年底就默认开了”,众说纷纭。

我的实测结论:

佐证:去翻新版官方文档(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 特性),需要:

  1. 在远程机器装 wezterm 的 terminfo:
    curl -o /tmp/w https://raw.githubusercontent.com/wezterm/wezterm/main/termwiz/data/wezterm.terminfo
    tic -x -o ~/.terminfo /tmp/w
  2. 配置里设 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,剩下都是配置细节。