Raxol.Terminal.TerminalUtils (Raxol Terminal v2.7.0)

Copy Markdown View Source

Utility functions for terminal operations, providing cross-platform and consistent handling of terminal capabilities and dimensions.

Summary

Functions

Whether this process still has a controlling terminal, cached per VM.

Detects terminal dimensions using a multi-layered approach

Detects terminal dimensions using :io.columns and :io.rows. Returns {:ok, width, height} or {:error, reason}.

Detects terminal dimensions using the stty size command. Returns {:ok, width, height} or {:error, reason}.

Detects terminal dimensions using the termbox2 NIF. Returns {:ok, width, height} or {:error, reason}.

Creates a bounds map with dimensions, starting at origin (0,0)

Gets terminal dimensions and returns them in a map format.

Checks whether a real terminal is available to drive.

Returns true if the current process is attached to a real TTY device.

Functions

controlling_terminal?()

@spec controlling_terminal?() :: boolean()

Whether this process still has a controlling terminal, cached per VM.

Cached because has_terminal_device?/0 is called per cursor move, and this branch opens a file. The answer cannot change during a run: a process does not acquire or lose its controlling terminal mid-flight.

detect_dimensions()

@spec detect_dimensions() :: {pos_integer(), pos_integer()}

Detects terminal dimensions using a multi-layered approach:

  1. Uses :io.columns and :io.rows (preferred)
  2. Falls back to termbox2 NIF if :io methods fail
  3. Falls back to stty size system command if needed
  4. Finally uses hardcoded default dimensions if all else fails

Returns a tuple of {width, height}.

detect_with_io(io_facade)

@spec detect_with_io(atom()) :: {:ok, pos_integer(), pos_integer()} | {:error, term()}

Detects terminal dimensions using :io.columns and :io.rows. Returns {:ok, width, height} or {:error, reason}.

detect_with_stty()

@spec detect_with_stty() :: {:ok, pos_integer(), pos_integer()} | {:error, term()}

Detects terminal dimensions using the stty size command. Returns {:ok, width, height} or {:error, reason}.

detect_with_termbox()

@spec detect_with_termbox() :: {:ok, pos_integer(), pos_integer()} | {:error, term()}

Detects terminal dimensions using the termbox2 NIF. Returns {:ok, width, height} or {:error, reason}.

get_bounds_map()

@spec get_bounds_map() :: %{x: 0, y: 0, width: pos_integer(), height: pos_integer()}

Creates a bounds map with dimensions, starting at origin (0,0)

get_dimensions_map()

@spec get_dimensions_map() :: %{width: pos_integer(), height: pos_integer()}

Gets terminal dimensions and returns them in a map format.

has_terminal_device?()

@spec has_terminal_device?() :: boolean()

Checks whether a real terminal is available to drive.

Unlike real_tty?/0 which uses Erlang's IO system (fails in -noshell mode), this checks at the OS level via prim_tty NIF. Use this for terminal initialization that needs to work with mix run (which sets -noshell).

Stdout answers this for an ordinary run. It is not the whole question, though, because stdout being a terminal and a terminal being available come apart whenever something interposes on the stream. Burrito's launcher does exactly that: it spawns the BEAM with stdout on a pipe so it can notice a downstream consumer going away, and a packaged raxol code then found isatty(:stdout) false on every run and skipped terminal setup entirely -- no raw mode, no alternate screen, so the TUI drew and ignored the keyboard.

Nothing in the driver actually needs stdout to be the terminal. Raw mode goes through /dev/tty (Raxol.Terminal.Driver.Stty), and the ANSI it writes reaches the terminal through whatever is relaying stdout. So fall back to asking whether this process still owns a controlling terminal.

real_tty?()

@spec real_tty?() :: boolean()

Returns true if the current process is attached to a real TTY device.

Uses Erlang's :io.columns/0 to detect whether the standard IO device supports terminal operations, which works reliably from within the BEAM (unlike shelling out to tty which doesn't inherit stdin).