TermUI.Backend.Selector (TermUI v1.0.0)
View SourceDetermines which terminal backend to use at runtime.
The Selector module implements a "try raw mode first" strategy for backend selection. After the platform/version guard, attempting acquisition is the authoritative check for whether native Raw mode is available.
Why Not Use Heuristics?
Environment-based detection (checking $TERM, current I/O device options, etc.) cannot
reliably detect all cases where raw mode is unavailable:
Nerves devices: The erlinit process may have already started a shell, making raw mode unavailable even though
$TERMsuggests a capable terminalSSH sessions: Remote SSH connections often have a shell already running in the PTY, preventing raw mode activation
Remote IEx: Connecting to a running node via
--remshor distributed Erlang inherits the remote node's terminal stateDocker containers: Terminal allocation varies by configuration; a TTY may be allocated but a shell may already be running
IDE terminals: Integrated terminals may report capabilities they don't fully support in raw mode
The Selection Strategy
The selector attempts to start raw mode using OTP 28's
:shell.start_interactive({:noshell, :raw}):
If raw mode succeeds (returns
:ok):- The terminal is now in raw mode
- Return
{:raw, state}for the Raw backend
If raw mode fails with
{:error, :already_started}:- A shell is already running, raw mode unavailable
- Detect terminal capabilities for graceful degradation
- Return
{:tty, capabilities}for the TTY backend
If the function is undefined (pre-OTP 28):
- Fall back to TTY mode
- Return
{:tty, capabilities}with detected capabilities
Return Values
The select/0 function returns one of:
{:raw, state}- Raw mode is active. Thestatemap contains::raw_mode_started-trueindicating raw mode was activated
{:tty, capabilities}- TTY mode should be used. Thecapabilitiesmap contains::colors- Color depth (:true_color,:color_256,:color_16,:monochrome):unicode- Boolean indicating Unicode support:dimensions-{rows, cols}tuple ornilif unknown:terminal- Boolean indicating terminal presence
Explicit Selection
For testing or configuration override, use select/1:
# Select a module explicitly (initialization is performed by Runtime)
{:explicit, TermUI.Backend.TTY, []} = Selector.select(TermUI.Backend.TTY)
{:explicit, TermUI.Backend.Raw, []} = Selector.select(TermUI.Backend.Raw)
# Auto-detect (same as select/0)
result = Selector.select(:auto)Examples
# Inspect the selected local mode. Runtime owns backend initialization.
case TermUI.Backend.Selector.select() do
{:raw, _state} -> :raw
{:tty, _capabilities} -> :tty
endOTP Version Requirements
- OTP 28+ on a supported Unix platform: Native Raw selection with
:shell.start_interactive/1 - OTP 27 and earlier: Automatic fallback to TTY mode
Summary
Types
Detected terminal capabilities for TTY mode.
Detected color depth for TTY mode.
State returned when raw mode is successfully activated.
Result of backend selection.
Functions
Selects the appropriate backend by attempting raw mode first.
Selects a backend with explicit mode or module specification.
Types
@type capabilities() :: %{ colors: color_depth(), unicode: boolean(), dimensions: {pos_integer(), pos_integer()} | nil, terminal: boolean() }
Detected terminal capabilities for TTY mode.
@type color_depth() :: :true_color | :color_256 | :color_16 | :monochrome
Detected color depth for TTY mode.
@type raw_state() :: %{raw_mode_started: boolean()}
State returned when raw mode is successfully activated.
@type selection_result() :: {:raw, raw_state()} | {:tty, capabilities()} | {:explicit, module(), keyword()}
Result of backend selection.
{:raw, state}- Raw mode active, use Raw backend{:tty, capabilities}- TTY mode, use TTY backend with capabilities{:explicit, module, opts}- Explicit backend selection (bypasses detection)
Functions
@spec select() :: {:raw, raw_state()} | {:tty, capabilities()}
Selects the appropriate backend by attempting raw mode first.
Returns {:raw, state} if raw mode succeeds, or {:tty, capabilities}
if raw mode is unavailable.
Examples
iex> case TermUI.Backend.Selector.select() do
...> {:raw, _state} -> :raw_mode
...> {:tty, _caps} -> :tty_mode
...> end
# Returns :raw_mode or :tty_mode depending on environment
@spec select(:auto | module() | {module(), keyword()}) :: selection_result()
Selects a backend with explicit mode or module specification.
Arguments
:auto- Same asselect/0, auto-detect backendmodule- Use specific backend module (e.g.,TermUI.Backend.TTY){module, opts}- Use specific backend with options
Examples
# Auto-detect
Selector.select(:auto)
# Force TTY mode
Selector.select(TermUI.Backend.TTY)
# Force with options
Selector.select({TermUI.Backend.TTY, line_mode: :full_redraw})