Ghostty.Terminal (Ghostty v0.4.6)

Copy Markdown View Source

A managed terminal emulator backed by libghostty-vt.

Each terminal is a GenServer that owns a libghostty-vt terminal instance. All operations are serialized through the GenServer to ensure thread safety (libghostty-vt terminal instances are not thread-safe).

Examples

{:ok, term} = Ghostty.Terminal.start_link(cols: 120, rows: 40)
Ghostty.Terminal.write(term, File.read!("recording.vt"))
{:ok, html} = Ghostty.Terminal.snapshot(term, :html)

Supervision

children = [
  {Ghostty.Terminal, name: :main, cols: 80, rows: 24, max_scrollback: 50_000}
]

Effects

Terminal programs can trigger side effects via VT sequences (query responses, bell, title changes). These are forwarded as messages to the process that called start_link/1:

  • {:pty_write, binary} — query responses to write back to the PTY
  • :bell — BEL character
  • :title_changed — title change via OSC 2

Snapshot formats

  • :plain — plain text with all styling stripped
  • :html — HTML with inline styles preserving all colors and attributes
  • :vt — raw VT escape sequences (round-trippable)

Summary

Functions

Returns the terminal screen as a grid of cells.

Returns a child spec for use in supervision trees.

Returns the current cursor position as {col, row} (0-indexed).

Returns the current render-state cursor metadata for the visible viewport.

Encodes a focus event into a terminal escape sequence.

Returns whether focus reporting (DEC mode 1004) is enabled.

Encodes a key event into a terminal escape sequence.

Encodes a mouse event into a terminal escape sequence.

Returns the current terminal mouse reporting mode state.

Returns the current visible render-state cells together with cursor metadata.

Performs a full terminal reset (RIS — Reset to Initial State).

Resizes the terminal. Text reflow is handled automatically.

Scrolls the terminal viewport.

Returns the scrollbar state for the terminal viewport.

Returns the current terminal dimensions as {cols, rows}.

Returns a snapshot of the terminal screen content.

Starts a terminal process linked to the caller.

Writes VT-encoded data to the terminal.

Types

cell()

@type cell() :: {binary(), rgb() | nil, rgb() | nil, non_neg_integer()}

cursor_state()

@type cursor_state() :: %{
  x: non_neg_integer() | nil,
  y: non_neg_integer() | nil,
  visible: boolean(),
  blinking: boolean(),
  style: cursor_style(),
  wide_tail: boolean(),
  color: rgb() | nil
}

cursor_style()

@type cursor_style() :: :bar | :block | :underline | :block_hollow

format()

@type format() :: :plain | :html | :vt

mouse_modes()

@type mouse_modes() :: %{
  tracking: boolean(),
  x10: boolean(),
  normal: boolean(),
  button: boolean(),
  any: boolean(),
  sgr: boolean()
}

option()

@type option() ::
  {:cols, pos_integer()}
  | {:rows, pos_integer()}
  | {:max_scrollback, non_neg_integer()}
  | {:name, GenServer.name()}

render_state()

@type render_state() :: %{
  cells: [[cell()]],
  cursor: cursor_state(),
  mouse: mouse_modes(),
  scrollbar: scrollbar(),
  focus_reporting: boolean()
}

rgb()

@type rgb() :: {byte(), byte(), byte()}

scrollbar()

@type scrollbar() :: %{
  total: non_neg_integer(),
  offset: non_neg_integer(),
  len: non_neg_integer()
}

Functions

cells(terminal)

@spec cells(GenServer.server()) :: [[cell()]]

Returns the terminal screen as a grid of cells.

Each cell is {grapheme, fg, bg, flags} where:

  • grapheme — UTF-8 binary (empty for blank cells)
  • fg / bg{r, g, b} tuples or nil
  • flags — bitmask (see Ghostty.Terminal.Cell for helpers)

Examples

rows = Ghostty.Terminal.cells(term)

for row <- rows, {char, fg, _bg, flags} <- row, char != "" do
  if Ghostty.Terminal.Cell.bold?({char, fg, nil, flags}), do: IO.write("*")
  IO.write(char)
end

child_spec(init_arg)

Returns a child spec for use in supervision trees.

cursor(terminal)

Returns the current cursor position as {col, row} (0-indexed).

cursor_state(terminal)

@spec cursor_state(GenServer.server()) :: cursor_state()

Returns the current render-state cursor metadata for the visible viewport.

encode_focus(gained?)

@spec encode_focus(boolean()) :: {:ok, binary()} | :none

Encodes a focus event into a terminal escape sequence.

Examples

{:ok, seq} = Ghostty.Terminal.encode_focus(true)   # => "\e[I"
{:ok, seq} = Ghostty.Terminal.encode_focus(false)  # => "\e[O"

focus_reporting?(terminal)

@spec focus_reporting?(GenServer.server()) :: boolean()

Returns whether focus reporting (DEC mode 1004) is enabled.

input_key(terminal, event)

@spec input_key(GenServer.server(), Ghostty.KeyEvent.t()) :: {:ok, binary()} | :none

Encodes a key event into a terminal escape sequence.

Returns {:ok, sequence} with the bytes to send to the PTY, or :none if the key event produces no output.

Examples

Ghostty.Terminal.input_key(term, %Ghostty.KeyEvent{key: :enter})
# => {:ok, "\r"}

Ghostty.Terminal.input_key(term, %Ghostty.KeyEvent{key: :c, mods: [:ctrl]})
# => {:ok, <<3>>}

input_mouse(terminal, event)

@spec input_mouse(GenServer.server(), Ghostty.MouseEvent.t()) ::
  {:ok, binary()} | :none

Encodes a mouse event into a terminal escape sequence.

Returns {:ok, sequence} or :none if mouse tracking is not enabled by the running program.

mouse_modes(terminal)

@spec mouse_modes(GenServer.server()) :: mouse_modes()

Returns the current terminal mouse reporting mode state.

render_state(terminal)

@spec render_state(GenServer.server()) :: render_state()

Returns the current visible render-state cells together with cursor metadata.

reset(terminal)

@spec reset(GenServer.server()) :: :ok

Performs a full terminal reset (RIS — Reset to Initial State).

resize(terminal, cols, rows)

@spec resize(GenServer.server(), pos_integer(), pos_integer()) :: :ok

Resizes the terminal. Text reflow is handled automatically.

Examples

Ghostty.Terminal.resize(term, 120, 40)

scroll(terminal, delta)

@spec scroll(GenServer.server(), integer()) :: :ok

Scrolls the terminal viewport.

Positive delta scrolls down (towards newer content), negative scrolls up (towards scrollback history).

scrollbar(terminal)

@spec scrollbar(GenServer.server()) :: scrollbar()

Returns the scrollbar state for the terminal viewport.

size(terminal)

@spec size(GenServer.server()) :: {pos_integer(), pos_integer()}

Returns the current terminal dimensions as {cols, rows}.

snapshot(terminal, format \\ :plain)

@spec snapshot(GenServer.server(), format()) :: {:ok, binary()}

Returns a snapshot of the terminal screen content.

Formats

  • :plain — plain text, stripped of all styling (default)
  • :html — HTML with inline styles preserving colors and attributes
  • :vt — raw VT escape sequences

Examples

{:ok, text} = Ghostty.Terminal.snapshot(term)
{:ok, html} = Ghostty.Terminal.snapshot(term, :html)
{:ok, vt}   = Ghostty.Terminal.snapshot(term, :vt)

start_link(opts \\ [])

@spec start_link([option()]) :: GenServer.on_start()

Starts a terminal process linked to the caller.

Effect messages (:bell, :title_changed, {:pty_write, data}) are sent to the calling process.

Options

  • :cols - number of columns (default: 80)
  • :rows - number of rows (default: 24)
  • :max_scrollback - maximum scrollback lines (default: 10_000)
  • :name - GenServer name registration

write(terminal, data)

@spec write(GenServer.server(), iodata()) :: :ok

Writes VT-encoded data to the terminal.

The terminal's VT parser processes escape sequences and updates the internal screen, cursor, and style state. Accepts iodata.

Examples

Ghostty.Terminal.write(term, "hello world")
Ghostty.Terminal.write(term, "\e[31mred\e[0m")
Ghostty.Terminal.write(term, ["line 1\r\n", "line 2\r\n"])