^[control-codes
live in libghostty-vt

Private modes

?9, ?1000 to ?1003, ?1005 to ?1007, ?1015, ?1016 Mouse Tracking

View as Markdown

Ask the terminal to report mouse buttons and movement, and choose how the reports are encoded.

?1000 Mouse Tracking

CSI ? 1000 h/l

?9 X10 Mouse

CSI ? 9 h/l

?1001 Highlight Mouse Tracking

CSI ? 1001 h/l

?1002 Button-Event Mouse Tracking

CSI ? 1002 h/l

?1003 Any-Event Mouse Tracking

CSI ? 1003 h/l

?1005 UTF-8 Mouse Encoding

CSI ? 1005 h/l

?1006 SGR Mouse Encoding

CSI ? 1006 h/l

?1007 Alternate Scroll

CSI ? 1007 h/l

?1015 urxvt Mouse Encoding

CSI ? 1015 h/l

?1016 SGR Pixel Mouse Encoding

CSI ? 1016 h/l
xterm
Mouse Tracking

These modes ask the terminal to send the program a report when the mouse is used over it, instead of using the mouse itself for selecting text. They are xterm’s, in DEC’s private space. Two groups of them work together: one chooses which events are reported, the other how a report is encoded.

ModeReports
9a button press only (X10 compatibility)
1000presses and releases
1001presses and releases, with highlight tracking
1002presses, releases, and movement while a button is down
1003presses, releases, and all movement
ModeEncoding
noneCSI M and three bytes: the button and the coordinates, each plus 32; coordinates beyond 223 cannot be sent
1005the same, with the coordinates in UTF-8
1006SGR: CSI < b ; x ; y M for a press, m for a release, in decimal
1015urxvt: CSI b ; x ; y M, in decimal
1016SGR, with the coordinates in pixels rather than cells

Mode 1007, alternate scroll, has the mouse wheel send cursor up and down keys while the alternate screen is showing and the wheel is not being reported (AlternateScroll); focus events, 1004, have their own page.

The reports go to the program, so a case cannot see them; what it can see is what DECRQM says. The cases follow xterm, which keeps one value for each group, not a flag per mode (dpmodes, really_set_mousemode):

  • Setting one event mode replaces the one before it, and DECRQM reports only the latest as set. Resetting any of them turns tracking off.
  • Setting one encoding replaces the one before it too, but resetting an encoding takes effect only if it is the one in use: xterm’s comment says “a reset is only effective against the matching mode”.
  • Alternate scroll is off unless the alternateScroll resource turns it on (charproc.c).
  • RIS turns tracking and the encoding off; DECSTR does not (ReallyReset).

libghostty-vt tracks the events the same way, with one value, but DECRQM answers from a separate flag for each mode, so it reports a replaced mode as still set. Resetting any encoding goes back to the default encoding even if another is in use, which DECRQM does not show either: in that case it still reports the other as set. It does not know mode 1001, and it starts with alternate scroll on. The cases those touch are known differences.

Support

TerminalVersionSupportNotes
VT220manual, 2nd ed., 1984Nonot in the manual
VT510manual, 1st ed., 1993Nonot in the manual; not in Table 5-6
xtermpatch 412, xterm-411aYesalternate scroll is off unless the alternateScroll resource is set
libghostty-vt0f171f6Partlyno mode 1001; DECRQM answers each mode’s own flag; alternate scroll on by default (MOUSE-3 to MOUSE-7, MOUSE-10)
kitty0.49.1, run in a VMPartlyfails MOUSE-6 of the 5 cases that show it working
foot1.28.0, run in a VMPartlyfails MOUSE-6, MOUSE-9 of the 5 cases that show it working
xterm.js headless6.0.0, run as a libraryPartlyfails MOUSE-6, MOUSE-9 of the 5 cases that show it working
alacritty_terminal0.26.0, run as a libraryPartlyfails MOUSE-6, MOUSE-9 of the 5 cases that show it working
ConEmubuild 230724, its documentationPartlylists 9, 1000, 1002, 1003, 1005, 1006 and 1015; not 1001, 1007 or 1016; ANSI escape codes
rxvt-unicode9.31, run in a VMPartlyfails MOUSE-6, MOUSE-9, MOUSE-12 of the 5 cases that show it working
VTE0.84.1, run as a libraryPartlyfails MOUSE-9 of the 5 cases that show it working

Validation

MOUSE-1: Off to begin with

\e[?1000$p
\e[?1002$p
\e[?1003$p
\e[?1006$p
|____|
reply \e[?1000;2$y\e[?1002;2$y\e[?1003;2$y\e[?1006;2$y

MOUSE-2: Set and reset

\e[?1002h
\e[?1002$p
\e[?1002l
\e[?1002$p
|____|
reply \e[?1002;1$y\e[?1002;2$y

MOUSE-3: One event mode at a time

\e[?1000h
\e[?1002h  # replaces 1000
\e[?1000$p
\e[?1002$p
|____|
reply \e[?1000;2$y\e[?1002;1$y

MOUSE-4: X10 replaces the others too

\e[?1003h
\e[?9h
\e[?9$p
\e[?1003$p
|____|
reply \e[?9;1$y\e[?1003;2$y

MOUSE-5: Resetting any event mode turns tracking off

\e[?1002h
\e[?1000l  # not the one that was set
\e[?1002$p
|____|
reply \e[?1002;2$y

MOUSE-6: Highlight tracking

\e[?1001h
\e[?1001$p
|____|
reply \e[?1001;1$y

MOUSE-7: One encoding at a time

\e[?1006h
\e[?1015h  # replaces 1006
\e[?1006$p
\e[?1015$p
|____|
reply \e[?1006;2$y\e[?1015;1$y

MOUSE-8: Resetting another encoding leaves this one

\e[?1006h
\e[?1015l
\e[?1006$p
|____|
reply \e[?1006;1$y

MOUSE-9: UTF-8 and pixel encodings

\e[?1005h
\e[?1005$p
\e[?1016h
\e[?1016$p
|____|
reply \e[?1005;1$y\e[?1016;1$y

MOUSE-10: Alternate scroll is off by default

\e[?1007$p
|____|
reply \e[?1007;2$y

MOUSE-11: DECSTR leaves tracking on

\e[?1002h
\e[?1006h
\e[!p
\e[?1002$p
\e[?1006$p
|____|
reply \e[?1002;1$y\e[?1006;1$y

MOUSE-12: RIS turns it off

\e[?1002h
\e[?1006h
\ec
\e[?1002$p
\e[?1006$p
|____|
reply \e[?1002;2$y\e[?1006;2$y
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.