跳到主要内容
ansicode

DECRPM 解码 —— 解析 DECRQM 回复(`CSI ? Ps ; Pm $ y`)

解析 DECRQM 回复 CSI ? Ps ; Pm $ y(以及 CSI Ps ; Pm $ y)—— Pm 数值说明该模式当前是 设置 / 重置 / 永久开 / 永久关 / 不识别 中的哪一种。

字节形式

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

\\x1b[\x1b[?<Ps>;<Pm>$y (private) \x1b[<Ps>;<Pm>$y (ANSI)
\\033[\033[?2004;1$y
\\e[\e[?2004;1$y
ESC [ESC [ ? Ps ; Pm $ y
hex1b 5b 3f ... 24 79

说明

DECRPM —— Report Private Mode(私有式)或 Report Mode(ANSI 式)—— 是终端对 DECRQM 查询(私有模式 CSI ? Ps $ p、ANSI 模式 CSI Ps $ p,见 decrqm)的回复。回复回显被查询的 Ps,再加一个 Pm 值,其小型枚举精确表达终端对该模式的认识。

Pm 取值: - 0 —— *不识别该模式*。视为不支持。别再探测同一 Ps;按会话缓存为「不支持」。 - 1 —— *当前为「设置」*。等效于最近一次执行了 \x1b[?<Ps>h(ANSI 式则 \x1b[<Ps>h)。 - 2 —— *当前为「重置」*。等效于最近一次执行了 \x1b[?<Ps>l(ANSI 式则 \x1b[<Ps>l)。 - 3 —— *永久设置*。终端硬编码为开;发 \x1b[?<Ps>l 是空操作。 - 4 —— *永久重置*。终端硬编码为关;发 \x1b[?<Ps>h 是空操作。

回复中的 ? 与请求中的 ? 镜像 —— 查询私有模式(如 ?2004 括号粘贴、?1000 鼠标)得 \x1b[?2004;Pm$y;查询 ANSI 模式(如 4 IRM、20 LNM)得 \x1b[4;Pm$y,*无* ?。消费者必须按请求形态原样匹配 —— 交错探测私有 / ANSI 时回复会被错路由。

推荐消费者模式(Python 风伪码): ``` def probe_mode(ps, private=True, timeout=0.1): prefix = '?' if private else '' sys.stdout.write(f'\x1b[{prefix}{ps}$p') sys.stdout.flush() reply = read_until_finalbyte('y', timeout) # CSI ... $ y m = re.match(rf'\x1b\[{re.escape(prefix)}(\d+);(\d+)\$y', reply or '') if not m or int(m.group(1)) != ps: return 'no-reply' return {0:'unknown', 1:'set', 2:'reset', 3:'permaset', 4:'permareset'}[int(m.group(2))] ```

「永久设置 / 永久重置」的真实含义:终端用 Pm=3 / Pm=4 表达「该特性开 / 关且无法用转义码切换」。例如: - 一直常开括号粘贴的终端(部分用户通过 xterm X 资源配置)对 ?2004Pm=3。 - 旧 Linux console 对 ?1000(鼠标追踪)回 Pm=4,因内核 vt 驱动从未实现。 - ConPTY 对多数 DEC 私有模式(如 ?9 X10 鼠标、?1015 urxvt 鼠标)回 Pm=4,因 conhost 翻译层把它们写死为关。

读取目的下把 Pm=3 / Pm=4 等同 Pm=1 / Pm=2 —— 模式确在该状态 —— 但别再发会被静默忽略的 h/l。

厂商兼容性: - xtermkittyiTerm2WezTermGhosttyKonsole:完全合规 —— 五种 Pm 都能正确返回。 - Alacritty:仅实现 Pm ∈ {0, 1, 2} —— 永不返回 3 / 4。依赖 permaset/permareset 分支的工具需把「缺失」视作「未知但可能支持」。 - Linux consolevt):只对常见模式(?1?7?25)回;其余完全静默(无回复 —— 务必加超时)。 - Windows Terminal:1.20 起完全合规;早期构建会对部分实际支持的模式回 Pm=0。 - cmd.exe / ConPTY:受限 —— 多数探测即使被支持也回 Pm=0。 - macOS Terminal:部分实现;部分模式即便能 h/l 切换也回 Pm=0 而不是 Pm=1 / Pm=2。在 macOS Terminal 上别信 DECRQM 做特性检测 —— 改用行为探测。

DECRQM + DECRPM vs DECRQSS(见 dcs-decrqss):DECRQM 是*通用模式状态*查询 —— 开 / 关 / 永久开 / 永久关 / 未知。DECRQSS 返回可设置资源的真实*值*(SGR 栈、DECSCUSR 形状、DECSTBM 行列、DECSCL 一致性级别)。「括号粘贴开了吗」用 DECRQM;「当前光标是什么形状」用 DECRQSS。TUI 启动探测通常两者都发:DECRQM 发现能力,DECRQSS 快照资源以便退出时恢复。

规范出处: xterm-ctlseqs (DECRPM, CSI ? Ps ; Pm $ y / CSI Ps ; Pm $ y)

参数

Ps回显发起的 DECRQM 查询的模式号。若同时发出多个探测,按回复 Ps 与请求匹配以消歧。
Pm0 不识别、1 设置、2 重置、3 永久设置(硬编码开)、4 永久重置(硬编码关)。

示例

bash
# Probe bracketed-paste; expect ?2004;1$y if on, ?2004;2$y if off.\nprintf '\\033[?2004$p'\nIFS= read -rs -d y -t 0.1 reply </dev/tty 2>/dev/null\necho \"DECRPM: ${reply#$'\\033'}y\"
python
import sys, re, termios, tty, select\n\ndef probe(ps, private=True, timeout=0.1):\n    prefix = '?' if private else ''\n    fd = sys.stdin.fileno(); old = termios.tcgetattr(fd)\n    try:\n        tty.setcbreak(fd)\n        sys.stdout.write(f'\\x1b[{prefix}{ps}$p'); sys.stdout.flush()\n        buf = ''\n        while True:\n            r,_,_ = select.select([fd], [], [], timeout)\n            if not r: break\n            buf += sys.stdin.read(1)\n            if buf.endswith('y'): break\n    finally:\n        termios.tcsetattr(fd, termios.TCSADRAIN, old)\n    m = re.match(rf'\\x1b\\[{re.escape(prefix)}(\\d+);(\\d+)\\$y', buf)\n    if not m or int(m.group(1)) != ps: return 'no-reply'\n    return ['unknown','set','reset','permaset','permareset'][int(m.group(2))]\n\nprint(probe(2004))
go
// After DECRQM probe, switch on the Pm enum.\nswitch pm {\ncase 0: cap[ps] = capUnknown      // not recognised\ncase 1: cap[ps] = capOn           // currently set\ncase 2: cap[ps] = capOff          // currently reset\ncase 3: cap[ps] = capHardwiredOn  // can't toggle off — skip restore\ncase 4: cap[ps] = capHardwiredOff // can't toggle on  — skip enable\n}
javascript
// Node parser — match DECRPM reply, return {ps, state}.\nfunction parseDECRPM(buf) {\n  const m = /\\x1b\\[(\\??)(\\d+);(\\d+)\\$y/.exec(buf);\n  if (!m) return null;\n  return {\n    private: m[1] === '?',\n    ps: Number(m[2]),\n    state: ['unknown','set','reset','permaset','permareset'][Number(m[3])] ?? 'invalid',\n  };\n}
c
/* Skeleton — emit DECRQM for ?1006 (SGR mouse), parse DECRPM Pm. */\nfprintf(stdout, \"\\x1b[?1006$p\"); fflush(stdout);\n/* read until 'y'; then sscanf buf, \"\\x1b[?%d;%d$y\", &ps, &pm) */\nint ps = 0, pm = 0;\n/* ...read loop... */\nif (ps == 1006) {\n    static const char *st[] = {\"unknown\",\"set\",\"reset\",\"permaset\",\"permareset\"};\n    printf(\"SGR mouse: %s\\n\", (pm>=0 && pm<=4) ? st[pm] : \"invalid\");\n}

终端支持

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

相关序列