^[control-codes
live in libghostty-vt

Control sequences

CHA Cursor Character Absolute

View as Markdown

Move the cursor to column n of the line it is on.

CSI Pn G
Defaults
Pn = 1
ECMA-48
§8.3.9 CHA – Cursor Character Absolute, p. 34
VT510
CHA—Cursor Horizontal Absolute

Move the cursor to column Pn of the line it is on, counting from 1. The line does not change. A 0 is taken as 1, and a column past the right edge of the screen is clamped to the last column.

Left and right margins do not limit it: with them set, CHA can still move the cursor to any column on the line. Origin mode changes where it counts from, though. With origin mode on, column 1 is the left margin, as it is for CUP, and the column is clamped to the right margin.

HPA does the same thing with a different final byte. ECMA-48 defines CHA in terms of the presentation component, the image on the screen, and HPA in terms of the data component, the stored text the image is made from. It also says that for a device with no separate data component, references to the data component “are to be read as referring to” the presentation component (§6, p. 14). A terminal is such a device, and libghostty-vt handles both final bytes with the same code.

Support

TerminalVersionSupportNotes
VT220manual, 2nd ed., 1984Nonot in the manual
VT510manual, 1st ed., 1993YesCHA
xtermpatch 412, xterm-411aYes
libghostty-vt0f171f6YesCHA-5: in origin mode the cursor lands a line lower
kitty0.49.1, run in a VMPartlyfails CHA-5 of the 4 cases that show it working
foot1.28.0, run in a VMPartlyfails CHA-5 of the 4 cases that show it working
pyte0.8.2, run as a libraryPartlyfails CHA-5 of the 5 cases that show it working
xterm.js headless6.0.0, run as a libraryPartlyfails CHA-5 of the 5 cases that show it working
alacritty_terminal0.26.0, run as a libraryPartlyfails CHA-5 of the 5 cases that show it working
ConEmubuild 230724, its documentationYeslists ESC [ col G: ANSI escape codes
rxvt-unicode9.31, run in a VMPartlyfails CHA-5 of the 4 cases that show it working
VTE0.84.1, run as a libraryYespasses the 5 cases that show it working

Validation

CHA-1: To the first column by default

\e[1;4H
\e[G
X
|X____|
|_____|
cursor 1,2

CHA-2: To a column

\e[2;1H
\e[4G
X
|_____|
|___X_|

CHA-3: Clamped to the last column

\e[99G
X
|____X|
pending-wrap yes

CHA-4: Not limited by the left margin

\e[?69h   # allow left and right margins
\e[3;5s   # margins at columns 3 and 5
\e[1;4H
\e[1G
X
|X____|

CHA-5: Counted from the left margin in origin mode

\e[2;3r   # region is lines 2 and 3
\e[?69h   # allow left and right margins
\e[3;5s   # margins at columns 3 and 5
\e[?6h    # origin mode
\e[1;2H   # line 2, column 4 of the screen
\e[G
X
\e[?6l
|______|
|__X___|
|______|

The X should land in the left margin’s column on the line the cursor was on, which is what esctest2’s test_CHA_RespectsOriginMode checks. In libghostty-vt it lands a line lower. CHA there is setCursorPos(current line + 1, Pn), and in origin mode setCursorPos adds the top margin to the line it is given, so the line the cursor is already on is offset a second time.

Every example on these pages runs in your browser, in libghostty-vt compiled to WebAssembly.