XTSAVE / XTRESTORE — 保存 / 恢复 DEC 私有模式(`CSI ? Pm s` / `CSI ? Pm r`)
把一个或多个 DEC 私有模式状态压栈,稍后恢复 —— tmux / screen / fzf 用以安全地切换鼠标 / 备用屏 / 粘贴模式而不破坏用户设置的基础机制。
字节形式
涵盖所有常见的字符串字面量写法,方便正反查找。
\x1b[?<Pm>s (save) \x1b[?<Pm>r (restore)\033[?1000s / \033[?1000r\e[?1000s / \e[?1000rESC [ ? Pm s / ESC [ ? Pm r1b 5b 3f ... 73 / 1b 5b 3f ... 72说明
前缀 `?` 把 `s` 和 `r` 置入 **DEC 私有模式**命名空间(无前缀时 `CSI Ps ; Ps s` 为 DECSLRM —— 见 `decslrm` —— 而 `CSI Ps ; Ps r` 为 DECSTBM —— 见 `decstbm`)。带前缀后,`Pm` 为一个或多个分号分隔的 DEC 私有模式号(即 `CSI ? Pm h/l` 用来开关它们的同一组数字)。 - **XTSAVE**(`\x1b[?<Pm>s`)—— 把所列各模式的*当前*状态压入终端内部的逐模式栈。栈深通常逐模式 1 层深(第二次 XTSAVE 覆盖第一次);xterm 等少数实现维持任意深度的真栈。 - **XTRESTORE**(`\x1b[?<Pm>r`)—— 弹出并应用每个所列模式的已保存状态。若某模式从未保存过,对它而言 XTRESTORE 为空操作。 - 一次多模式:`\x1b[?1000;1002;1006;1049s` 一次保存鼠标 + 备用屏的四个模式;对称的 `\x1b[?1000;1002;1006;1049r` 一次恢复。 **为何存在** —— tmux、GNU screen、fzf、atuin、neovim 的 `:terminal`、lazygit 等交互工具需要临时改变鼠标追踪、备用屏、括号粘贴、焦点事件等,又不能在崩溃或用户切后台时给用户留下被破坏的设置。XTSAVE / XTRESTORE 是*非破坏性*的方式 —— 捕获用户偏好、切换到工具偏好、退出时无论用户原本怎样都能回滚。 **常用保存 / 恢复集合**(「tmux 鼠标集」):`?1000`(X10 鼠标)、`?1002`(cell motion 鼠标)、`?1003`(all-motion 鼠标)、`?1006`(SGR 鼠标编码)、`?1015`(urxvt 鼠标编码)、`?1049`(带光标保存的备用屏)。括号粘贴加 `?2004`;焦点事件 `?1004`。 **与 `?1049` 的关系** —— `\x1b[?1049h/l` 本身是*复合*序列,等效组合了 `?1047`(备用屏)+ `?1048`(光标保存)+ 进入时隐式保存 / 退出时恢复备用屏内容。除此之外,显式 XTSAVE / XTRESTORE 才是常规手段。不要再用 XTSAVE / XTRESTORE 包裹 `?1049` —— 它已自带往返。 **注意** —— xterm / iTerm2 / Kitty / WezTerm / Ghostty / Alacritty / Konsole / Windows Terminal 均实现;Linux console 仅部分实现常见模式;cmd.exe 不实现。栈深各异 —— 按 1 估,不要嵌套保存。macOS Terminal 实现最不齐,若以它为目标需专门测试。
规范出处: xterm-ctlseqs (XTSAVE / XTRESTORE — CSI ? Pm s / r)
参数
| Pm | 一个或多个分号分隔的 DEC 私有模式号(如 `1000;1002;1006;1049`)。与 `CSI ? Pm h`(开启)/ `CSI ? Pm l`(关闭)使用的数字相同。 |
示例
# Wrap an interactive subshell so mouse + alt-screen prefs survive it.\nprintf '\033[?1000;1002;1006;1049s' # save\nprintf '\033[?1000l\033[?1049h' # enter alt-screen, mouse off\nbash --noprofile --norc\nprintf '\033[?1049l' # leave alt-screen\nprintf '\033[?1000;1002;1006;1049r' # restore user's modesimport sys\nMODES = '1000;1002;1006;1049'\nsys.stdout.write(f'\x1b[?{MODES}s') # save\ntry:\n sys.stdout.write('\x1b[?1000h') # enable mouse for our app\n run_app()\nfinally:\n sys.stdout.write(f'\x1b[?{MODES}r') # restore// Defer-based save/restore wrapper for a TUI's mouse + altscreen setup.\nmodes := \"1000;1002;1006;1049\"\nfmt.Printf(\"\\x1b[?%ss\", modes)\ndefer fmt.Printf(\"\\x1b[?%sr\", modes)\nfmt.Print(\"\\x1b[?1049h\\x1b[?1000;1002;1006h\") // enter alt-screen + cell-motion + SGR\nrunTUI()// Node TUI — save on SIGINT/exit so user's mouse prefs always come back.\nconst modes = '1000;1002;1006;1049';\nprocess.stdout.write(`\\x1b[?${modes}s`);\nprocess.on('exit', () => process.stdout.write(`\\x1b[?${modes}r`));\nprocess.on('SIGINT', () => process.exit(130));/* Save -> enter alt-screen + mouse -> restore on signal. */\nstatic const char *MODES = \"1000;1002;1006;1049\";\nprintf(\"\\x1b[?%ss\", MODES);\nsignal(SIGINT, on_exit_restore);\nprintf(\"\\x1b[?1049h\\x1b[?1000;1002;1006h\");\nrun_tui();\nprintf(\"\\x1b[?%sr\", MODES);终端支持
- 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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 支持 | 部分 | 部分 | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 不支持 | 不支持 |