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.
\x1bN SS2 \x1bO SS3 \x1bn LS2 \x1bo LS3 \x1b~ LS1R \x1b} LS2R \x1b| LS3R\033N / \033O / \033n / \033o / \033~ / \033} / \033|\eN / \eO / \en / \eo / \e~ / \e} / \e|ESC N / ESC O / ESC n / ESC o / ESC ~ / ESC } / ESC |1b 4e / 1b 4f / 1b 6e / 1b 6f / 1b 7e / 1b 7d / 1b 7cDescription
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
# 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# Box from DEC special graphics: ┌──┐ / │ │ / └──┘\nimport sys\nsys.stdout.write('\\x1b(0lqqk\\nx x\\nmqqj\\x1b(B\\n')\nsys.stdout.flush()// 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'// 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/* 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
| xterm | Linux console (fbcon) | macOS Terminal.app | iTerm2 | Windows Terminal | cmd.exe / ConPTY | kitty | alacritty | WezTerm | Ghostty | GNOME Terminal | Konsole | tmux | GNU screen |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | partial | partial | partial | partial | partial | partial | partial | partial | partial | yes | partial | yes | yes |
Related sequences
In the family cookbook
ESC cookbook · 5. Locking shifts + the 8-bit C1 bridge — `\x1bn` / `\x1bo` / `\x1b|` and `\x80-\x9F`