DECTABSR — Tab Stop Report (`CSI 2 $ w`)
Ask the terminal to dump its current horizontal tab stops as a DCS report. Closes the round-trip story: set via HTS (esc-hts), clear via TBC (csi-tbc), query the current set via DECTABSR.
Byte forms
Every common string-literal form so you can paste-and-search either direction.
\x1b[2$w\033[2$w\e[2$wESC [ 2 $ w1b 5b 32 24 77Description
DECTABSR — *Tab Stop Report* — is a DECRQPSR (Request Presentation State Report) variant that asks the terminal to enumerate its current horizontal tab stops as a DCS reply. It's the third leg of the tab-stop round trip:
- Set a tab stop at the current column: \x1bH (HTS — see esc-hts).
- Clear tabs: \x1b[g (this column) / \x1b[3g (all) (TBC — see csi-tbc).
- Query current tab stops: \x1b[2$w (DECTABSR — this entry).
Request shape: \x1b[2$w (the 2 is the Ps selector — 1 is DECCIR cursor-info-report, 2 is DECTABSR). The $ intermediate + w final mirror the DECRQPSR convention.
Reply shape (DECRPTAB): \x1bP2$u<col1>/<col2>/…/<colN>\x1b\\ — a DCS frame whose body is a slash-separated list of 1-indexed column numbers where tab stops currently live. Example reply (xterm default 8-column tabs on an 80-col screen):
```
\x1bP2$u9/17/25/33/41/49/57/65/73\x1b\\
```
Note there's no tab stop at column 1 (already cursor-home, no point) and the slashes separate but don't terminate. The DCS prefix \x1bP2$u echoes the request's 2$ plus u for 'unsolicited' / 'response' (analogous to DECRQSS's $r reply).
Round-trip pattern (save / restore terminal tab state across a TUI session): ``` startup: send \x1b[2$w -> receive \x1bP2$u9/17/25/...\x1b\\ parse: split body on '/' -> [9, 17, 25, ...] save stops_before exit: send \x1b[3g # TBC clear all for col in stops_before: send \x1b[<col>G\x1bH # CHA to col, HTS at col ```
This is what tput rs2 does on terminfo entries that declare dectabsr — the 'reset to known tab state' sequence reads + restores rather than blindly setting \x1b[?5W (DECST8C — set 8-column tabs).
Edge cases:
- Empty reply body (\x1bP2$u\x1b\\) means no tab stops are set at all — emit \x1b[?5W or write an 8-column HTS sequence to restore a default.
- Some emulators report the implicit 'end of line' stop (column == screen width + 1) — strip that out of your saved list.
- The reply uses \x1b\\ (ST as ESC + backslash) by default; xterm in 8-bit C1 mode (see decscl-compat-level) uses single-byte \x9c.
Coverage: xterm = full (reference). WezTerm / mlterm / Konsole = full. Ghostty = partial (emits reply but always with default 8-col tabs). Kitty / iTerm2 = partial (replies but may miss user-set stops on certain code paths). Alacritty / Linux console / Windows Terminal / cmd / ConPTY / macOS Terminal / gnome-terminal = no-op (no reply at all — be sure to time out probes at ~100 ms).
Spec citation: DEC VT510 RM (DECTABSR / DECRPTAB) / xterm-ctlseqs (CSI 2 $ w)
Examples
# Probe tab stops; read DCS reply with timeout.\nprintf '\\033[2$w'\nIFS= read -rs -d '\\\\' -t 0.1 reply </dev/tty 2>/dev/null\necho \"DECTABSR: ${reply}\" # expect: \\x1bP2$u9/17/25/...import sys, re\n# Parse the slash-separated tab list from a DECRPTAB reply.\nreply = '\\x1bP2$u9/17/25/33/41/49/57/65/73\\x1b\\\\'\nm = re.search(r'\\x1bP2\\$u([\\d/]*)\\x1b\\\\', reply)\nstops = [int(c) for c in m.group(1).split('/') if c] if m else []\nprint(stops)// Save-then-restore tab stops across a TUI's runtime.\nfmt.Print(\"\\x1b[2$w\") // request DECTABSR\n// ... read DCS reply, parse to []int ...\n// On exit: TBC all, then HTS at each saved col\nfmt.Print(\"\\x1b[3g\") // TBC 3 clears all\nfor _, col := range saved {\n fmt.Printf(\"\\x1b[%dG\\x1bH\", col)\n}// Detect 'no tab stops at all' (empty body).\nfunction parseDECTABSR(buf) {\n const m = /\\x1bP2\\$u([^\\x1b]*)\\x1b\\\\/.exec(buf);\n if (!m) return null;\n return m[1].split('/').filter(s => s.length).map(Number);\n}\nconsole.log(parseDECTABSR('\\x1bP2$u\\x1b\\\\')); // [] — no tabs at all/* Skeleton: probe + parse (cbreak stdin, timed read). */\nprintf(\"\\x1b[2$w\"); fflush(stdout);\n/* read until ST (0x1b 0x5c). then strtok on '/' between \"$u\" and ST. */Terminal support
- xterm
- yes
- Linux console (fbcon)
- no
- macOS Terminal.app
- no
- iTerm2
- partial
- Windows Terminal
- no
- cmd.exe / ConPTY
- no
- kitty
- partial
- alacritty
- no
- WezTerm
- yes
- Ghostty
- partial
- GNOME Terminal
- no
- Konsole
- yes
- tmux
- yes
- GNU screen
- yes
| 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 | partial | no | no | partial | no | yes | partial | no | yes | yes | yes |