TermUI.Input.TTY.Server (TermUI v1.0.0)

View Source

GenServer that manages IEx-compatible TTY input using a separate process.

This server spawns a separate process that continuously polls for input using :io.get_chars/2. This approach lets TUI applications read through the active IEx IO server without replacing its shell. Because the terminal stays in cooked mode, delivery may still be buffered until Enter.

This is a standalone alternative and is not used by TermUI.Runtime, which runs TermUI.Input.TTY.poll/2 in its own dedicated reader process.

Architecture

The server manages a spawned process that:

  1. Continuously polls with receive after 0 for non-blocking behavior
  2. Calls :io.get_chars("", 1) to read single characters
  3. Parses escape sequences and converts charlists to binaries
  4. Sends parsed key events as messages to the server

The server maintains:

  • A queue of parsed events waiting to be delivered
  • The original IO options (for restoration on shutdown)
  • The spawned input process PID

Usage

{:ok, server} = TermUI.Input.TTY.Server.start_link(receiver: self())
{:ok, event} = TermUI.Input.TTY.Server.poll(server)
:ok = TermUI.Input.TTY.Server.stop(server)

IO Server Configuration

The server configures the IO server on startup:

On termination, it restores the original options.

Summary

Functions

Returns a specification to start this module under a supervisor.

Polls for a key event.

Returns the current event queue size.

Starts the TTY input server.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

poll(server, timeout \\ 100)

Polls for a key event.

Returns the next queued event. The timeout argument is retained for API compatibility but is not used to wait; callers receive {:error, :no_event} immediately while the reader is alive and the queue is empty.

Returns

  • {:ok, event} - A key event was received
  • {:error, :eof} - End of input stream
  • {:error, :no_event} - Reader is alive but no event is queued

Examples

case TermUI.Input.TTY.Server.poll(server) do
  {:ok, %Event.Key{} = event} -> handle_key(event)
  {:error, :no_event} -> try_again_later()
  {:error, :eof} -> handle_shutdown()
end

queue_size(server)

Returns the current event queue size.

start_link(opts \\ [])

Starts the TTY input server.

Options

  • :receiver - PID to receive {:input_event, event} messages. Supply this when using push delivery; omitting it is only safe if no input will arrive before shutdown.
  • :name - Name for GenServer registration (optional)

Examples

{:ok, server} = TermUI.Input.TTY.Server.start_link(receiver: some_pid)

stop(server, reason \\ :normal, timeout \\ 5000)

Stops the TTY input server.