SO/SI
SO (Shift Out) and SI (Shift In) are C0 control characters used for character set switching. SO invokes the G1 character set into GL, while SI restores the G0 character set.
Syntax
SO 0x0E Shift Out — invoke G1 into GL
SI 0x0F Shift In — invoke G0 into GL
SO = 0x0e ; SI = 0x0f ;
Description
Per ECMA-48 §8.3.126 and §8.3.119, SO and SI are used for code extension purposes, causing "the meanings of the bit combinations following it in the data stream to be changed." Their use is defined in ECMA-35.
In the VT100 character set architecture, four character sets (G0, G1, G2, G3) can be designated via escape sequences, and two of them can be active at once — one invoked into GL (the left half, 0x20-0x7F) and one into GR (the right half, 0xA0-0xFF).
- SO (0x0E) invokes G1 into GL. Subsequent graphic characters in the 0x20-0x7F range are interpreted according to the G1 character set.
- SI (0x0F) invokes G0 into GL, restoring the default mapping.
By default, G0 is ASCII and G1 is the DEC Special Graphics character set (line-drawing characters).
Common use
The most common use of SO/SI is to enable line-drawing characters for drawing boxes and borders in TUI applications. A program sends SO to switch to the line-drawing set, outputs the box characters, then sends SI to return to ASCII.
In 8-bit environments, SO and SI are known as LS1 (Locking-Shift One) and LS0 (Locking-Shift Zero), respectively. They are functionally identical.
Examples
# Draw a simple box using DEC line-drawing characters
printf '\e(B' # Designate ASCII into G0
printf '\e)0' # Designate DEC Special Graphics into G1
printf '\x0e' # SO — switch to G1 (line drawing)
printf 'lqqk\n' # ┌──┐
printf 'x x\n' # │ │
printf 'mqqj\n' # └──┘
printf '\x0f' # SI — switch back to G0 (ASCII)
Specifications
| Specification | Section |
|---|---|
| ECMA-48 | §8.3.119, §8.3.126 |
Terminal support
| Terminal | Support | Version | Notes |
|---|---|---|---|
| Terminal Emulators | |||
| Alacritty | ~ | Yes | Charset switching supported via configure_charset/set_active_charset, but only charsets B and 0 supported per escape_support.md |
| Bobcat | ✓ | 0.9.0 | Character set handling in TerminalCtrl, UTF-8 mode configurable |
| contour | ✓ | v0.3.10 | SO (0x0E) and SI (0x0F) implemented as LS1/LS0; invokes G1/G0 charset into GL |
| foot | ✓ | Yes | Implemented in vt.c:241-255, selects G0/G1 character sets |
| Ghostty | ✓ | v1.0.0 | SO/SI handled in stream.zig:772-773, invoke G1/G0 charsets |
| iTerm2 | ✓ | Yes | |
| Kitty | ✓ | Yes | |
| Konsole | ✓ | Yes | G1/G0 charset invocation |
| mintty | ✓ | Yes | SO (Shift Out/LS1) at src/termout.c:1715, SI (Shift In/LS0) at src/termout.c:1720 |
| mlterm | ✓ | rel-0_2_1 | SO/SI handled natively; ISO-2022 encodings delegated to mef library |
| PuTTY | ✓ | 0.45 | Implemented as LS1/LS0 for G1/G0 charset selection |
| Rio | ✓ | v0.0.28 | Shifts between G0 and G1 character sets |
| rxvt-unicode | ✓ | rxvt-unicode-1.2 | |
| st | ✓ | 0.5 | Charset switching added in 0.5, control codes in 0.6 |
| terminology | ✓ | v0.1.0 | |
| VT100 | ✓ | Yes | |
| VTE | ~ | Yes | Implemented as LS1/LS0. Only G1 character set mapping is functional |
| WezTerm | ✓ | Yes | Handled in performer.rs:466-470 via ControlCode::ShiftOut/ShiftIn, controls G0/G1 charset selection for DEC line drawing |
| Windows Terminal | ✓ | Yes | SI (0x0F) calls LockingShift(0), SO (0x0E) calls LockingShift(1) in OutputStateMachineEngine.cpp:74-78 |
| xterm | ✓ | Yes | Implemented in charproc.c:3768-3780. SO/SI switch curgl between G0/G1 |
| xterm.js | ✓ | 2.3.0 | |
| Multiplexers | |||
| cy | ✗ | No | Not handled in Execute function |
| GNU Screen | ✓ | v.4.2.0 | SO/SI (Shift Out/In) implemented in Special() at src/ansi.c:697-702 |
| tmux | ✓ | 0.8 | Charset shift implementation for G0/G1 ACS |
| tuios | ✓ | v0.0.15 | |
| Zellij | ✓ | v0.7.0 | |
See also
- SS2/SS3 — Single Shifts — Temporarily invoke G2/G3 for one character
- Locking Shifts — LS2, LS3, and right-hand locking shifts