跳到主要内容
ansicode

APC —— 应用程序命令(`ESC _ … ESC \\` / `\x9F … \x9C`)

ECMA-48 为应用对应用消息保留的字符串引导符。现代用途:Kitty 图形协议、VS Code shell 集成、Windows Terminal API 补丁。与 DCS / OSC / PM / SOS 同辈。

字节形式

涵盖所有常见的字符串字面量写法,方便正反查找。

\\x1b[\x1b_<body>\x1b\\ (7-bit) \x9f<body>\x9c (8-bit C1)
\\033[\033_<body>\033\\ (7-bit) \237<body>\234 (8-bit C1)
\\e[\e_<body>\e\\
ESC [ESC _ <body> ESC \\ or 0x9F <body> 0x9C
hex1b 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 通道外勿用。
TerminatorESC + 反斜杠(\x1b\\,7 位 ST)或单字节 0x9C(8 位 ST)。任一皆可;与引导符宽度匹配。
Body不透明字节 —— 终端不得解释。厂商协议以首字节家族标签区分(Kitty 'G'、VS Code '633;' 等)。

示例

bash
# 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 terminal
python
import 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
go
// 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}
javascript
// 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});
c
/* 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 */

终端支持

xterm
部分
Linux console (fbcon)
不支持
macOS Terminal.app
不支持
iTerm2
部分
Windows Terminal
部分
cmd.exe / ConPTY
不支持
kitty
支持
alacritty
不支持
WezTerm
支持
Ghostty
支持
GNOME Terminal
不支持
Konsole
支持
tmux
不支持
GNU screen
不支持

相关序列

在家族食谱中

ESC 食谱 · 6. APC —— 现代应用对应用信封 `\x1b_ …… \x1b\\`