# `Drafter.Terminal.Probe`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/terminal/probe.ex#L1)

Asks the connected terminal which graphics protocol it supports.

The terminal is asked rather than guessed at from environment variables, so the
answer describes the terminal a session is actually attached to. That matters
for a session served over ssh or telnet, where the host's own environment says
nothing about the client, and locally under a multiplexer, which strips the
variables a guess would rely on.

`run/3` performs the whole exchange: it writes `FrenchCurve.Capability.probe/0`,
reads until the device attributes reply arrives or the deadline passes, and
returns the protocol together with any bytes that were not part of the answer.

    {protocol, leftover} = Drafter.Terminal.Probe.run(write, read)

The two functions are how transports differ. `write` takes iodata and puts it on
the wire. `read` takes a millisecond timeout and returns `{:ok, bytes}`,
`:timeout` when nothing arrived in time, or `{:error, reason}`. A `read` must
wait up to the timeout it is given before reporting `:timeout`, because one
silent slice ends the exchange: a terminal that understands the queries answers
them at once.

Run it before the input pipeline starts. The replies are control sequences that
would otherwise be delivered as keystrokes, so they are consumed here; anything
the user typed during the exchange comes back as `leftover` and belongs to the
input pipeline.

# `protocol`

```elixir
@type protocol() :: :kitty | :iterm2 | :sixel | nil
```

# `read_fun`

```elixir
@type read_fun() :: (non_neg_integer() -&gt;
                 {:ok, binary()} | :timeout | {:error, term()})
```

# `write_fun`

```elixir
@type write_fun() :: (iodata() -&gt; any())
```

# `query`

```elixir
@spec query() :: binary()
```

The bytes to write to ask the terminal what it supports.

For a transport that cannot read synchronously: write this, accumulate whatever
arrives, and use `settled?/1` and `resolve/1` on the accumulated bytes.

# `resolve`

```elixir
@spec resolve(binary()) :: {protocol(), binary()}
```

Turn collected `replies` into `{protocol, leftover}`.

`leftover` is `replies` with the answers removed — bytes that arrived during the
exchange without being part of it, which belong to the input pipeline.

# `run`

```elixir
@spec run(write_fun(), read_fun(), keyword()) :: {protocol(), binary()}
```

Ask the terminal, and return `{protocol, leftover}`.

`protocol` is `:kitty`, `:iterm2`, `:sixel`, or `nil` when the terminal named
nothing usable or did not answer at all. `leftover` is the bytes that arrived
during the exchange without being part of it.

## Options

  * `:timeout` - milliseconds to wait for the answer in total. Default `200`. A
    terminal that supports the queries answers immediately; the deadline only
    bounds one that ignores them.

# `settled?`

```elixir
@spec settled?(binary()) :: boolean()
```

Whether `replies` holds the whole answer, so there is no point reading further.

A transport collecting asynchronously must still give up on a deadline: a
terminal that understands neither query never answers, and this never becomes
true for it.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
