# `Drafter.Runtime`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/runtime.ex#L1)

Behaviour for pluggable application runtime backends.

A *runtime backend* adapts an application style to Drafter's event loop: how an
app produces its initial state, and how it turns the loop's messages — input
events, named callbacks, timers, lifecycle hooks — into new state and effects.
The loop, renderer, transport and timer machinery are the same whichever backend
is in use.

An app selects one with `use Drafter.App, runtime: MyBackend`. Two ship with
Drafter: `Drafter.Runtime.Callback`, the default, taking `mount/1` and
`handle_event/2,3`; and `Drafter.Runtime.Reducer`, taking `init/1` and `update/2`.

`render/1` is not part of this behaviour. The renderer calls the app's `render/1`
directly, so every backend requires the app to define it.

# `app`

```elixir
@type app() :: module()
```

# `refresh_rate`

```elixir
@type refresh_rate() :: pos_integer() | String.t() | :unlimited | nil
```

A frame-pacing specification, as accepted by `Drafter.Runtime.FrameClock.interval_for/1`.

`nil` means "no preference"; the loop then falls back to its own default.

# `result`

```elixir
@type result() :: term()
```

What a backend returns from `c:handle_input/3` and `c:handle_message/4`.

`Drafter.EventResult.parse/2` turns any of these into `{state, actions, control}`,
so a backend may return a bare state, `{:ok, state}`, `{:ok, state, actions}`,
`{:noreply, state}`, `:handled`, `:unhandled`, or `{:stop, reason}`.

# `state`

```elixir
@type state() :: term()
```

# `handle_input`

```elixir
@callback handle_input(app(), term(), state()) :: result()
```

Handle a raw input/framework event tuple, returning the app's event result.

# `handle_message`

```elixir
@callback handle_message(app(), atom(), term(), state()) :: result()
```

Handle a named application message with its payload, returning the app's event result.

# `mount`

```elixir
@callback mount(app(), map()) :: state()
```

Produce the app's initial state from mount props (callback `mount`, reducer `init`).

# `on_message`

```elixir
@callback on_message(app(), term(), state()) :: state()
```

Handle an out-of-band process message delivered to the loop.

# `ready`

```elixir
@callback ready(app(), state()) :: state()
```

Run the post-mount ready hook, returning possibly-updated state.

# `refresh_rate`

```elixir
@callback refresh_rate(app()) :: refresh_rate()
```

The app's preferred frame pacing, or `nil` to let the loop choose.

The value is passed to `Drafter.Runtime.FrameClock.interval_for/1`, so it may be a
millisecond integer, an fps string such as `"30fps"`, `"unlimited"`, or `:unlimited`.

# `scroll_active`

```elixir
@callback scroll_active(app(), state()) :: state()
```

Hook invoked while scrolling is active (for scroll-driven state).

# `scroll_idle`

```elixir
@callback scroll_idle(app(), state()) :: state()
```

Hook invoked once scrolling settles.

# `timer`

```elixir
@callback timer(app(), term(), state()) :: state()
```

Handle a fired timer.

# `unmount`

```elixir
@callback unmount(app(), state()) :: :ok
```

Release what the app holds, once, as it stops for any reason, with its last state.

# `for_app`

```elixir
@spec for_app(app()) :: module()
```

Resolve the runtime backend module for an app module.

The first of these that is set wins: the calling process's
`:drafter_runtime_override` process-dictionary entry, which a shared session sets;
the app's own `__runtime__/0`, defined by `use Drafter.App, runtime: ...`; then
`Drafter.Runtime.Callback`. The result is passed through `normalize/1`, so
shorthand atoms are accepted in either place.

# `mount_props`

```elixir
@spec mount_props(keyword() | map()) :: map()
```

The mount props carried by `opts`, as a map.

Every entry point that starts an app — `Drafter.run/2`, a pushed nested session,
and `Drafter.run_session/3` behind the ssh and telnet transports — carries mount
props under the single `:props` key. `opts` is that keyword list, or a map, which
is returned as the props themselves.

Returns `%{}` when `:props` is absent. The surrounding options are never treated
as props.

## Examples

    iex> Drafter.Runtime.mount_props(props: %{user_id: 7})
    %{user_id: 7}

    iex> Drafter.Runtime.mount_props(refresh_rate: "60fps")
    %{}

    iex> Drafter.Runtime.mount_props(props: [a: 1, b: 2])
    %{a: 1, b: 2}

    iex> Drafter.Runtime.mount_props(%{already: :props})
    %{already: :props}

# `normalize`

```elixir
@spec normalize(atom()) :: module()
```

Normalize a backend shorthand or module to a backend module.

Recognised shorthands are `:callback`, `:reducer` and `:shared`. Any other atom is
returned unchanged, so a backend module may be given directly.

## Examples

    iex> Drafter.Runtime.normalize(:callback)
    Drafter.Runtime.Callback

    iex> Drafter.Runtime.normalize(:reducer)
    Drafter.Runtime.Reducer

    iex> Drafter.Runtime.normalize(:shared)
    Drafter.Runtime.Shared

    iex> Drafter.Runtime.normalize(Drafter.Runtime.Callback)
    Drafter.Runtime.Callback

---

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