APC —— 应用程序命令(`ESC _ … ESC \\` / `\x9F … \x9C`)
ECMA-48 为应用对应用消息保留的字符串引导符。现代用途:Kitty 图形协议、VS Code shell 集成、Windows Terminal API 补丁。与 DCS / OSC / PM / SOS 同辈。
字节形式
涵盖所有常见的字符串字面量写法,方便正反查找。
\x1b_<body>\x1b\\ (7-bit) \x9f<body>\x9c (8-bit C1)\033_<body>\033\\ (7-bit) \237<body>\234 (8-bit C1)\e_<body>\e\\ESC _ <body> ESC \\ or 0x9F <body> 0x9C1b 5f <body> 1b 5c / 9f <body> 9c说明
APC(*应用程序命令*)是 ECMA-48 为应用对应用消息保留的字符串引导符 —— 终端应当透明转发给同 TTY 上监听的对端应用、不自行解释主体的字节。7 位形 \x1b_<body>\x1b\\(ESC _ + 不透明字节 + ST);8 位 C1 形为单字节 \x9F 引导 + 主体 + 单字节 \x9C 终止。与 DCS(\x1bP,设备控制)、OSC(\x1b],OS 命令)、PM(\x1b^,隐私消息)、SOS(\x1bX,串起始)同辈 —— 五个被同样 ST 框定的「串形」序列家族。
2026 年为何还讲 APC。 ECMA-48 原本设计 APC 用于在远程主机的应用与终端上的同位应用之间打隧道(早期瘦客户机 / 大型机前端)。该用例基本死了,但字节形状被借用为不愿动用 OSC 用户面语义的厂商专用协议的「安全脱逃口」:
- Kitty 图形协议是头号现代用户 —— Kitty + Konsole + WezTerm + Ghostty + Wayst 通过 \x1b_G<key1>=<val1>,<key2>=<val2>,...;<base64-像素>\x1b\\ 接收图像数据。_ 后的首字节 G 是 Kitty 家族标签;后续采纳 Kitty 图形协议的模拟器都保留这一字节。(slug 名 dcs-kitty-graphics 早于此澄清而定 —— 真实字节流是 APC,不是 DCS。)
- VS Code shell 集成借 APC 带 633; 前缀作为私有通道,承载 VS Code 不愿用 OSC 133 公开广告的 shell 集成标记。
- Windows Terminal 在预览版里试验 \x1b_<JSON>\x1b\\ 作为终端 API JSON 消息载体(未稳定)。
- ConPTY 直通 —— Windows ConPTY 因安全考量过滤多数 OSC,历史上对 APC 直通更宽松,故部分 Windows 侧工具改走 APC 以原样抵达主机终端。
解析规则。 视主体为不透明字节,直至在包边界见到 ST。具体:ECMA-48 规定主体内任何「ESC \\ 之外的转义序列」终端须忽略却需透传;严格解析器把字节读入缓冲,扫描 \x1b 紧接 \\(唯一合法 7 位 ST)或单字节 \x9C。天真的「按 ESC 分割」解析器对任何合法包含 ESC 字节的主体(Kitty 图形协议尤甚 —— 其内嵌二进制统计上一定撞 0x1B)会错框。
安全态度。 因 APC 本就要原样透传,*确实*实现 APC 分派的终端必须白名单已识别协议(Kitty 图形 G 标签、VS Code 633; 等)并静默丢弃未知 APC 主体 —— 把陌生人的 APC 主体送给用户会让一个应用冒充另一个。*不*实现任何 APC 协议的终端(Alacritty、gnome-terminal、macOS Terminal、Linux console)静默吞掉即可(事实如此);视 APC 为「要么抵达知道标签的对端,要么完全消失」—— 永远不要把它当作保证可见的通道。
与 DCS / OSC / PM / SOS 的区别。
- DCS(\x1bP)—— 设备控制(数据面向*终端*本身:DECRQSS、DECDLD、DECUDK、Sixel、terminfo cap 查询)。
- OSC(\x1b])—— OS 命令(数据面向「终端即 OS 垫层」:窗口标题、调色板、剪贴板、超链接、提示标记 —— 用户面语义)。
- APC(\x1b_)—— 应用命令(数据面向*对端应用*,不面向终端 —— 终端只是搬运工)。
- PM(\x1b^)—— 隐私消息(搬运语义类似 APC,实际采用更少;部分遗留瘦客户机硬件用过)。
- SOS(\x1bX)—— 串起始(最通用引导符,无语义;2026 年几乎无人用)。
常见解析 Bug —— 实现了 OSC + DCS 却忘了 APC 的终端:见到 \x1b_G...,识别不出引导符,于是把 _G... 印到可见屏幕。Kitty 图形渲染坏了之后看见散落的 _G 串就是这个症。
覆盖度 —— APC 分派(即把主体路由到注册处理器)小众。Kitty + WezTerm + Konsole + Ghostty = 部分到完整(各把 APC G 路由到图形处理器;其它 APC 主体丢弃)。xterm = 部分(识别 APC 框架,默认丢弃主体 —— 需 apcDispatch X 资源启用)。mlterm + iTerm2 + Windows Terminal = 部分(识别 APC 框架,厂商标签主体处理,其余丢弃)。Alacritty + gnome-terminal + macOS Terminal + Linux console + cmd / ConPTY = 无作用但正确(识别 APC 框架,主体静默吞 —— 无可见残留)。现代模拟器至少都把框架做对了 —— 把主体字面打印的 bug 仅在很老的构建里残存。
规范出处: ECMA-48 §8.3.2 (APC) / xterm-ctlseqs (APC) / Kitty graphics protocol
参数
| Introducer (7-bit) | ESC + 下划线(\x1b 0x5f)。C0 安全形;UTF-8 下亦可用。 |
| Introducer (8-bit) | 单字节 0x9F。UTF-8 不安全(续字节)。除已知 Latin-1 通道外勿用。 |
| Terminator | ESC + 反斜杠(\x1b\\,7 位 ST)或单字节 0x9C(8 位 ST)。任一皆可;与引导符宽度匹配。 |
| Body | 不透明字节 —— 终端不得解释。厂商协议以首字节家族标签区分(Kitty 'G'、VS Code '633;' 等)。 |
示例
# Probe whether the terminal accepts a Kitty-graphics APC frame.\n# Empty image (1x1 transparent PNG, base64-tiny) — if rendered, terminal supports Kitty graphics.\nprintf '\\033_Gf=32,s=1,v=1,a=T;AAAAAA==\\033\\\\'\n# Emit a custom APC tag — most terminals will silently swallow.\nprintf '\\033_myapp;hello\\033\\\\'\necho 'still here' # no visible artifact on any modern terminalimport sys\n# Send a Kitty-graphics-style APC frame.\ndef apc(body):\n sys.stdout.write('\\x1b_' + body + '\\x1b\\\\')\n sys.stdout.flush()\napc('Gf=32,s=1,v=1,a=T;AAAAAA==') # Kitty 'G' tag + base64 payload\napc('myapp;state=ready') # private tag — silently swallowed on non-vendor terminals// Robust APC parser: read until ST at packet boundary, never split on raw ESC.\nfunc readAPC(r *bufio.Reader) ([]byte, error) {\n var buf bytes.Buffer\n for {\n b, err := r.ReadByte()\n if err != nil { return nil, err }\n if b == 0x9c { return buf.Bytes(), nil } // 8-bit ST\n if b == 0x1b {\n n, _ := r.ReadByte()\n if n == '\\\\' { return buf.Bytes(), nil } // 7-bit ST\n buf.WriteByte(b); buf.WriteByte(n); continue\n }\n buf.WriteByte(b)\n }\n}// Emit APC + listen for response APC from a peer.\nfunction sendAPC(body) {\n process.stdout.write('\\x1b_' + body + '\\x1b\\\\');\n}\n// Parse incoming APC (collect bytes until ST, never split on raw ESC).\nlet apcBuf = '';\nprocess.stdin.on('data', chunk => {\n const m = /\\x1b_([^\\x1b]*(?:\\x1b[^\\\\][^\\x1b]*)*)\\x1b\\\\/.exec(apcBuf + chunk.toString('binary'));\n if (m) console.error('APC body:', m[1]);\n});/* Emit APC with custom vendor tag. */\n#include <stdio.h>\nvoid send_apc(const char* tag, const char* body) {\n printf(\"\\x1b_%s;%s\\x1b\\\\\", tag, body);\n fflush(stdout);\n}\n/* Usage: send_apc(\"myapp\", \"hello\"); // silently swallowed on non-vendor terms */在哪里用到
实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。
- Kitty graphics protocol (`kitty +kitten icat`)Kitty 图像渲染器发 `\x1b_G<key>=<val>,...;<base64-像素>\x1b\\` 形式的 APC 帧 —— `_` 后的 `G` 标签是 Kitty 家族标识。Konsole、WezTerm、Ghostty、Wayst 原样接受同一协议。`kitten icat ~/image.png` 是面向用户的标准发射器
- VS Code shell integration (`OSC 633;` channel)VS Code 终端发 APC `633;` 帧(如 `\x1b_633;A\x1b\\` 标记提示符开始、`\x1b_633;B\x1b\\` 标记命令开始)来界定提示符边界而不污染 OSC 133 —— 与基于 OSC 的公共 shell 集成规范有别。可由 `$VSCODE_INJECTION` shell 钩子发出的这些包装观察到
- tmux passthrough mode (`set -g allow-passthrough on`)tmux ≥ 3.3 启用 `allow-passthrough on` 将内部 pane 的 APC 帧转发到外层终端 —— tmux 内 `kitten icat` 借此把 Kitty 图形递送到宿主终端。未启该选项时 tmux 在 pane 边界剥离 APC(默认拒绝姿态)
- WezTerm `wezterm.action.SendString` raw APC handlersWezTerm 的 Lua 配置可通过 `wezterm.on('window-config-reloaded', ...)` 配合 `set_user_var` 注册自定义 APC 处理器 —— 终端侧脚本发 `\x1b_<自定义标签>;<载荷>\x1b\\`,WezTerm 把主体路由到注册的 Lua 回调。用于编辑器感知的状态栏集成与项目上下文叠层
- mosh client-server diff protocolmosh 状态同步协议(SSP)用 APC 框架作为 mosh-client 与 mosh-server 间终端状态 diff 的线缆封装 —— SSP 帧 `\x1b_<base64-protobuf>\x1b\\` 透明穿越中间终端,利用 APC 的透传语义做隧道,不与 DCS / OSC 纠缠
常见问题
针对这条序列,开发者真正会去搜索的问题的简短回答。
- APC 序列是什么 ——
\x1b_...\x1b\\—— 哪些真实工具使用它? - APC(Application Program Command,C1 控制 0x9F,7 比特引入为
\x1b_)是应用专属二进制 payload 的通用透明信封,以 ST\x1b\\终止。与 CSI / OSC / DCS 不同,payload 字节不由终端解释 —— 它们透传给应用在里面定义的任何协议。现代真实用户:(a) Kitty graphics protocol 把 base64 编码的 PNG / RGBA 块包在\x1b_G<params>;<base64>\x1b\\里做内联图像渲染,(b) VS Code shell integration 用 OSC 633 通道(不是 APC,但架构类似 ——\x1b]633;...\x07)做 prompt-end / command-finished 标记,(c) WezTerm user vars 用 APC\x1b_<key>=<value>\x1b\\设 shell 状态供终端配置可见,(d) iTerm2 内联图像 用 OSC 1337(不是 APC)。APC 的优点是其全面透传语义 —— 行为良好的终端只是忽略未知 APC payload,不会乱码显示。 - 为什么我的 APC 序列在某些终端上被原样回显为文字?
- 两个原因。(1) 漏 ST 终止符 —— APC 在
\x1b_进入字符串解析模式,仅在\x1b\\退出;漏发 ST,解析器继续吞字节包括后续程序输出,直到最终失同步。(2) 终端根本不识别 APC,落到字面显示 —— 适用于较旧 xterm 补丁号(< 332)、macOS Terminal(始终字面)、GNU screen ≤ 4.x(除非启用bind ^_a apc否则字面)、未allow-passthrough on的 tmux(按版本完全丢弃或字面回显 APC)。防御性模式:先用 XTVERSION 探测知道在和谁说话、在字面回显集合中的终端回退到普通输出,*始终*把\x1b_与其配对的\x1b\\ST 一起发 —— 永远不要用 BEL(\x07)当替代终止符(BEL 终止 OSC,不是 APC)。
终端支持
- xterm
- 部分
- Linux console (fbcon)
- 不支持
- macOS Terminal.app
- 不支持
- iTerm2
- 部分
- Windows Terminal
- 部分
- cmd.exe / ConPTY
- 不支持
- kitty
- 支持
- alacritty
- 不支持
- WezTerm
- 支持
- Ghostty
- 支持
- GNOME Terminal
- 不支持
- Konsole
- 支持
- tmux
- 支持
- GNU screen
- 支持
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 部分 | 不支持 | 不支持 | 部分 | 部分 | 不支持 | 支持 | 不支持 | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 |