TermUI.Backend.State (TermUI v1.0.0)

View Source

Shared state structure for terminal backends.

The State module provides a consistent wrapper around backend-specific state, enabling uniform state management across different backend implementations (Raw and TTY modes).

Purpose

This optional helper can wrap backend selection data for callers that want a uniform value. TermUI.Runtime stores backend fields directly in TermUI.Runtime.State and does not construct this struct. The helper provides:

  • Consistent interface: All backends expose the same state structure
  • Mode tracking: Easy identification of current terminal mode
  • Capability access: Unified access to detected terminal capabilities
  • Size caching: Cached terminal dimensions to avoid repeated queries
  • Lifecycle tracking: Initialization status for proper cleanup

Usage

A custom integration can create a state struct after backend selection:

case Selector.select() do
  {:raw, raw_state} ->
    %State{
      backend_module: TermUI.Backend.Raw,
      backend_state: raw_state,
      backend_mode: :raw,
      capabilities: %{},
      initialized: false
    }

  {:tty, capabilities} ->
    %State{
      backend_module: TermUI.Backend.TTY,
      backend_state: nil,
      backend_mode: :tty,
      capabilities: capabilities,
      initialized: false
    }
end

Fields

  • :backend_module - The backend implementation module (required)
  • :backend_state - Backend-specific internal state
  • :backend_mode - Current terminal mode, :raw or :tty (required)
  • :capabilities - Map of detected terminal capabilities
  • :size - Cached terminal dimensions as {rows, cols} or nil
  • :initialized - Whether the backend has been fully initialized

Naming Convention

This field is named :backend_mode (not :mode) to be consistent with Runtime.State.backend_mode and to avoid confusion with other mode fields throughout the codebase (e.g., line_mode, mouse_mode, color_mode).

Constructors

Instead of creating structs directly, use the constructor functions:

# General constructor with explicit backend module
State.new(MyBackend, backend_mode: :tty, capabilities: %{colors: :true_color})

# Convenience constructor for raw mode
State.new_raw()
State.new_raw(%{raw_mode_started: true})

# Convenience constructor for TTY mode
State.new_tty(%{colors: :color_256, unicode: true})

State Updates

State structs are immutable. Use update functions for convenience:

state = State.new_tty(%{colors: :true_color})
state = State.put_size(state, {24, 80})
state = State.mark_initialized(state)

Summary

Types

Terminal mode indicating which backend type is active.

Cached terminal dimensions as {rows, cols}.

t()

The backend state struct.

Functions

Marks the state as initialized.

Creates a new backend state with the given module and options.

Creates a new raw mode backend state.

Creates a new TTY mode backend state with the given capabilities.

Updates the backend-specific state.

Updates the capabilities map.

Updates the cached terminal dimensions.

Types

backend_mode()

@type backend_mode() :: :raw | :tty

Terminal mode indicating which backend type is active.

dimensions()

@type dimensions() :: {pos_integer(), pos_integer()} | nil

Cached terminal dimensions as {rows, cols}.

t()

@type t() :: %TermUI.Backend.State{
  backend_mode: backend_mode(),
  backend_module: module(),
  backend_state: term(),
  capabilities: map(),
  initialized: boolean(),
  size: dimensions()
}

The backend state struct.

Contains all metadata needed to manage a terminal backend instance.

Functions

mark_initialized(state)

@spec mark_initialized(t()) :: t()

Marks the state as initialized.

This function is idempotent - calling it on an already initialized state has no effect.

Arguments

  • state - The current state struct

Examples

iex> state = State.new_tty(%{})
iex> state.initialized
false
iex> state = State.mark_initialized(state)
iex> state.initialized
true

new(backend_module, opts \\ [])

@spec new(
  module(),
  keyword()
) :: t()

Creates a new backend state with the given module and options.

Arguments

  • backend_module - The backend implementation module
  • opts - Keyword list of options:
    • :backend_mode - Required. The terminal mode (:raw or :tty)
    • :backend_state - Optional. Backend-specific internal state
    • :capabilities - Optional. Map of terminal capabilities (default: %{})
    • :size - Optional. Cached dimensions as {rows, cols} (default: nil)
    • :initialized - Optional. Initialization status (default: false)

Examples

iex> State.new(MyBackend, backend_mode: :tty)
%State{backend_module: MyBackend, backend_mode: :tty, ...}

iex> State.new(MyBackend, backend_mode: :tty, capabilities: %{colors: :true_color})
%State{backend_module: MyBackend, backend_mode: :tty, capabilities: %{colors: :true_color}, ...}

Raises

new_raw(backend_state \\ nil)

@spec new_raw(term()) :: t()

Creates a new raw mode backend state.

This is a convenience function that sets:

Arguments

  • backend_state - Optional. Backend-specific internal state (default: nil)

Examples

iex> State.new_raw()
%State{backend_module: TermUI.Backend.Raw, backend_mode: :raw, ...}

iex> State.new_raw(%{raw_mode_started: true})
%State{backend_module: TermUI.Backend.Raw, backend_mode: :raw, backend_state: %{raw_mode_started: true}, ...}

new_tty(capabilities, backend_state \\ nil)

@spec new_tty(map(), term()) :: t()

Creates a new TTY mode backend state with the given capabilities.

This is a convenience function that sets:

Arguments

  • capabilities - Map of detected terminal capabilities
  • backend_state - Optional. Backend-specific internal state (default: nil)

Examples

iex> State.new_tty(%{colors: :color_256, unicode: true})
%State{backend_module: TermUI.Backend.TTY, backend_mode: :tty, capabilities: %{colors: :color_256, unicode: true}, ...}

iex> State.new_tty(%{colors: :true_color}, %{some: :state})
%State{backend_module: TermUI.Backend.TTY, backend_mode: :tty, capabilities: %{colors: :true_color}, backend_state: %{some: :state}, ...}

put_backend_state(state, backend_state)

@spec put_backend_state(t(), term()) :: t()

Updates the backend-specific state.

Arguments

  • state - The current state struct
  • backend_state - The new backend-specific state value

Examples

iex> state = State.new_raw()
iex> state = State.put_backend_state(state, %{cursor: {1, 1}})
iex> state.backend_state
%{cursor: {1, 1}}

put_capabilities(state, capabilities)

@spec put_capabilities(t(), map()) :: t()

Updates the capabilities map.

Note: This replaces the entire capabilities map, it does not merge.

Arguments

  • state - The current state struct
  • capabilities - The new capabilities map

Examples

iex> state = State.new_tty(%{colors: :basic})
iex> state = State.put_capabilities(state, %{colors: :true_color, unicode: true})
iex> state.capabilities
%{colors: :true_color, unicode: true}

put_size(state, size)

@spec put_size(t(), dimensions()) :: t()

Updates the cached terminal dimensions.

Arguments

  • state - The current state struct
  • size - The new size as {rows, cols} tuple or nil

Examples

iex> state = State.new_tty(%{})
iex> state = State.put_size(state, {24, 80})
iex> state.size
{24, 80}

iex> state = State.put_size(state, nil)
iex> state.size
nil