跳到主要内容
ansicode

DECSET ?1004 — 焦点进出事件

让终端在窗口获得或失去键盘焦点时上报事件。

字节形式

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

\\x1b[\x1b[?1004h (enable) \x1b[?1004l (disable)
\\033[\033[?1004h / \033[?1004l
\\e[\e[?1004h / \e[?1004l
ESC [ESC [ ? 1 0 0 4 h / l
hex1b 5b 3f 31 30 30 34 68 / 6c

说明

xterm-ctlseqs 私有模式 1004 —— DECSET 启用、DECRST 关闭。启用后,窗口或 pane 每次获得键盘焦点时终端向 stdin 推送 \x1b[I(3 字节:ESC [ I);失去焦点时推送 \x1b[O(ESC [ O)。启用为 \x1b[?1004h、关闭为 \x1b[?1004l。两个上报序列足够短,解析器必须按 CSI 处理(末字节 I = 0x49、O = 0x4f,皆在 CSI 末字节范围内),而非按鼠标协议或功能键解析。该状态属于 *终端发射端* 状态 —— 一旦开启,终端会持续上报,直到 DECRST 或窗口关闭。

可移植性 —— 现代模拟器:xterm、iTerm2、kitty、alacritty、wezterm、gnome-terminal、Konsole、Ghostty、Windows Terminal、ConPTY 1809+。Linux console 不发出焦点事件(framebuffer 没有「窗口焦点」概念);Apple Terminal.app 历史上也未实现 ?1004。tmux 需要在 ~/.tmux.conf 中加 set -g focus-events on 才会把外层终端的焦点事件转发进 pane —— 否则 tmux 静默吞掉,pane 内程序永远看不到。GNU screen 各版本支持参差不齐。消费端:vim 的 FocusGained / FocusLost 自动命令、neovim 的同名事件、kitty 自身的失焦保留状态优化,以及若干 TUI(helix、lazygit、btop)用焦点暂停动画或重查数据。

「崩溃后泄漏」是最常见运维问题:未及时发出 \x1b[?1004l 就崩溃的 TUI 让终端继续上报事件,此后每次 alt-tab 都把字面 ^[[I^[[O 灌入 stdin —— 通常是 shell 提示符,于是出现破坏 readline 状态的乱码。从干净 shell 恢复:printf '\x1b[?1004l',或 reset(调用 RIS —— 更重的锤子,会同时清掉回滚)。可持久的清理模式是注册信号处理 / atexit / defer,让进程无论如何退出都发 ?1004l —— 这与 stuck-bracketed-pastestuck-cursor-hidden 同形,修法相同。相关:dec-mouse-tracking(兄弟 DECSET ?1000 / ?1006,启用/关闭/崩溃泄漏形态一致)、dec-bracketed-paste(DECSET ?2004,清理模式相似)、alt-screen(DECSET ?1049,TUI 启动/退出时常与焦点事件一并切换)。

规范出处: xterm-ctlseqs (Private mode 1004)

示例

bash
printf '\033[?1004h'   # enable focus events\n# (when window focus changes, terminal emits \x1b[I or \x1b[O)
python
import sys; sys.stdout.write('\x1b[?1004h')
go
fmt.Print("\x1b[?1004h")
javascript
process.stdout.write('\x1b[?1004h')
c
printf("\x1b[?1004h");

在哪里用到

实际会发出该序列的工具——把抽象字节锚定到你已经用过的命令上。

  • vim, neovim FocusGained / FocusLost autocmds编辑器启动时开启 `\x1b[?1004h` —— 让终端在窗口焦点变化时发 `\x1b[I` / `\x1b[O`,用于触发 `:checktime`(重载外部修改文件)以及失焦时暂停 swap 文件刷写
  • tmux `focus-events on`tmux 必须通过 `set -g focus-events on` 才会把外层终端的 FocusIn / FocusOut 透传到活动窗格 —— 不开它,即便外层终端支持焦点事件,tmux 内的 vim `FocusGained` 也不会触发
  • Helix editorHelix 启动时开启焦点报告 —— 驱动自动保存(`editor.auto-save.focus-lost = true`)并在窗口失焦时暂停 LSP 诊断轮询,降低后台 CPU
  • alacritty, kitty, WezTerm, iTerm2四款终端在 DECSET 1004 开启时原生发 `\x1b[I` / `\x1b[O` —— gnome-terminal / konsole / Windows Terminal 同样支持;macOS Terminal.app 和 Linux 终端(fbcon)不支持,所以 tmux / vim 会做探测
  • VS Code, Zellij window-focus auto-writeGUI 编辑器 / 复用器把焦点事件接到用户可见行为:VS Code 在 FocusGained 时重跑文件 watcher;Zellij 通过焦点信号变暗非活动窗格 —— 都在 raw 模式下依赖 DECSET 1004

常见问题

针对这条序列,开发者真正会去搜索的问题的简短回答。

怎样让编辑器在用户 alt-tab 回到终端时重绘?
\x1b[?1004h 启用焦点上报。之后终端会在获得焦点时向 stdin 写 \x1b[I、失焦时写 \x1b[O。把它们当作触发重绘 / 状态刷新的空操作按键事件。退出时用 \x1b[?1004l 关闭,否则下一个程序会看到字面 [I 字节。Neovim、Helix、tmux、Bubbletea、textual 都正是这么做的;原始 TUI 常常省略,结果焦点切换后屏幕残留过期内容。
哪些终端真的会发 \x1b[I / \x1b[O 焦点事件?
xterm(始作俑者)、iTerm2、kitty、ghostty、wezterm、alacritty、较新的 Windows Terminal、gnome-terminal、konsole、urxvt —— 几乎所有现代模拟器都发。tmux 在 set -g focus-events on 时转发。Linux fbcon 与老 DEC VT510 硬件发;那种环境下焦点感知重绘永远不触发,编辑器只能继续摆烂。把焦点上报当作渐进增强 —— 千万别把正确性押在事件一定来上。

终端支持

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

相关序列

在家族食谱中

DEC 食谱 · 6. 焦点事件与同步更新 —— `?1004`、`?2026`