^[control-codes live in libghostty-vt

Control sequences

CHA Cursor Character Absolute

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.

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.