^[control-codes live in libghostty-vt

Device control strings

Sixel Sixel Graphics

Draw a bitmap image in the text, six pixels high at a time, from a device control string.

DCS P1 ; P2 ; P3 q s…s ST
Defaults
P1 = 0, P2 = 0
VT330/VT340
§14.2.1 Device Control String
xterm
Sixel Graphics

A sixel is a column of six pixels, and a sixel image is a string of them. It is sent in a device control string whose final byte is q, with no intermediate byte. Each data character from ? to ~ is one sixel: its value less 63 gives six bits, the least significant at the top. The VT330/VT340 manual defines the format in its chapter 14, §14.2. The parameters are:

  • P1, the pixel aspect ratio: 0, 1, 5 and 6 (and the default) make each pixel twice as tall as it is wide, 2 five times, 3 and 4 three times, and 7 to 9 square. The raster attributes below override it.
  • P2, what a 0 bit means: with 0 or 2 (the default), it is drawn in the background color; with 1, it is left as it was, so the image is transparent there.
  • P3, the horizontal grid size, which the VT300 ignores.

Within the data, a few characters are controls rather than sixels (§14.3):

CharacterFunction
! Pn srepeat the sixel s Pn times
" Pan ; Pad ; Ph ; Pvraster attributes: the aspect ratio as a fraction, and the image size
# Pcdraw in color register Pc from now on
# Pc ; Pu ; Px ; Py ; Pzdefine color register Pc, in HLS (Pu = 1) or RGB (Pu = 2)
$graphics carriage return: back to the left of the same sixel row
-graphics new line: down six pixels, to the left

Where the image goes, and where the text cursor is left when it ends, depend on DECSDM, mode 80.

The VT330 and the VT340 have sixel graphics, and so did the VT240 before them; the VT510 does not. xterm has them too, but not as a default xterm. Sixel support is compiled in unless configure is given --disable-sixel-graphics (configure.in), and it is then turned on only when xterm is a graphics terminal, a 240, 241, 330, 340 or 382 (ptyx.h). That is the decGraphicsID resource if it names one of those, and decTerminalID otherwise (charproc.c). Both default to a VT420 (ptyx.h), so begin_sixel sets nothing up (charproc.c), the string is collected like any other, and at ST it is dropped (misc.c). Nothing is drawn and the cursor does not move, and that is what the cases expect, as the sources page explains. Started as xterm -ti vt340, the same xterm draws the image. The case runner sees characters, not pixels, so even there it could check only where the cursor ends up.

The answer to DA is how a program can tell: a terminal with sixel graphics includes 4 in it, and a default xterm does not.

Not in libghostty-vt

libghostty-vt has no sixel graphics. Its DCS handler in dcs.zig recognizes q only after + (XTGETTCAP) or $ (DECRQSS), and drops any other device control string, so a sixel image is dropped too. That is what a default xterm does, so both cases pass. Ghostty draws images with the kitty graphics protocol instead.

Support

TerminalVersionSupportNotes
VT220manual, 2nd ed., 1984Nothe VT220 has no graphics
VT340manual, vol. 2, 2nd ed., 1988Yes§14 Sixel Graphics
VT510manual, 1st ed., 1993Nothe VT510 manual has no sixel graphics
xtermpatch 412, xterm-411aNoconsumed and ignored: compiled in, but on only as a VT240, VT330, VT340 or VT382, and a default xterm is a VT420
libghostty-vt83edd49Nothe string is dropped
Konsole26.11.70, a24c3d71Yesdraws them (Vt102Emulation.cpp), and answers DA with 62;1;4 (Vt102Emulation.cpp)
ConEmubuild 230724, its documentationNonot in ConEmu’s list, which has no DCS; ANSI escape codes

Validation

SIXEL-1: Nothing is drawn, and the cursor does not move

A
\eP0;0;8q"1;1;4;12\#1;2;100;0;0\#1~~~~-~~~~\e\\   # a red 4×12 image
B
|AB____|
|______|
cursor 1,3
reply none

SIXEL-2: Text before ST is part of the image

\ePq~~~~   # never terminated
ABC        # sixels, like the rest
|______|
cursor 1,1
Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.