DECSET ?1004 — Focus in/out events
Make the terminal report when its window gains or loses keyboard focus.
Byte forms
Every common string-literal form so you can paste-and-search either direction.
\x1b[?1004h (enable) \x1b[?1004l (disable)\033[?1004h / \033[?1004l\e[?1004h / \e[?1004lESC [ ? 1 0 0 4 h / l1b 5b 3f 31 30 30 34 68 / 6cDescription
xterm-ctlseqs Private Mode 1004 — DECSET enable, DECRST disable. When enabled, the terminal pushes \x1b[I (3 bytes: ESC [ I) onto stdin every time the window or pane gains keyboard focus, and \x1b[O (ESC [ O) every time it loses focus. Enable with \x1b[?1004h, disable with \x1b[?1004l. The two report sequences are short enough that parsers must classify them under CSI handling (final byte I = 0x49 and O = 0x4f, both in the CSI final-byte range), not under mouse-protocol or function-key parsing. The state is *terminal-emitter* state — once set, the terminal keeps reporting until DECRST or until the window closes.
Portability — modern emulators: xterm, iTerm2, kitty, alacritty, wezterm, gnome-terminal, Konsole, Ghostty, Windows Terminal, ConPTY 1809+. Linux console does not emit focus events (the framebuffer has no notion of "window focus"); Apple Terminal.app historically did not implement ?1004 either. tmux requires set -g focus-events on to forward focus events from the outer terminal into the inner pane — without that line in ~/.tmux.conf, tmux silently swallows the reports and apps inside the pane never see them. GNU screen has limited / inconsistent support across releases. Consumer apps: vim's FocusGained / FocusLost autocommands, neovim's matching events, kitty's own keep-state-on-blur optimisation, plus several TUIs (helix, lazygit, btop) that use focus to pause animations or re-query data.
The "leak after crash" pitfall is the most common operational issue: a TUI that didn't get to emit \x1b[?1004l before crashing leaves the terminal still reporting events, so every subsequent alt-tab pumps a literal ^[[I or ^[[O into whatever's reading stdin — usually the shell prompt, where the characters appear as garbage that breaks readline state. Recovery from a clean shell: printf '\x1b[?1004l', or reset (which calls RIS — the bigger hammer that also clears scrollback). The durable cleanup pattern is to install a signal handler / atexit / defer that emits ?1004l regardless of how the process exits — this is the same shape as the stuck-bracketed-paste and stuck-cursor-hidden pitfalls and the same fix applies. Related: dec-mouse-tracking (sibling DECSET ?1000 / ?1006 with the same enable / disable / leak-on-crash shape), dec-bracketed-paste (DECSET ?2004, similar cleanup pattern), alt-screen (DECSET ?1049, frequently toggled alongside focus events at TUI start / exit).
Spec citation: xterm-ctlseqs (Private mode 1004)
Examples
printf '\033[?1004h' # enable focus events\n# (when window focus changes, terminal emits \x1b[I or \x1b[O)import sys; sys.stdout.write('\x1b[?1004h')fmt.Print("\x1b[?1004h")process.stdout.write('\x1b[?1004h')printf("\x1b[?1004h");Used in
Real-world tools that emit this sequence — anchors the bytes to commands you've already used.
- vim, neovim FocusGained / FocusLost autocmdseditors enable `\x1b[?1004h` on startup so the terminal sends `\x1b[I` / `\x1b[O` on window focus change — used to fire `:checktime` (reload externally-changed files) and pause swap-file flushing when unfocused
- tmux `focus-events on`tmux must opt-in via `set -g focus-events on` to forward FocusIn / FocusOut from the outer terminal into the active pane — without this, vim's `FocusGained` autocmd never fires inside tmux even on a focus-supporting terminal
- Helix editorHelix enables focus reporting at startup to drive auto-save (`editor.auto-save.focus-lost = true`) and to pause LSP diagnostics polling when the window is not focused, reducing background CPU
- alacritty, kitty, WezTerm, iTerm2all four terminals natively emit `\x1b[I` / `\x1b[O` when DECSET 1004 is on — gnome-terminal / konsole / Windows Terminal also support it; macOS Terminal.app and the Linux console (fbcon) do not, which is why tmux / vim probe for it
- VS Code, Zellij window-focus auto-writeGUI editors / multiplexers wire focus events to user-visible behaviour: VS Code re-runs file watchers on FocusGained; Zellij dims inactive panes via the focus signal — both lean on DECSET 1004 in raw mode
Frequently asked
Short answers to the questions developers actually search for this sequence.
- How do I tell my editor to redraw when the user alt-tabs back into the terminal?
- Enable focus reporting with
\x1b[?1004h. The terminal will then write\x1b[Ion focus-in and\x1b[Oon focus-out to your stdin. Parse those as no-op key events that trigger a redraw / state-refresh hook. Disable on exit with\x1b[?1004lso the next program doesn't see the bracket bytes as literal[I. Neovim, Helix, tmux, Bubbletea, and textual all do exactly this; raw TUIs often skip it and end up with stale screens after a focus switch. - Which terminals actually emit
\x1b[I/\x1b[Ofocus events? - xterm (the originator), iTerm2, kitty, ghostty, wezterm, alacritty, recent Windows Terminal, gnome-terminal, konsole, urxvt — basically every modern emulator. tmux forwards them when
set -g focus-events on. Linux fbcon and the old DEC VT510 hardware do NOT emit them; on those, your focus-aware redraw never fires and the editor just keeps stale state. Treat focus reporting as a progressive enhancement: never gate correctness on receiving the event.
Terminal support
- xterm
- yes
- Linux console (fbcon)
- no
- macOS Terminal.app
- no
- iTerm2
- yes
- Windows Terminal
- yes
- cmd.exe / ConPTY
- no
- kitty
- yes
- alacritty
- yes
- WezTerm
- yes
- Ghostty
- yes
- GNOME Terminal
- yes
- Konsole
- yes
- tmux
- yes
- GNU screen
- no
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | no | no | yes | yes | no | yes | yes | yes | yes | yes | yes | yes | no |
Related sequences
In the family cookbook
DEC cookbook · 6. Focus events & sync update — `?1004`, `?2026`