Skip to main content
ansicode

Single Shift / Locking Shift family — SS2 / SS3 / LS2 / LS3 / LS1R / LS2R / LS3R

ISO 2022 character-set switching: invoke G2 / G3 once (SS2 / SS3), or lock GL / GR to a different G-set (LS2 / LS3 / LS1R / LS2R / LS3R). Pairs with \x1b( / \x1b) / \x1b* / \x1b+ G-set designators.

Byte forms

Every common string-literal form so you can paste-and-search either direction.

\\x1b[\x1bN SS2 \x1bO SS3 \x1bn LS2 \x1bo LS3 \x1b~ LS1R \x1b} LS2R \x1b| LS3R
\\033[\033N / \033O / \033n / \033o / \033~ / \033} / \033|
\\e[\eN / \eO / \en / \eo / \e~ / \e} / \e|
ESC [ESC N / ESC O / ESC n / ESC o / ESC ~ / ESC } / ESC |
hex1b 4e / 1b 4f / 1b 6e / 1b 6f / 1b 7e / 1b 7d / 1b 7c

Description

The ISO 2022 / ECMA-35 character-set machinery uses four registers (G0, G1, G2, G3) each pointing at a designated character set, with two 'pointers' (GL = invoked for 0x20–0x7F, GR = invoked for 0xA0–0xFF) selecting which G-set is currently active for which byte range. The single-shift and locking-shift families switch the active mapping.

Designators (set what each G-register points at — covered separately, listed here for context): - \x1b(<final> — designate G0 (e.g. \x1b(0 = G0 → DEC Special Graphics, the line-drawing set) - \x1b)<final> — designate G1 - \x1b*<final> — designate G2 - \x1b+<final> — designate G3

Invokers (switch GL / GR to point at a different G-register — this entry): - SS2 \x1bN (ESC N) — *Single Shift 2* — the next single 7-bit byte is interpreted via G2, then GL reverts. Useful for inserting a single character from a designated set without locking. - SS3 \x1bO (ESC O) — *Single Shift 3* — same as SS2 but uses G3. Note: ESC O is also the SS3 prefix in many keypad / function key codes (\x1bOA = up-arrow in application mode — see keymap), which is the same SS3 in spirit. - LS2 \x1bn (ESC n) — *Locking Shift 2* — point GL at G2 until a subsequent shift. All subsequent 7-bit bytes go through G2. - LS3 \x1bo (ESC o) — *Locking Shift 3* — point GL at G3. - LS1R \x1b~ (ESC ~) — *Locking Shift 1 Right* — point GR at G1 (8-bit byte range 0xA0–0xFF flows through G1). - LS2R \x1b} (ESC }) — *Locking Shift 2 Right* — point GR at G2. - LS3R \x1b| (ESC |) — *Locking Shift 3 Right* — point GR at G3. - (LS0 = \x0F SI and LS1 = \x0E SO are C0 single-byte controls — see c0-controls.)

Why you'd see these in the wild: 1. DEC line drawing — \x1b(0 designates G0 → DEC Special Graphics, then code 0x6C (l) renders as . Frequently used by ncurses for box-drawing on non-UTF-8 terminals. 2. Legacy mainframe / serial-line apps that ship 7-bit binary 'paths' through GR-mapped G-sets to avoid 8-bit MSB-stripping middleware. 3. NRCS (National Replacement Character Sets) — e.g. \x1b)A designates G1 → UK NRCS (substitutes £ for #). 4. Telnet/SSH options for 7-bit data paths.

Modern UTF-8 era reality: ISO 2022 is largely vestigial. Terminals that announce UTF-8 (\x1b%G / \x1b%@ is the UTF-8 designate / leave-UTF-8 pair) typically interpret incoming bytes as UTF-8 and ignore SS2/SS3/LS2/LS3 entirely. The exception is DEC Special Graphics — kept alive because ncurses still emits \x1b(0 for line drawing on terminals that don't have full Unicode box glyphs.

Coverage: xterm = full (reference). kitty / WezTerm / iTerm2 / Ghostty / Konsole: support \x1b(0 line-drawing path; SS2/SS3/LS2/LS3/LS{1,2,3}R variously implemented but the practical use case is line drawing. gnome-terminal = full. Alacritty = partial (DEC Special Graphics + UTF-8 only). Linux console = partial (\x1b(0 works, more exotic invokers are no-ops). Windows Terminal / cmd / ConPTY: line-drawing via \x1b(0 partial; locking-shift right (\x1b~/\x1b}/\x1b|) no-op. macOS Terminal = partial. Don't rely on anything beyond DEC Special Graphics + designators unless you've specifically probed.

Spec citation: ISO 2022 / ECMA-35 / xterm-ctlseqs (Single Shift + Locking Shift family)

Examples

bash
# DEC line-drawing top-left corner: designate G0 -> SpecGraphics, emit 'l', restore.\nprintf '\\033(0l\\033(B'   # 'l' renders as ┌; \\033(B restores G0 -> US-ASCII
python
# Box from DEC special graphics: ┌──┐ / │  │ / └──┘\nimport sys\nsys.stdout.write('\\x1b(0lqqk\\nx  x\\nmqqj\\x1b(B\\n')\nsys.stdout.flush()
go
// Single-shift: print one G2 char without locking.\n// (G2 designated to DEC special graphics first.)\nfmt.Print(\"\\x1b*0\")   // designate G2 -> SpecGraphics\nfmt.Print(\"\\x1bNl\")    // SS2 then 'l' -> single ┌\nfmt.Print(\"X\")          // back to GL=G0 (US-ASCII) -> literal 'X'
javascript
// Lock GR to G2 for an 8-bit data path. Reset with LS0 (SI = \\x0f).\nprocess.stdout.write('\\x1b*A\\x1b}');  // G2 -> UK NRCS, LS2R\n// ... write 8-bit bytes; £ now lives at 0xA3 via G2\nprocess.stdout.write('\\x1b~');           // LS1R back to G1 default
c
/* ncurses-style box-drawing entry; designate G0 to SpecGraphics. */\nprintf(\"\\x1b(0\");\nputs(\"lqqk\");  /* ┌──┐ */\nputs(\"x  x\");  /* │  │ */\nputs(\"mqqj\");  /* └──┘ */\nprintf(\"\\x1b(B\");                /* restore G0 to US-ASCII */

Terminal support

xterm
yes
Linux console (fbcon)
partial
macOS Terminal.app
partial
iTerm2
partial
Windows Terminal
partial
cmd.exe / ConPTY
partial
kitty
partial
alacritty
partial
WezTerm
partial
Ghostty
partial
GNOME Terminal
yes
Konsole
partial
tmux
yes
GNU screen
yes

Related sequences

In the family cookbook

ESC cookbook · 5. Locking shifts + the 8-bit C1 bridge — `\x1bn` / `\x1bo` / `\x1b|` and `\x80-\x9F`