Skip to main content
ansicode

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[\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

Description

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

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");

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[I on focus-in and \x1b[O on focus-out to your stdin. Parse those as no-op key events that trigger a redraw / state-refresh hook. Disable on exit with \x1b[?1004l so 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[O focus 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

Related sequences

In the family cookbook

DEC cookbook · 6. Focus events & sync update — `?1004`, `?2026`