XTPUSHSGR, XTPOPSGR Push and Pop Video Attributes
Save the current SGR attributes on a stack, or some of them, and bring them back later.
XTPUSHSGR Push Video Attributes
CSI Pm # {
XTPOPSGR Pop Video Attributes
CSI # }
XTPUSHSGR Push Video Attributes
CSI Pm # p
XTPOPSGR Pop Video Attributes
CSI # q
- xterm
- Functions using CSI
CSI # { saves the current SGR attributes, the ones the next character will be written with, and CSI # } brings them back. A program can change the colors and style for a while and return to whatever was set before, without having to know what that was. xterm keeps the saved attributes on a stack, so pushes and pops nest. CSI # p and CSI # q are the same two functions, there for languages in which a { in a string is awkward (csi_hash_table).
With no parameters, a push saves all of the attributes. With parameters, named by their SGR numbers, it saves only those, and the matching pop restores only those and leaves the rest as they are:
Pm | Attribute |
|---|---|
1 | bold |
2 | faint |
3 | italic |
4 | underline |
5 | blink |
7 | inverse |
8 | invisible |
9 | crossed out |
21 | double underline |
30 | the foreground color |
31 | the background color |
A color has many SGR numbers, so 30 and 31 stand for all of them; 10 and 11, which older xterms used for the colors, are still taken. Any other number is ignored. A pop with nothing pushed does nothing.
The stack is ten deep. xterm counts pushes past the tenth without saving anything for them, and the pops that match them restore nothing, so pushes and pops still pair up: after eleven pushes, the first pop restores nothing and the second restores what the tenth push saved (xtermPushSGR, xtermPopSGR). Nothing resets the stack: neither RIS nor DECSTR touches it, so what was pushed before a reset can be popped after it. These are xterm’s own, and in a default build (OPT_XTERM_SGR). xterm also has XTREPORTSGR, CSI Pt ; Pl ; Pb ; Pr # |, which reports the attributes that every cell in a rectangle shares.
Contour’s SGRSAVE and SGRRESTORE. Contour’s Save and Restore SGR attributes, read at commit d5541aa, gives CSI # { and CSI # } other names and a simpler meaning: one saved set of attributes rather than a stack, and no parameters. It says that pushing and popping some attributes and not others is functionality a terminal need not have and a program can manage for itself. In Contour at release 0.8.0, CSI # { saves all of the attributes over whatever was saved before, CSI # } restores them as often as it is sent, and a push with parameters is not recognized (Screen::saveGraphicsRendition, Functions.hpp). A program that pushes once and pops once gets the same from both; one that nests gets the innermost attributes back each time from Contour.
libghostty-vt recognizes neither function and ignores both, so the cases in which a pop changes anything are known differences.
Support
| Terminal | Version | Support | Notes |
|---|---|---|---|
| VT220 | manual, 2nd ed., 1984 | No | not in the manual |
| VT510 | manual, 1st ed., 1993 | No | not in the manual |
| xterm | patch 412, xterm-411a | Yes | |
| libghostty-vt | 0f171f6 | No | XTPUSHSGR-1 to XTPUSHSGR-3, XTPUSHSGR-5 to XTPUSHSGR-7: ignores both |
| kitty | 0.49.2, its documentation | No | not in its CSI dispatch; CSI # P and CSI # Q push and pop colors instead (vt-parser.c) |
| VTE | 0.84.1, source | No | recognized and ignored (vteseq.cc) |
Validation
XTPUSHSGR-1: Pop brings everything back
\e[1;31m
\e[#{
\e[0;4m
A
\e[#}
B
|AB__|
attr 1,1 underline -bold fg=default
attr 1,2 bold -underline fg=1
XTPUSHSGR-2: Only the attributes named
\e[1;31m
\e[1#{ # save bold only
\e[0m
A
\e[#}
B
|AB__|
attr 1,1 plain
attr 1,2 bold fg=default
XTPUSHSGR-3: Pushes nest
\e[1m
\e[#{
\e[3m
\e[#{
\e[0m
A
\e[#}
B
\e[#}
C
|ABC_|
attr 1,1 plain
attr 1,2 bold italic
attr 1,3 bold -italic
XTPUSHSGR-4: A pop with nothing pushed does nothing
\e[1m
\e[#}
A
|A___|
attr 1,1 bold
XTPUSHSGR-5: The other final bytes
\e[1m
\e[#p
\e[0m
A
\e[#q
B
|AB__|
attr 1,1 plain
attr 1,2 bold
XTPUSHSGR-6: Past ten, a push saves nothing
\e[1m
\e[#{\e[#{\e[#{\e[#{\e[#{\e[#{\e[#{\e[#{\e[#{\e[#{ # ten, all bold
\e[0;3m
\e[#{ # the eleventh is not saved
\e[0m
\e[#} # so its pop restores nothing
A
\e[#} # and the next brings back the tenth
B
|AB__|
attr 1,1 plain
attr 1,2 bold -italic
XTPUSHSGR-7: RIS leaves the stack alone
\e[1m
\e[#{
\ec
\e[#}
A
|A___|
attr 1,1 bold