^[control-codes live in libghostty-vt

Control sequences

DECSCA Select Character Protection Attribute

Protect characters from erasure, then erase everything else on the line or the screen.

CSI Ps " q
Defaults
Ps = 0
DEC STD 070
§5.11.1 Select Character Attribute, p. 5-166
VT510
DECSCA—Select Character Protection Attribute

DEC’s way of filling in a form: draw the form’s labels protected, let the user type into the gaps, then clear just the typing and leave the form standing. It takes three functions.

DECSCA (CSI Ps " q) chooses whether the characters written from now on can be erased selectively. Like SGR, it applies only to what is written afterwards, and changes nothing already on the screen.

PsCharacters written afterwards
0, or left outcan be erased selectively
1are protected: the selective erases leave them alone
2can be erased selectively, as with 0

DEC STD 070 calls the attribute Selectively Erasable, so 1 turns it off and 2 turns it on; the VT510 manual describes it, as this page does, by what DECSED and DECSEL may erase.

DECSEL (CSI ? Ps K) and DECSED (CSI ? Ps J) are EL and ED with a ?, and take the same Ps: 0 from the cursor to the end of the line or the screen, 1 from the start to the cursor, and 2 all of it. They erase only the characters that are not protected. Neither is limited by the margins, nor DECSED by the scrolling region or origin mode. DEC STD 070 defines DECSEL in §5.11, p. 5-159 and DECSED on p. 5-162, and the VT510 manual as DECSEL and DECSED.

Plain EL and ED erase everything, protected or not. DEC STD 070 describes the two forms of each as one that erases all characters “regardless of their logical attributes”, and one that erases only the selectively erasable ones (§5.11.1.2, p. 5-165). xterm switches DEC protection off for the length of any erase that is not a selective one (do_erase_line, do_erase_display).

DEC STD 070 says a selective erase turns a character into a space and leaves “the character rendition and attributes associated with each position” unchanged. xterm does not keep them: it clears the cells it erases selectively with the same ClearCells that EL and ED use, which gives them the current colors and no other rendition. libghostty-vt does the same as xterm.

The protection attribute is part of what DECSC saves and DECRC restores.

With guarded areas

SPA and EPA protect characters too, ECMA-48’s way, and the two kinds meet in xterm’s protected_mode, which records whichever was used last: SPA sets it to ISO protection (CASE_SPA) and DECSCA to DEC protection (CASE_DECSCA). While it is ISO, every erase respects protection, the selective ones included. esctest2 holds that DECSED and DECSEL should not respect ISO protection (test_DECSED_doesNotRespectISOProtect), but marks the test as a known bug in xterm, which keeps it “for backward compatibility”. libghostty-vt does what xterm does.

DECSERA

DECSERA (CSI Pt ; Pl ; Pb ; Pr $ {) erases the unprotected characters of a rectangle (DEC STD 070 §5.12.1, p. 5-172, VT510 DECSERA). The rectangle works as it does for DECERA. xterm turns every character in it that DECSCA did not protect into a space and leaves the attributes alone (ScrnWipeRectangle).

DEC STD 070 makes DECSERA a level 4 function. libghostty-vt identifies itself as a VT220, level 2 (see DA), and has no handler for CSI … $ { in stream.zig, so it ignores it. xterm is a VT420 by default and implements it (CASE_DECSERA), so DECSCA-10 expects xterm’s result and is a known difference (see the sources page).

Validation

DECSCA-1: DECSEL leaves protected characters

AB
\e[1"q    # protect what follows
CD
\e[0"q    # and stop
EF
\e[1;1H
\e[?K     # selective erase to the end of the line
|__CD__|
cursor 1,1

DECSCA-2: From the start of the line

AB\e[1"qCD\e[0"qEF
\e[1;5H
\e[?1K
|__CD_F|
cursor 1,5

DECSCA-3: DECSED below and above

AB\e[1"qCD\e[0"qEF\r\n
GHIJKL
\e[2;3H
\e[?1J    # selective erase from the start of the screen
|__CD__|
|___JKL|

DECSCA-4: EL erases protected characters too

AB\e[1"qCD\e[0"qEF\r\n
GHIJKL
\e[1;1H
\e[K      # EL, not selective
\e[2;1H
\e[?K     # DECSEL on a line with nothing protected
|______|
|______|

DECSCA-5: Parameter 2 is the same as 0

AB\e[1"qCD
\e[2"q    # selectively erasable again
EF
\e[1;1H
\e[?2K
|__CD__|

DECSCA-6: DECSC saves the attribute

\e[1"q    # protect
\e7       # save the cursor
\e[0"q    # stop protecting
\e8       # restore: protecting again
CD
\e[0"q
EF
\e[1;1H
\e[?2K
|CD____|

DECSCA-7: Erased cells lose their rendition

\e[1;31m  # bold red
AB
\e[0m
\e[1;1H
\e[?2K
|______|
attr 1,1 plain
attr 1,2 plain

DECSCA-8: Guarded text survives DECSED too

AB
\eV       # SPA
CD
\eW       # EPA
EF
\e[?2J
|__CD__|
|______|

DECSCA-9: After DECSCA, ED erases guarded text

AB\eVCD\eW # CD guarded, ISO protection
\e[1"q    # DEC protection is now in force
EF
\e[0"q
\e[2J     # ED ignores DEC protection
|______|
|______|

DECSCA-10: DECSERA keeps protected characters

AB\e[1"qCD\e[0"qEF
\e[1;1;1;6\x24{ # DECSERA over the whole line
|__CD__|
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.