Raxol.Terminal.Capabilities.Probe (Raxol Terminal v2.6.1)

Copy Markdown View Source

Pure probe reducer -- the clock seam (04 design §1b).

step(state, event) where event is {:input, bytes}, {:clock, monotonic_ms}, or :start. No time is read inside step and no process sleeps anywhere: the silence timeout is driven entirely by injected clock events, so every timing case is a table row, not a flake. The real driver wraps this with a receive/after loop feeding System.monotonic_time(:millisecond) as {:clock, now} events.

Sentinel discipline (F0 §2): the batched query ends with Primary DA (CSI c). Every VT-class terminal answers DA1, so read-to-sentinel converts "did it ignore me?" into a bounded wait -- any wanted reply that did not arrive before the sentinel is unsupported. Silence past the deadline is the failure mode, answered with a conservative default.

Deadline policy (F0 §7 step 3): ~100 ms local, ~1 s when $SSH_* is present; extended AT MOST ONCE when a first byte is seen but the sentinel is not. After the sentinel, one drain window stays open until the next clock event so reordered replies (a documented terminal bug) are still parsed; after :done, late replies are drained, never reclassified, and never leaked as keystrokes.

Summary

Functions

Builds a probe from the env seed.

The exact batched query the probe emits on :start.

Probe outcome: :pending or {:done, %Capabilities{}}.

Advances the reducer by one event. Returns {state, actions}.

Types

action()

@type action() ::
  {:write, iodata()}
  | {:passthrough, iodata()}
  | {:extend_deadline, non_neg_integer()}
  | {:leak_free, binary()}
  | {:done, Raxol.Terminal.Capabilities.t()}

event()

@type event() :: :start | {:input, binary()} | {:clock, integer()}

phase()

@type phase() :: :init | :awaiting | :draining | :done

t()

@type t() :: %Raxol.Terminal.Capabilities.Probe{
  budget_ms: pos_integer(),
  caps: Raxol.Terminal.Capabilities.t() | nil,
  deadline: integer() | nil,
  env: map(),
  extend_ms: pos_integer(),
  extended?: boolean(),
  now0: integer(),
  opts: keyword(),
  phase: phase(),
  scanner: Raxol.Terminal.Capabilities.ReplyScanner.t(),
  seen_input?: boolean(),
  tty?: boolean()
}

Functions

new(env, opts \\ [])

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

Builds a probe from the env seed.

Options:

  • :budget_ms -- override the silence deadline (default ~100 ms local, ~1 s when any $SSH_* var is present)
  • :extend_ms -- the one-time extension budget (default = budget)
  • :now_ms -- clock origin the deadline is computed from (default 0; the driver passes System.monotonic_time(:millisecond))
  • :tty? -- false = non-TTY path: Core-minus, ZERO queries emitted
  • :tmux_passthrough? -- re-issue identity/color queries wrapped for the outer terminal (only meaningful when $TMUX is set)
  • :platform -- forwarded to the classifier (:windows gates)

query_sequence()

@spec query_sequence() :: binary()

The exact batched query the probe emits on :start.

result(probe)

@spec result(t()) :: :pending | {:done, Raxol.Terminal.Capabilities.t()}

Probe outcome: :pending or {:done, %Capabilities{}}.

step(p, arg2)

@spec step(t(), event()) :: {t(), [action()]}

Advances the reducer by one event. Returns {state, actions}.