TermUI.Terminal.SizeDetector (TermUI v1.0.0)

View Source

Terminal size detection utilities.

This module provides centralized terminal size detection that can be used by both the Terminal module and backend implementations. It attempts multiple methods in order of reliability:

  1. Erlang :io module (most reliable when available)
  2. LINES/COLUMNS environment variables
  3. stty size command (last resort)

All methods validate dimensions against practical bounds to prevent resource exhaustion from malicious input.

Size Format

All functions return size as {rows, cols} (height, width) to match standard terminal conventions where rows come first.

Example

iex> SizeDetector.detect()
{:ok, {24, 80}}

iex> SizeDetector.detect(size: {40, 120})
{:ok, {40, 120}}

Bounds Checking

Detected sizes are validated against max_dimension/0 (9999) to prevent integer overflow or resource exhaustion attacks through environment variables or malicious terminal responses.

Summary

Functions

Auto-detects terminal size using all available methods.

Detects terminal size, optionally accepting an explicit size.

Detects terminal size from LINES and COLUMNS environment variables.

Detects terminal size from Erlang's :io module.

Detects terminal size from the stty size command.

Returns the maximum valid terminal dimension.

Validates that the given dimensions are within practical bounds.

Functions

auto_detect()

@spec auto_detect() ::
  {:ok, {pos_integer(), pos_integer()}} | {:error, :size_detection_failed}

Auto-detects terminal size using all available methods.

Tries methods in order:

  1. :io.rows/0 and :io.columns/0
  2. LINES and COLUMNS environment variables
  3. stty size command

Returns

  • {:ok, {rows, cols}} - Successfully detected size
  • {:error, :size_detection_failed} - All methods failed

detect(opts \\ [])

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

Detects terminal size, optionally accepting an explicit size.

When an explicit size tuple is provided, it's validated and returned. Otherwise, auto-detection is attempted.

Options

  • :size - Explicit {rows, cols} tuple to use instead of detection

Returns

  • {:ok, {rows, cols}} - Successfully detected or validated size
  • {:error, reason} - Failed to detect size

Examples

# Auto-detect
{:ok, {24, 80}} = SizeDetector.detect()

# Use explicit size
{:ok, {40, 120}} = SizeDetector.detect(size: {40, 120})

detect_from_env()

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

Detects terminal size from LINES and COLUMNS environment variables.

These are standard environment variables set by many shells and terminal emulators. Values are validated against practical bounds.

detect_from_io()

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

Detects terminal size from Erlang's :io module.

Uses :io.rows/0 and :io.columns/0 which query the terminal directly. This is the most reliable method when running in a real terminal.

detect_from_stty()

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

Detects terminal size from the stty size command.

This is a fallback method that works on most Unix-like systems. It spawns a subprocess to run stty size with safety protections.

max_dimension()

@spec max_dimension() :: pos_integer()

Returns the maximum valid terminal dimension.

validate_size(rows, cols)

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

Validates that the given dimensions are within practical bounds.

Returns

  • {:ok, {rows, cols}} - Valid dimensions
  • {:error, :invalid_size} - Invalid dimensions