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
no
GNU screen
no

Related sequences

In the family cookbook

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