DECARM — 按键自动重复模式(CSI ? 8 h / l)
切换终端在按住按键时是否重复发送字节 —— 对游戏与编辑器很重要,按住 j 不应狂刷缓冲。
字节形式
涵盖所有常见的字符串字面量写法,方便正反查找。
\\x1b[
\x1b[?8h (repeat) \x1b[?8l (no repeat)\\033[
\033[?8h / \033[?8l\\e[
\e[?8h / \e[?8lESC [
ESC [ ? 8 h / ESC [ ? 8 lhex
1b 5b 3f 38 68 / 6c说明
Auto-Repeat 模式。设置(\x1b[?8h,几乎所有终端的默认)时,按键超过 OS 定义的延迟(典型 250 ms)后保持按下会让终端按 OS 定义的速率(典型每秒 30 次)注入重复字节 —— 即标准打字机风格行为。重置(\x1b[?8l)时,无论按键保持多久,每次物理按键都只发一个字节 —— 这样把 j 解作「下移一格」的 roguelike 在玩家按住按键时不会跨地图狂奔。OS 延迟 / 速率不能通过 DECARM 调整(X11 用 xset r rate、macOS 用 defaults write -g KeyRepeat、Windows 用辅助功能面板);DECARM 只切开 / 关。对 TUI 游戏(基于 curses 的 roguelike 如 nethack、brogue)、解释单键命令的模式化编辑器、以及希望每次击键都防抖为离散事件的应用至关重要。支持几乎一致 —— 仅 Linux console 偏 partial(取决于 framebuffer 驱动)。退出时务必恢复 ?8h —— 关着 auto-repeat 退出会污染用户 shell 会话。
规范出处: DEC VT100 (DECARM) / xterm-ctlseqs
参数
示例
printf '\033[?8l' # turn OFF auto-repeat for the duration of this TUI\ntrap 'printf "\\033[?8h"' EXIT # restore on any exit\n# … game loop …import sys, atexit\nsys.stdout.write('\x1b[?8l') # roguelike: one move per keypress\natexit.register(lambda: sys.stdout.write('\x1b[?8h'))fmt.Print("\x1b[?8l")\ndefer fmt.Print("\x1b[?8h") // always restoreprocess.stdout.write('\x1b[?8l')\nprocess.on('exit', () => process.stdout.write('\x1b[?8h'))printf("\x1b[?8l");\natexit_set_restore("\x1b[?8h"); /* paired restore is non-optional */在哪里用到
实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。
- vim long-press repeat suppressionVim 进入插入模式时若配置 `:set ttimeoutlen=0` 会发 `\x1b[?8l`,防止终端侧按键重复超过编辑器按键级别的常规模式命令 —— `:verbose set ttimeoutlen?` 检查,用 `script` 跟踪可见发送
- less paging with held arrow key在 OS *不*生成按键重复的终端上(pre-3.2 tmux 配置中的某些复用器),`less` 依赖 DECARM `\x1b[?8h`(默认)让用户按住 `j` 或 `↓` 时按行滚动重复 —— `tmux: set -g repeat-time 0` 关闭后观察按一次后停止滚动
- terminfo `decarm` capability作为 `km` 布尔能力(「有 meta 键」—— 对 DEC 系终端隐含 DECARM)列出;`infocmp xterm | grep -E 'km|decarm'` 显示该位。应用代码通过 `tigetflag("km")` 读取来决定是否安装按键去抖
- GNU readline `quoted-insert` (Ctrl-V / Ctrl-Q)按住 `Ctrl-V` 插入字面控制字节,在 OS 层不为修饰键自动重复的终端(某些带 modifier-debounce 的 Wayland 合成器)依赖 DECARM `?8h` 重复 —— `bind -P | grep quoted-insert` 显示绑定,用 `READLINE_DEBUG=1` 跟踪
- htop refresh-rate vs key-repeat interactionhtop 的「Setup → Display options → Update interval」在「按住箭头滚动进程列表」场景与 DECARM 交互:`?8h`(默认)配 1.5 s 刷新时,按住 `↓` 滚动长列表平滑,因为终端按键重复超过重绘;`?8l` 则每次按键后暂停直到重绘追上 —— 在会话间切换 `setterm -repeat off`/`on` 可见
常见问题
针对这条序列,开发者真正会去搜索的问题的简短回答。
- DECARM
\x1b[?8h/\x1b[?8l到底控制什么? - DECARM(Auto-Repeat Mode)控制*终端*是否在按键按住时生成重复按键 —— 不同于窗口系统应用的任何 OS 级按键重复。在硬件 DEC VT220 / VT320 上这是按住键得重复的唯一方法;在现代模拟器上基本是 vestigial,因为 OS 已在输入法层(xkb、IBus、macOS 上的 IMK、Windows 内核)处理按键重复。今天
?8h是支持矩阵中每个终端的默认值;?8l(关闭)xterm、kitty、foot、gnome-terminal、konsole 支持 —— Windows Terminal、ConPTY、alacritty(完全委托给 OS)上是空操作。用例已死 —— 现代 TUI 作者通过 XKB 或 AppKit 按键规则全局禁用 OS 按键重复,而不依赖 DECARM。 - 我的编辑器模态键映射应不应该发
\x1b[?8l关闭重复? - 不要 ——
\x1b[?8l触不到 OS 按键重复层,所以任何把重复委托给 OS 的终端上(Windows Terminal、ConPTY、alacritty、Wayland 下设enable_key_repeat = yes的 kitty),按住键仍会向你的 stdin 涌入重复字节。到达的字节无论 DECARM 开关都一样,因为重复是在终端*之外*生成的。正确模式在应用侧:给收到的字节打时间戳并在去抖窗口内忽略重复,或用 kitty keyboard protocol\x1b[>1u(它*会*把 key-down vs key-up vs key-repeat 作为带渐进增强标志的独立事件上报)。对 Vim / Helix / Kakoune 风格模态编辑,kitty 协议是受支持的答案。
终端支持
- 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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 支持 | 部分 | 支持 | 支持 | 支持 | 部分 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |