DEC private modes — alt screen, cursor, mouse, focus
DEC private mode set / reset. The `ESC [ ? n h` and `ESC [ ? n l` family — alternate screen buffer, cursor visibility, bracketed paste, focus events, mouse tracking, application keypad, line wrap. Originally DEC VT-series extensions, adopted universally by xterm-family terminals.
17 sequences
Mode cookbook — building a TUI from six DEC switches
DEC private modes are how every full-screen TUI works — vim, less, tmux, htop, fzf all live inside this dialect. Six stops, top to bottom: the ?-prefix syntax that separates private modes from ECMA-48 standard ones; the alt-screen bedrock; cursor visibility while you repaint; bracketed paste so you can tell typing from a Cmd-V; mouse tracking with the SGR extended encoding; and focus events + synchronized update for flicker-free live UIs.
1. The
?prefix — DECSET vs SMEvery code in this family carries a leading
?inside the parameter —\x1b[?25hshows the cursor,\x1b[?25lhides it. The?is what makes the sequence a **DEC private mode** (DECSET / DECRST) rather than an ECMA-48 standard mode (SM / RM, no?). Using the same number without?either does nothing or hits a completely unrelated mode —\x1b[4henables insert mode (IRM), nothing like?4which would be DECSCLM smooth-scroll. Final byte ishfor **set / on** andlfor **reset / off**. Multiple modes can be combined in one sequence with;separators:\x1b[?25;1049hhides the cursor + enters the alt-screen in one go.2. Alt-screen —
\x1b[?1049h/lSwitching to the alt-screen is the bedrock TUI move — vim, less, tmux, htop all emit
\x1b[?1049hon launch and\x1b[?1049lon exit. The 1049 variant does three things at once: saves the cursor position, switches to a separate screen buffer, and clears that buffer. Exit (l) restores the cursor and switches back, so the user's shell scrollback is exactly as they left it — nothing the TUI drew leaks into the scrollback log. The older?1047only switches buffers (no cursor save) and the original?47doesn't even clear the alt buffer — pre-2000 emulators only had?47; modern TUIs should always emit?1049. Forgetting the close half (l) on a crash leaves the user staring at half a vim screen with no escape — install SIGINT / SIGTERM handlers that emit it before exit.3. Cursor visibility & blink —
?25,?12Hide the cursor with
\x1b[?25lbefore repainting a screen, show it again with\x1b[?25hat the end — otherwise the cursor jumps across the frame as you write and looks like a glitch. The blink attribute is a separate switch:\x1b[?12hturns blink on,\x1b[?12lturns it off, **independent** of?25. So a hidden-and-non-blinking cursor is\x1b[?25l\x1b[?12l; a visible-but-steady cursor for a code editor is\x1b[?25h\x1b[?12l. DECTCEM (?25) is universally supported across every modern emulator; the?12blink toggle is honoured by xterm, kitty, alacritty, wezterm, iTerm2, and ghostty but ignored by Windows Terminal pre-1.20 and the Linux console. For cursor *shape* (block / underline / bar), use DECSCUSR\x1b[N q— that's a CSI primitive, not a DEC private mode.4. Bracketed paste —
\x1b[?2004hWithout bracketed paste your shell or editor has no way to tell
cat | sudo rm -rf /typed character-by-character from the same string arriving as a single Cmd-V — they look identical on the wire. Send\x1b[?2004hand the terminal starts wrapping pasted text in markers:\x1b[200~before,\x1b[201~after. Now your input loop knows: anything between those markers came from the system clipboard, so don't auto-indent it, don't execute on\nmid-paste, don't interpretCtrl-Cinside. Bash 4.4+, zsh, fish, vim, neovim, emacs, and readline-based REPLs all opt in by default. The classic trap is forgetting to send\x1b[?2004lon exit — if your TUI crashes with bracketed-paste left on, the user's shell starts showing literal^[[200~in front of every paste they make until they runreset(1).5. Mouse tracking —
?1000+?1006Enable click events with
\x1b[?1000h(the X10 / VT200 format) — the terminal then reports each press as\x1b[Mbxywhereb,x,yare single bytes (x + 32,y + 32). The catch: single-byte coords mean column 224+ rolls over and your TUI thinks the user clicked at column 1. **Always pair?1000hwith?1006h** to switch into the SGR-extended encoding — reports become\x1b[<b;x;yM(press) /\x1b[<b;x;ym(release) with decimal integers, no cap. Add?1002hto also receive drag events while a button is held;?1003hfor *all* motion (heavy — every pixel of movement emits an event). Disable each with the matchingl. On exit, emit\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?1006lso the user's terminal is clean — otherwise every click in their shell prints escape-code spam.6. Focus events & sync update —
?1004,?2026Two live-UI conveniences.
\x1b[?1004henables focus reporting — the terminal sends\x1b[Iwhen its window gains keyboard focus and\x1b[Owhen it loses focus. Useful for pausing a live tail or clock redraw while the user is in another window, then resuming on return — htop, fzf, tig, and lazygit all do this.\x1b[?2026his the newer synchronized-update mode (kitty / iTerm2 / wezterm / ghostty / foot / VTE-based emulators since ~2022): everything written between?2026hand?2026lis buffered then committed to the screen atomically — no half-painted frames, no flicker on full repaints. The dance is?2026h→ emit cursor moves + every cell update for one frame →?2026l. Emulators that don't recognise the mode silently ignore it (writes still arrive sequentially, just without the atomic guarantee), so it's safe to emit unconditionally — no capability detection required.
All sequences in this family
- DECSC / DECRC — Save and restore cursor
\x1b7 (save) \x1b8 (restore)Push and pop the cursor state (position + attributes).
- DECSET 1049 — Alternate screen buffer
\x1b[?1049h (enter) \x1b[?1049l (leave)Switch to a separate screen buffer (like vim/less do on launch).
- DECTCEM ?25 — Show/hide cursor
\x1b[?25h (show) \x1b[?25l (hide)Show or hide the text cursor.
- DECSET ?2004 — Bracketed paste mode
\x1b[?2004h (enable) \x1b[?2004l (disable)Wrap pasted text in distinct escape markers so apps can tell paste from typing.
- DECSET ?1000 / ?1006 — Mouse tracking
\x1b[?1000h (click only) \x1b[?1002h (cell drag) \x1b[?1003h (any motion) \x1b[?1006h (SGR encoding)Receive mouse click / drag / scroll events as escape sequences.
- DECAWM ?7 — Auto-wrap mode
\x1b[?7h (enable wrap) \x1b[?7l (disable)Toggle whether the cursor wraps to the next line at the right margin (default: on).
- DECSET ?1004 — Focus in/out events
\x1b[?1004h (enable) \x1b[?1004l (disable)Make the terminal report when its window gains or loses keyboard focus.
- DECSET ?2026 — Synchronized update mode
\x1b[?2026h (begin frame) \x1b[?2026l (end frame)Buffer screen updates until you signal end-of-frame — eliminates flicker on full repaints.
- DECSET ?12 — Cursor blink
\x1b[?12h (start blinking) \x1b[?12l (stop blinking)Enable or disable the blinking attribute of the cursor — independent of cursor shape.
- DECOM ?6 — Origin mode (clip cursor addressing to scroll region)
\x1b[?6h (origin = region) \x1b[?6l (origin = screen)Make the cursor's row/column origin (1,1) be the top-left of the DECSTBM region instead of the screen.
- DECSET ?1047 — Alt screen without cursor save
\x1b[?1047h (enter alt) \x1b[?1047l (leave alt)Switch to / from the alt screen WITHOUT saving the cursor — the bare-bones precursor to ?1049.
- DECSDM — Sixel display mode (CSI ? 80 h / l)
\x1b[?80h (set) \x1b[?80l (reset)Choose the cursor's resting position after a sixel render — at top-left of the image (private-mode set) or below the image (reset, the modern default).
- DECSCNM — Reverse video screen mode (CSI ? 5 h / l)
\x1b[?5h (reverse) \x1b[?5l (normal)Globally swap foreground and background colours for the entire screen — the screen-wide reverse-video toggle, distinct from per-cell SGR 7.
- DECSCLM — Smooth scrolling mode (CSI ? 4 h / l)
\x1b[?4h (smooth) \x1b[?4l (jump)Toggle between smooth (one-line-per-frame animated) and jump (instant) scrolling — a DEC VT100 hardware-era setting that modern emulators almost universally ignore.
- DECARM — Auto-repeat keys mode (CSI ? 8 h / l)
\x1b[?8h (repeat) \x1b[?8l (no repeat)Toggle whether the terminal repeats key bytes while a key is held down — a TUI-relevant knob for games and editors where holding `j` should NOT spam the buffer.
- DECCOLM — 80 / 132 column mode (CSI ? 3 h / l)
\x1b[?3h (132 cols) \x1b[?3l (80 cols)Toggle between 80 and 132 column page width — DEC VT100's flagship 'wide mode' switch, gated by an opt-in resource on every modern emulator.
- DECNCSM — No Clear Screen on column-mode change (`CSI ? 95 h / l`)
\x1b[?95h (set — preserve) \x1b[?95l (reset — clear)Suppress DECCOLM's (and DECSCPP's) implicit screen-clear side-effect. With DECNCSM set, switching between 80- and 132-column modes preserves cell contents instead of wiping them.