DECSET ?1000 / ?1006 — Mouse tracking
Receive mouse click / drag / scroll events as escape sequences.
Byte forms
Every common string-literal form so you can paste-and-search either direction.
\x1b[?1000h (click only) \x1b[?1002h (cell drag) \x1b[?1003h (any motion) \x1b[?1006h (SGR encoding)\033[?1000h …\e[?1000h …ESC [ ? <N> h / l1b 5b 3f ... 68 / 6cDescription
Mouse reporting in modern terminals is layered: a *target* mode selects which events the terminal will report, and an *encoding* mode selects how the bytes are framed. Target modes are mutually exclusive: ?1000 reports button-press + release only (the original "X10 compatibility" mode), ?1002 adds drag events while any button is held, ?1003 reports every cursor motion regardless of button state. Encoding modes layer on top: ?1005 (UTF-8 — deprecated, broken under multi-byte locales), ?1015 (urxvt — an integer-CSI form, less common today), and ?1006 (SGR — the de-facto modern standard, ASCII-only, no 223-column limit, recommended by xterm-ctlseqs since xterm 268). Without an encoding mode the legacy "X10" framing applies: 3 bytes after \x1b[M, with column and row encoded as byte values offset by 32, capping addressable cells at column / row 223 — fine for an 80×24 VT but broken on any modern terminal larger than 223 cells. The portable enable is \x1b[?1000h\x1b[?1006h (click + SGR) or \x1b[?1002h\x1b[?1006h (click + drag + SGR).
The mouse-tracking-protocols pitfall warns that the ?1000 vs ?1006 distinction trips up parser implementations even today — a TUI that emits ?1000h without also emitting ?1006h on a kitty / wezterm / Ghostty terminal will receive coordinates in the legacy 223-byte-offset frame and then mis-interpret columns 224+ as control characters. Linux console / fbcon implements mouse reporting via gpm (partial in the matrix — works only if gpm is running, which is rare on modern distros). ConPTY 1809+ forwards mouse events but legacy Windows cmd.exe does not. tmux passes mouse events through with the caveat that tmux set -g mouse on *captures* mouse events at the tmux layer (for pane select / resize) before forwarding — applications inside tmux see only events tmux chooses to pass; tmux set -g mouse off restores raw forwarding. GNU screen 4.x and 5.0 BLOCK mouse events entirely (no passthrough at the multiplexer layer — the screen-strips-mouse quirk documented in lib/terminals.ts). Apple Terminal.app supports ?1000 / ?1006 but emits cell coordinates rather than pixel coordinates for click events even when ?1003 is on.
The matching close is per-mode: \x1b[?1000l / \x1b[?1002l / \x1b[?1003l for target modes, \x1b[?1006l for SGR encoding. Forgotten cleanup is the classic "shell now spams ^[[<35;42;7M on every cursor move" symptom — fix with reset or by explicitly running all four l toggles. The robust pattern is the same as for bracketed paste: pair every enable with a cleanup-block disable, or use XTSAVE / XTRESTORE (\x1b[?1000s / \x1b[?1000r) to restore parent state on exit. Mouse-tracking events are *not* generated when the terminal window is unfocused — pair with dec-focus-events (?1004) if your TUI needs to know about focus boundaries. Related: dec-bracketed-paste (the same enable / close discipline applies), alt-screen (TUIs entering alt-screen typically enable mouse in the same atomic block), dec-focus-events (focus reporting, the complementary input-event stream).
Spec citation: xterm-ctlseqs (Mouse Tracking)
Examples
printf '\033[?1000h\033[?1006h' # enable click+SGR\nprintf '\033[?1000l\033[?1006l' # disableimport sys; sys.stdout.write('\x1b[?1000h\x1b[?1006h')fmt.Print("\x1b[?1000h\x1b[?1006h")process.stdout.write('\x1b[?1000h\x1b[?1006h')printf("\x1b[?1000h\x1b[?1006h");Used in
Real-world tools that emit this sequence — anchors the bytes to commands you've already used.
- tmux`set -g mouse on` enables `?1000h` + `?1006h` (SGR-encoded) so pane resize / scrollback works with the mouse
- vim, neovim`set mouse=a` — click to position cursor, drag to visual-select, scroll wheel scrolls buffer
- less -R --mousescrollback via wheel, click-to-set-mark on long files
- htop, btop — click on column headers to sort
- fzf --mouse (preview pane scroll + select)
Frequently asked
Short answers to the questions developers actually search for this sequence.
- Why does my mouse-coord parsing break past column 224?
- You're stuck on the legacy X10 encoding (no
?1006henabled). X10 packs row and column into single bytes offset by 32 (ASCII space), which caps addressable cells at row / column 223 — past that the byte hits the high-bit non-printable range and parsers desync. Enable SGR mouse encoding alongside your target mode:\x1b[?1000h\x1b[?1006h(click + SGR) or\x1b[?1002h\x1b[?1006h(drag + SGR). SGR encoding frames coordinates as decimal CSI parameters — no per-byte limit. - Why don't mouse events arrive even after I sent
\x1b[?1000h? - Three usual suspects: (1) stdin is in cooked mode — the kernel buffers until newline before delivering bytes; switch to raw via
termios.tcsetattr(... ICANON cleared)orstty -icanon -echo. (2) A multiplexer between your app and the terminal is intercepting — tmux needsset -g mouse onto forward, screen needsmousetrack on. (3) The terminal itself disables mouse reporting in settings — iTerm2 *Preferences → Pointer → Reporting* is on by default but can be toggled off per profile.
Terminal support
- xterm
- yes
- Linux console (fbcon)
- partial
- macOS Terminal.app
- yes
- iTerm2
- yes
- Windows Terminal
- yes
- cmd.exe / ConPTY
- partial
- kitty
- yes
- alacritty
- yes
- WezTerm
- yes
- Ghostty
- yes
- GNOME Terminal
- yes
- Konsole
- yes
- tmux
- partial
- 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 | partial | yes | yes | yes | partial | yes | yes | yes | yes | yes | yes | partial | no |