ttyscan
ttyscan queries a terminal emulator for its type, size, capabilities, and identity. Creating a curses-compatible terminfo(5) file entry if necessary, and exports any corrected values of TERM, TERM_PROGRAM, COLORTERM, LINES, COLUMNS, and optionally TERMCAP (termcap(5)) environment variables.
Problem
ncurses does not support XTGETTCAP, and so terminfo(5) files for some terminals must be deployed to remote systems by system operators in some circumstances.
Curses programs fail to start until those files are deployed or an alternate TERM is exported. All kinds of errors and warnings may be raised until this is done, some examples:
$ tmux attach missing or unsuitable terminal: xterm-kitty $ irssi setupterm() failed for TERM=xterm-kitty: 0 Can't initialize screen handling. $ htop Error opening terminal: xterm-kitty. $ python -c 'import curses;curses.setupterm()' Traceback (most recent call last): File "<string>", line 1, in <module> _curses.error: setupterm: could not find terminal
Solution
ttyscan creates the missing terminfo(5) file using the XTGETTCAP (DCS +q) terminal query protocol when supported, and suggests a new TERMINFO environment value for export, allowing legacy calls to curses of setupterm(3) to succeed:
$ ttyscan -vft
ttyscan: probing terminal via XTGETTCAP ...
ttyscan: size via dual CPR: 28x100
ttyscan: XTGETTCAP supported, terminal: rio; received 198 caps (11 bool, 5 num, 178 str) in 4ms
ttyscan: writing /home/jq/.config/ttyscan/terminfo/r/rio
export COLORTERM=truecolor
export TERM=rio
export LINES=28
export COLUMNS=100
export TERMINFO=/home/jq/.config/ttyscan/terminfo
export TERMCAP='rio|XTGETTCAP-discovered terminal::am::ut::hs::km::5i::mi::ms::NP::xn::Co#256::co#80::it#8::li#24::pa#32767::ac=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~::bl=^G::mb=\E[5m::md=\E[1m::bt=\E[Z::vi=\E[?25l::cl=\E[H\E[2J::ve=\E[?12l\E[?25h::cr=\r::cs=\E[%i%p1%d;%p2%dr::le=\b::DO=\E[%p1%dB::do=\n::nd=\E[C::cm=\E[%i%p1%d;%p2%dH::up=\E[A::vs=\E[?12;25h::dC=\E[P::mh=\E[2m::dl=\E[M::ds=\E]2;^G::ec=\E[%p1%dX::cd=\E[J::ce=\E[K::cb=\E[1K::vb=\E[?5h$<100/>\E[?5l::fs=^G::ho=\E[H::ch=\E[%i%p1%dG::ta=\t::st=\EH::al=\E[L::sf=\n::mk=\E[8m::is=\E[!p\E[?3;4l\E[4l\E>::K2=\EOE::kb=^?::kB=\E[Z::kl=\EOD::kd=\EOB::kr=\EOC::ku=\EOA::kD=\E[3~::@7=\EOF::@8=\EOM::k1=\EOP::k0=\E[21~::F1=\E[23~::F2=\E[24~::k2=\EOQ::k3=\EOR::k4=\EOS::k5=\E[15~::k6=\E[17~::k7=\E[18~::k8=\E[19~::k9=\E[20~::kh=\EOH::kI=\E[2~::kH=\E[1;2B::kN=\E[6~::kP=\E[5~::ps=\E[i::pf=\E[4i::po=\E[5i::op=\E[39;49m::rp=%p1%c\E[%p2%{1}%-%db::mr=\E[7m::sr=\EM::ZR=\E[23m::ae=\E(B::te=\E[?1049l\E[23;0;0t::ei=\E[4l::ke=\E[?1l\E>::se=\E[27m::ue=\E[24m::r1=\Ec\E]104^G::r2=\E[!p\E[?3;4l\E[4l\E>::sa=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m::me=\E(B\E[m::ZH=\E[3m::as=\E(0::ti=\E[?1049h\E[22;0;0t::im=\E[4h::ks=\E[?1h\E=::so=\E[7m::us=\E[4m::ct=\E[3g::ts=\E]2;::cv=\E[%i%p1%dd:'
$ eval `ttyscan`
$ echo $TERM, $TERMINFO
rio, /home/jquast/.config/ttyscan/terminfo
$ file -b $TERMINFO/r/rio
Compiled terminfo entry "rio"
$ python -c 'import curses;curses.setupterm()'; echo $?
0
CLI Arguments
usage: ttyscan [-h] [-v] [-f] [-t] [--version] Export terminal capabilities discovered via XTGETTCAP options: -h, --help show this help message and exit -v, --verbose Print diagnostic information to stderr -f, --force Force export of all values even if unchanged -t, --termcap Also export TERMCAP value --version show program's version number and exit
ttyscan saves a terminfo(5) file to $XDG_CONFIG_HOME/ttyscan/terminfo or ~/.config/ttyscan/terminfo, and does not re-query or re-create it when it already exists from previous executions, unless --force is used.
Typical total execution time for remote shells is under 200ms.
Installation
pip install ttyscan
ttyscan requires Python3.6 or newer.
ttyscan.py is a stand-alone python file, it does not require pip to install, you can copy this file from source and execute it directly, eg. python ~/bin/ttyscan.py
Motive
Naturally, transferring your $HOME/.terminfo folder to a remote machine is the best solution.
However, enterprise systems, bastion hosts, cloud systems, web consoles, hypervisors, radio, and serial ports can be challenging places or protocol layers through which to deploy terminfo files.
Some workarounds include exporting a generally-compatible TERM=xterm-256color or xterm with sometimes minor corruption of screen output, missed interpretation of keyboard input such as backspace or delete, or a small reduction of features such as italic or underlined text or number of colors.
ttyscan.py serves as a demonstration: that a full terminal capability database can be transferred using XTGETTCAP for many modern terminals. Further, by use of data collected by ucs-detect, terminals that do not respond to XTVERSION or ENQ inquires can be identified by their unique fingerprints of DA1 response and their unicode terminal width “quirks”.
My hope is that ncurses and setupterm(3) could someday negotiate with full XTGETTCAP support at some point in the future, so that remote deployment of terminfo files are not necessary, and that this utility would not be used or required!
Scope
At time of this writing (November 2026), the ucs-detect dataset of 42 terminals shows three categories of support for XTGETTCAP:
Full XTGETTCAP capability support: AbsoluteTelnet/SSH, Bobcat, contour, foot, ghostty, iTerm2, kitty, rio, WezTerm, and XTerm.NET transmit their complete terminfo boolean, numeric, and string capabilities via XTGETTCAP.
ttyscan produces terminfo files for only these terminals. ttyscan may also discover a preferred TERM from TN, and COLORTERM=truecolor from RGB.
Partial XTGETTCAP capability support: GNOME Terminal, mlterm, TeraTerm, Terminator, XFCE Terminal, and XTerm report only TN, Co, and RGB.
ttyscan may only discover preferred TERM from TN, and COLORTERM=truecolor from RGB.
XTerm supports only keyboard sequences in addition to TN, Co, and RGB. It does not report all capabilities, and so a terminfo(5) entry cannot be built. However, TERM=xterm-256color and TERM=xterm are the most ubiquitous terminal name, you should be just fine.
No Support: alacritty (refuses: alacritty/vte#98), Extraterm, Hyper, Konsole (requested: KDE#507017), libvterm, linux fbdev, mintty, pterm, QTerminal, rxvt-unicode, screen, securecrt, st, SyncTERM, Tabby, Terminal.exe (planned: microsoft/terminal#17735), terminology, tmux (passthrough: tmux/tmux#3755), VS Code (xterm.js, proposal: xtermjs/xterm.js#4107), Warp, weston-terminal, Zellij, and zutty, QTerminal (leaky sequence fixed upstream).
Non-Compliant: ConEmu, cool-retro-term, and Apple Terminal: When a terminal emulator does not support XTGETTCAP, it should silently consume VT100 Mode DCS queries without answer. However, these terminals display XTGETTCAP query output, eg. +q544e+q524742+q636f6c6f7273+q626c696e6b+.. is visibly written to screen: cool-retro-term, ConEmu.exe, and Apple’s Terminal.app all fail to parse DCS queries in this way.
Architecture
ttyscan uses the following,
XTGETTCAP field TN is used to correct TERM when unmatched.
XTGETTCAP field RGB is used to correct COLORTERM when unmatched.
all capability strings, keyboard and screen, when provided by XTGETTCAP.
DEC Private Mode 2048 (In-Band Resize) to determine COLUMNS and LINES
Or failing that, using Cursor Position Report like XTerm’s resize.c
DECRQSS (Request Setting Selection) to probe truecolor support when RGB is unavailable, by setting and querying a 24-bit background color and exporting COLORTERM if successful
DA1 response is received and Unicode widths are probed to identify TERM_PROGRAM.
Every query uses a following cursor position report (CPR) sequence to test for negative response (no support), so the timeout elapses for terminal automation (such as tcl/expect pexpect) that acts as a terminal but is not one. The default timeout is set by environment value, TTYSCAN_QUERY_TIMEOUT=10.0 (float) in seconds.
Full vs. Partial XTGETTCAP
The difference of Full and Partial XTGETTCAP support is best described by foot:
XTGETTCAP is an escape sequence initially introduced by XTerm, and also implemented (and extended, to some degree) by Kitty.
Applications using this feature do not need to use the classic, file-based, terminfo definition.
XTerm’s implementation (as of XTerm-370) only supports querying key (as in keyboard keys) capabilities, and three custom capabilities:
TN - terminal name Co - number of colors (alias for the colors capability) RGB - number of bits per color channel (different semantics from the RGB capability in file-based terminfo definitions!).
Kitty has extended this, and also supports querying all integer and string capabilities.
Foot supports this, and extends it even further, to also include boolean capabilities. This means foot’s entire terminfo can be queried via XTGETTCAP.
Forwarding of Environment Variables
Further, all of TERM, COLORTERM, LINES, or COLUMNS may not be transmitted by all software or protocols, some examples:
ssh does not forward COLORTERM unless configured using SendEnv in ssh_config(5) and AcceptEnv in sshd_config(5).
serial does not forward any; TERM is defined by host agetty(8) configuration, for example.
rlogin can forward all but COLORTERM.
telnet can forward all, but IAC NAWS and NEW-ENVIRON capability varies by software.
websocket cannot forward any (without customized subprotocol), often used in web browsers.
ttyscan detects when these variables are unset or do not match values and re-exports the corrected values when they differ.
Terminal Identification
When TERM_PROGRAM is unset (or --force is used), ttyscan attempts to identify it:
using XTVERSION and reply (CSI > q, answered DCS > | name version ST)
using ENQ (0x05) and takes the printable answerback
If both are silent it sends DA1 (CSI c), which all terminals reply with a service class and a set of extension numbers such as CSI ? 62 ; 4 ; 9 ; 22 c. When that DA1 response is not unique, ttyscan draws complex grapheme clusters (complex unicode strings), narrowing the unique “quirks” of a terminal’s unicode measurements by Cursor Position Response until one remains.
For example, Apple’s Terminal.app may be remotely identified even though it does not answer XTVERSION or ENQ, although it does export TERM_PROGRAM, ssh does not forward it. Further, its DA1 reply is not unique, and so it is identified only by its unicode “quirks”.
These “quiz tables” are generated automatically from data collected by ucs-detect, by command tox -e update. At time of this writing (November 2026) this allows identifying the following terminals that otherwise do not identify themselves:
alacritty, ConEmu, cool-retro-term, Extraterm, Hyper, libvterm, QTerminal, securecrt, st, Tabby, TeraTerm, weston-terminal, WindowsTerminal, zutty.
Metadata
Release files for ttyscan 0.0.10
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ttyscan-0.0.10.tar.gz | 38.2 kB | Without provenance | Details for ttyscan-0.0.10.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ttyscan-0.0.10-py3-none-any.whl | Python 3 | none | any | Details for ttyscan-0.0.10-py3-none-any.whl |
Total release size: 58.0 kB
Release files / ttyscan-0.0.10.tar.gz
| Download URL | ttyscan-0.0.10.tar.gz |
|---|---|
| Size | 38.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
228dceb41d249a75ef00a51232e760f2671af8db431543d55e930d5255c06e00
|
|
BLAKE2b-256 checksum How to use checksums |
558ebff8f6c3968e6c1292881639bbb5adcb5acf405969c963542375ca72ee28
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / ttyscan-0.0.10-py3-none-any.whl
| Download URL | ttyscan-0.0.10-py3-none-any.whl |
|---|---|
| Size | 19.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3898a1222309522a8819962f453a1c086b6f02fd07e80a23241948e9ade430ef
|
|
BLAKE2b-256 checksum How to use checksums |
dffcee069ec7d90705b49d058c3de631141227c00e673df5ed42f69d7309b642
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Security for ttyscan 0.0.10
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.