Skip to main content
ansicode

Secondary DA reply decoder — `CSI > Pp ; Pv ; Pc c` model / firmware / cart

Decoder reference for the secondary Device Attributes reply — what each Pp model code means and how kitty / wezterm / alacritty / iTerm2 / Ghostty encode their version in Pv.

Byte forms

Every common string-literal form so you can paste-and-search either direction.

\\x1b[\x1b[>Pp;Pv;Pcc
\\033[\033[>0;276;0c
\\e[\e[>0;276;0c
ESC [ESC [ > Pp ; Pv ; Pc c
hex1b 5b 3e ... 63

Description

The *reply* shape for secondary DA (request side documented under `csi-da`). The application sends `\x1b[>c` (or `\x1b[>0c`); the terminal answers `\x1b[>Pp;Pv;Pc c` on stdin. TUIs branch on **Pp** to pick a feature set and on **Pv** to enable per-version workarounds — this page is the lookup table. **Pp — model code**: - `0` — DEC VT100 (or terminal claiming VT100 compatibility — common from minimal embedded emulators). - `1` — DEC VT220. - `2` — DEC VT240 / VT241. - `18` — DEC VT330. - `19` — DEC VT340. - `24` — DEC VT320. - `28` — DEC VT330 (alternative). - `32` — Tek 4014. - `41` — DEC VT420. - `61` — DEC VT510 (and most **xterm-class** emulators, including **xterm itself** patch ≥ 95). - `64` — DEC VT520. - `65` — DEC VT525 (and **Kitty**, **Alacritty**, **WezTerm**, **iTerm2** all use 65 as a 'modern xterm-class' marker by convention; **Ghostty** uses 65 too). - `82` — rxvt. - `83` — Eterm. - `84` — sun terminal. - `85` — rxvt-unicode (urxvt). **Pv — firmware / version**: - DEC hardware: literal ROM version (e.g. xterm patch level → Pv = 95+). - **xterm**: Pv is the xterm patch level — `\x1b[>61;392;0c` means xterm patch 392. - **Kitty**: Pv encodes the version — Pv = `major*10000 + minor*100 + patch`, so 0.41.1 → 4101 (older versions used base-100, check current behaviour). - **WezTerm**: Pv is a build year-month — e.g. 4604 ≈ 2024.10 build. - **Alacritty**: Pv is fixed at `0` (Alacritty doesn't expose version this way; use XTVERSION instead — see `xtversion`). - **iTerm2**: Pv is fixed at `95` (frozen at the original xterm-95 marker for compatibility — use XTVERSION for the real version). - **Ghostty**: Pv is the build patch level. - **Windows Terminal**: Pv is the WT version major*100 + minor (e.g. 0 for 1.0, 18 for 1.18). **Pc — cartridge / serial**: - DEC hardware: ROM cartridge number, almost always `0` on serial-only models. - Software emulators: always `0`. - Treat any non-zero Pc as informational only — no portable meaning. **Recommended consumer pattern**: ``` match (Pp, Pv) { (65, v) if v >= 1000 => 'modern xterm-class' # Kitty / WezTerm / Alacritty / iTerm2 (64, _) => 'VT520 or VT520-compat' (61, _) => 'xterm or xterm-class' (41, _) => 'VT420 or VT420-compat' (1, _) => 'VT220 or VT220-compat' (0, _) => 'VT100 or minimal' _ => 'unknown — feature-detect via XTVERSION or DECRQM' } ``` **Don't trust DA2 for fine-grained feature detection** — Pp coverage is coarse and Pv encoding is per-emulator. For 'does this terminal support feature X' use **XTVERSION** (`xtversion`) for the emulator name + version, then `DECRQM` (`decrqm`) to query the specific mode bit. Use DA2 only for the broad model bucket.

Spec citation: xterm-ctlseqs (Secondary DA reply) / DEC VT320+ DA2

Parameters

PpModel code — 0 VT100, 1 VT220, 41 VT420, 61 xterm-class, 64 VT520, 65 modern xterm-class (Kitty/WezTerm/Alacritty/iTerm2/Ghostty), 82 rxvt, 85 urxvt.
PvFirmware / version — encoding is per-emulator (xterm patch, Kitty major*10000+minor*100+patch, WezTerm year-month build, Alacritty=0, iTerm2=95). Use XTVERSION for portable version detection.
PcCartridge / serial — almost always 0 on software emulators; informational only.

Examples

bash
# Ask + decode model bucket.\nold=$(stty -g); stty -echo raw min 0 time 1\nprintf '\033[>c' > /dev/tty\nIFS= read -r -d c reply < /dev/tty\nstty "$old"\npp=$(echo "$reply" | sed -nE 's/.*>([0-9]+);.*/\\1/p')\ncase "$pp" in\n    0)   echo 'VT100 / minimal' ;;\n    1)   echo 'VT220-compat' ;;\n    41)  echo 'VT420-compat' ;;\n    61)  echo 'xterm-class' ;;\n    65)  echo 'modern xterm-class (Kitty/WezTerm/Alacritty/iTerm2/Ghostty)' ;;\n    82)  echo 'rxvt' ;;\n    85)  echo 'urxvt' ;;\n    *)   echo "unknown model: $pp" ;;\nesac
python
import sys, termios, tty, re\nMODELS = {0:'VT100/minimal',1:'VT220',41:'VT420',61:'xterm-class',64:'VT520',65:'modern xterm-class',82:'rxvt',85:'urxvt'}\nfd = sys.stdin.fileno(); old = termios.tcgetattr(fd); tty.setraw(fd)\ntry:\n    sys.stdout.write('\x1b[>c'); sys.stdout.flush()\n    buf = b''\n    while not buf.endswith(b'c'): buf += sys.stdin.buffer.read1(64)\n    m = re.search(rb'>(\d+);(\d+);(\d+)c', buf)\n    pp, pv, pc = (int(x) for x in m.groups())\n    print(f'model={MODELS.get(pp, "unknown")} ({pp}); version={pv}; cart={pc}')\nfinally:\n    termios.tcsetattr(fd, termios.TCSADRAIN, old)
go
// Decode the (Pp, Pv) bucket — Pc is informational.\nvar models = map[int]string{0:\"VT100\",1:\"VT220\",41:\"VT420\",61:\"xterm-class\",64:\"VT520\",65:\"modern xterm-class\",82:\"rxvt\",85:\"urxvt\"}\nfunc decodeDA2(reply string) (string, int) {\n    var pp, pv, pc int\n    fmt.Sscanf(reply, \"\\x1b[>%d;%d;%dc\", &pp, &pv, &pc)\n    return models[pp], pv\n}
javascript
// Parse a DA2 reply string into a labelled bucket.\nfunction decodeDA2(reply) {\n    const m = reply.match(/>(\d+);(\d+);(\d+)c/);\n    if (!m) return null;\n    const [pp, pv, pc] = m.slice(1).map(Number);\n    const models = {0:'VT100',1:'VT220',41:'VT420',61:'xterm-class',64:'VT520',65:'modern xterm-class',82:'rxvt',85:'urxvt'};\n    return { model: models[pp] || `unknown(${pp})`, version: pv, cartridge: pc };\n}
c
/* Decode a DA2 reply already buffered in `reply`. */\nint pp = 0, pv = 0, pc = 0;\nif (sscanf(reply, \"\\x1b[>%d;%d;%dc\", &pp, &pv, &pc) == 3) {\n    /* branch on pp / pv */\n}

Terminal support

xterm
yes
Linux console (fbcon)
partial
macOS Terminal.app
partial
iTerm2
yes
Windows Terminal
yes
cmd.exe / ConPTY
partial
kitty
yes
alacritty
partial
WezTerm
yes
Ghostty
yes
GNOME Terminal
yes
Konsole
yes
tmux
no
GNU screen
no

Related sequences

In the family cookbook

CSI cookbook · 4. Queries — DA, DA2, DSR, DECRQM