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 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 支持 | 部分 | 部分 | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |