# `Rho.Dyn.Screens`
[🔗](https://github.com/GenericJam/rho/blob/v0.1.0/lib/rho/dyn/screens.ex#L1)

How Dyn screens plug into the host's UI: a host seam (`config :rho, :dyn,
screens: Module`, `Rho.Dyn.Config`). rho builds, checks, selftests and
watches Dyn screens the same way whatever the UI; this says what a
screen is and how the UI tells the Keeper a launch got going:

  * **compiling**: which module bodies are screens (`c:screen_source?/1`)
    and the function each screen process enters through (`c:entry/0`): the
    compiler wraps it to register the process with the Keeper first
    (`Rho.Dyn.Keeper.watch/3`), so the screen's crash counts against its
    generation;
  * **selftest**: which loaded modules are screens (`c:screen?/1`, filed
    as `:screen` in the registry, `Rho.Dyn.screens/1`) and how one proves
    it mounts and renders (`c:selftest/1`, run in the contained selftest
    process);
  * **launches**: `c:after_first_render/1` has the UI call an MFA (with the
    screen appended) after its first frame, which starts the clock to
    "stable" (`Rho.Dyn.boot/1`); `c:subscribe_app_events/0` and
    `c:left_app?/1`: the app lifecycle events the Keeper receives, and
    which of them mean the user left the app (a launch that drew its
    first frame and was left is stable, not failed);
  * **the agent's view**: `c:live/1`, is generation `n` on screen yet (so
    `dyn_propose` with approve-all answers once it is); `c:guide/0`, the
    screen rules for `Rho.Dyn.agent_guide/0`.

`Rho.Dyn.Screens.None` (the default) has no screens; `Rho.Dyn.Screens.Mob`
is mob's (`use Mob.Screen`, `mount/3` + `render/1`).

# `after_first_render`

```elixir
@callback after_first_render(mfa :: {module(), atom(), list()}) :: :ok
```

Has the UI apply `mfa` (with the screen module appended) after its first frame.

# `entry`

```elixir
@callback entry() :: {atom(), arity()}
```

The function a screen process enters through, `{name, arity}` (mob: `{:mount, 3}`).

# `guide`

```elixir
@callback guide() :: String.t() | nil
```

The screen rules for the agent (`Rho.Dyn.agent_guide/0`), or nil.

# `left_app?`

```elixir
@callback left_app?(message :: term()) :: boolean()
```

Does `message` (one the Keeper received) mean the user left the app?

# `live`
*optional* 

```elixir
@callback live(n :: pos_integer()) :: :live | :waiting
```

Generation `n` is current: does the UI show it yet (`:live`), or not yet
(`:waiting`)? Without this callback, being current is enough (`:active`).

# `screen?`

```elixir
@callback screen?(module()) :: boolean()
```

Is loaded module `module` a screen?

# `screen_source?`

```elixir
@callback screen_source?(forms :: [Macro.t()]) :: boolean()
```

Do a `defmodule`'s body forms define a screen (its entry gets wrapped)?

# `selftest`

```elixir
@callback selftest(module()) :: {:ok, String.t()} | {:error, String.t()}
```

Proves screen `module` works (mounts and renders), in the selftest's
contained process: `{:ok, detail}` or `{:error, why}`. A raise is a failure.

# `subscribe_app_events`

```elixir
@callback subscribe_app_events() :: :ok
```

Subscribes the calling process (the Keeper) to the app's lifecycle events.

---

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