# Migrate from Emerge 0.4.2 to 0.5.0

The main change is configuration: return an `Emerge.Config` struct instead of a
renderer keyword list. UI construction, element events and viewport state work
as before. Raw input callbacks are now opt-in and use clearer event names.

## 1. Update the dependency

In `mix.exs`:

```elixir
{:emerge, "~> 0.5.0"}
```

Then run:

```sh
mix deps.update emerge
```

Emerge fetches the matching precompiled renderer for supported targets. There is
no new source-build step for a normal upgrade. Existing compile-time backend
selection stays unchanged.

## 2. Return a config from mount

**0.4.2:**

```elixir
@impl Viewport
def mount(opts) do
  {:ok, %{count: 0}, Keyword.merge([title: "Counter", width: 800, height: 600], opts)}
end
```

**0.5.0:**

```elixir
@impl Viewport
def mount(opts) do
  output = %Emerge.Config.Window{
    title: Keyword.get(opts, :title, "Counter"),
    size: Keyword.get(opts, :size, {800, 600})
  }

  {:ok, %{count: 0}, %Emerge.Config{output: output}}
end
```

The state and `render/1` do not change. A stateless viewport returns
`{:ok, %Emerge.Config{}}` and still uses `render/0`.

Arguments to `start_link/1` are still keywords, but your mount callback decides
which ones to use. In this example, pass `size: {1024, 768}` instead of separate
`width` and `height` arguments. Process options such as `name:` stay unchanged.

### If you start EmergeSkia directly

Replace keyword startup with a config, and move `otp_app` into `assets`:

```elixir
# 0.4.2
EmergeSkia.start(otp_app: :my_app, title: "Preview", rendering_api: :raster)
```

```elixir
# 0.5.0
EmergeSkia.start(%Emerge.Config{
  output: %Emerge.Config.Window{title: "Preview"},
  renderer: %Emerge.Config.Renderer{api: :raster},
  assets: %Emerge.Config.Assets{otp_app: :my_app}
})
```

Viewports infer their asset application when possible. Standalone callers must
supply it. Old positional startup calls and keyword renderer configs are removed.

## 3. Move settings into their groups

The config has six groups. You only need to supply the ones you customize;
all others use defaults. Remove the old `emerge_skia:` and `viewport:` wrappers.

### Window and renderer

This is a typical replacement for a window's flat options:

```elixir
%Emerge.Config{
  output: %Emerge.Config.Window{title: "My App", size: {1024, 768}},
  renderer: %Emerge.Config.Renderer{api: :opengl}
}
```

- `backend: :wayland` or `:macos` becomes `Window.backend`; usually leave it automatic.
- `title` and `always_on_top` move into `Window`.
- `width` and `height` become the `size` tuple.
- `rendering_api` (or the old `backend_renderer` alias) becomes `Renderer.api`.
  Use `:opengl`, not the old `:gl` alias.
- Raster's nested `present` setting becomes `Renderer.raster_present`.
- `vulkan_drm_node` becomes `Renderer.device`.
- `renderer_cache` becomes `Renderer.cache`; its field names are unchanged.
- `headless.target_fps` becomes `Renderer.target_fps`.

See `Emerge.Config.Window` and `Emerge.Config.Renderer` for field descriptions.

### DRM display

Replace `backend: :drm`, `drm_card`, `drm_output` and `drm_mode` with a DRM record:

```elixir
%Emerge.Config{
  output: %Emerge.Config.DRM{card: "/dev/dri/card0", connector: "DSI-1"},
  renderer: %Emerge.Config.Renderer{api: :opengl}
}
```

`drm_mode` becomes `mode`. Old width/height preferences become
`preferred_size: {width, height}`; the selected display mode determines the actual
size. Display discovery still uses `EmergeSkia.drm_outputs(drm_card: path)`.
See `Emerge.Config.DRM` for display-off and brightness settings.

### Headless output

Replace the backend flag and nested headless options with a headless record:

```elixir
%Emerge.Config{
  output: %Emerge.Config.Headless{
    target: self(),
    size: {400, 300},
    frames: %Emerge.Config.BinaryFrames{pixel_format: :bw1, dither: true}
  },
  renderer: %Emerge.Config.Renderer{api: :raster},
  assets: %Emerge.Config.Assets{otp_app: :my_app}
}
```

`target` and `frame_message` keep their names. Binary `pixel_format`, `bw1_polarity`
and `dither` move into `BinaryFrames`. For PRIME output, use `PrimeFrames` instead;
`prime.drm_node` becomes `PrimeFrames.device`, while `max_in_flight` and
`on_backpressure` keep their names. The explicit frame struct replaces the old
`:binary`/`:prime` mode flag. Frame messages still contain `VideoInterop.Frame`.

### Assets

Move the root `otp_app` into `assets`. Other asset field names stay the same:

```elixir
%Emerge.Config{
  assets: %Emerge.Config.Assets{
    otp_app: :my_app,
    fonts: [%Emerge.Config.Font{family: "Inter", source: "fonts/Inter-Regular.ttf"}]
  }
}
```

Use `RuntimePaths` for `assets.runtime_paths` and `AssetCache` for `assets.cache`.
See [Use assets](../tutorials/use_assets.md) for the full examples.

### Input and cursor

- `viewport.input_mask` is replaced by `Input.events`; see the next step.
- `scroll_line_pixels` moves into `Input`.
- `hw_cursor` becomes `Input.cursor.hardware`.
- Cursor image overrides move from `drm_cursor` into `Input.cursor`, using
  `CursorIcon` records. `source` and `hotspot` keep their names.

### Diagnostics

Instead of individual logging booleans, choose categories in `Diagnostics.logs`:

- `input_log: true` → `:input`
- `render_log: true` → `:render`
- `close_signal_log: true` → `:close`
- `renderer_stats_log: true` → `:stats`
- `renderer_animation_log: true` → `:animation`

For example, to keep automatic performance summaries:

```elixir
%Emerge.Config{
  diagnostics: %Emerge.Config.Diagnostics{logs: [:stats]}
}
```

`stats: true` collects data for `EmergeSkia.stats/2` without printing summaries.
Use `logs: [:stats]` when you want printed summaries; specifying both is unnecessary.
macOS supports printed summaries, not the stats API. The old `stats` field moves
into `Diagnostics.stats`; `drm_force_gpu_finish` becomes `Diagnostics.force_gpu_finish`.

### Runtime

Most applications can leave this group at its defaults. If you customized it:

- `drm_startup_retries` → `Runtime.startup_retries`
- `drm_retry_interval_ms` → `Runtime.retry_interval_ms`
- `viewport.renderer_check_interval_ms` → `Runtime.health_check_interval_ms`
- `viewport.renderer_module` → `Runtime.adapter`
- `viewport.renderer_opts` → `Runtime.adapter_opts`

Custom adapter authors need to update to the new config contract. Ordinary
applications use the default adapter and need no adapter changes.

## 4. Update raw input callbacks, if you use them

Buttons, text inputs, scrolling, element key bindings and window close callbacks
continue to work without raw subscriptions. If you use `handle_input/2`, explicitly
choose its events; the new default is none:

```elixir
%Emerge.Config{
  input: %Emerge.Config.Input{events: [:key, :pointer_button, :resize]}
}
```

Use `Emerge.Config.Input.all_events/0` for all categories. Numeric masks and the
`:all`/`:none` shortcuts are no longer accepted.

Update callback patterns:

- Key/button actions `1` and `0` become `:press` and `:release`.
- `:cursor_pos` becomes `:pointer_move`; `:cursor_button` becomes `:pointer_button`.
- `:cursor_scroll` becomes `:pointer_scroll`; `:cursor_entered` becomes `:pointer_enter`.
- `:resized` becomes `:resize`; `:focused` becomes `:focus`.
- Subscribe to `:text` for committed text and IME updates.

For example, `{:key, {:escape, 1, modifiers}}` becomes
`{:key, {:escape, :press, modifiers}}`. See the
[input guide](../tutorials/handle_input.md) for complete callback values.
These changes apply to viewport callbacks, not standalone native event messages.

DRM touchscreen observers now subscribe to `:touch` and receive
`{:touch, {contact_id, :down | :move | :up | :cancel, {x, y}}}` instead of emulated
mouse-button callbacks. Each contact has an independent lifetime. Virtual keys
activate on that contact's release; cancellation never activates a key. This does
not change mouse observations or require raw subscriptions for ordinary UI input.

## Optional: change settings while running

Existing mount-only configuration needs no additional callback. To make a supported
setting follow application state, add `config(previous, state)` (or `config(previous)`
for a stateless viewport). Implement only one arity:

```elixir
@impl Viewport
def config(previous, state) do
  output = %{previous.output | title: state.title}
  %{previous | output: output}
end
```

Call `Viewport.rerender(state)` as usual after changing state. The callback receives
the previous config with all nested records expanded into structs. Update those
records to keep other settings; supplying a new record uses its defaults, not
values from the previous one. Lists also replace rather than append.

### Settings you can change while running

- Raw input subscriptions and the renderer health-check interval.
- Window titles on Wayland and macOS.
- Frame-rate limits on headless and Wayland outputs.
- DRM display on/off and brightness with explicit OpenGL, Vulkan or raster
  (CPU or GPU-upload presentation).

### Settings that need a new viewport

Output selection, size, renderer API/device, caches, cursor appearance, assets,
diagnostics and adapter settings cannot be changed by the callback.

### Backend limitations

Frame-rate limits are not available on DRM or macOS. DRM power/brightness controls
require an explicit OpenGL, Vulkan or raster renderer; automatic API selection
remains unsupported. Raster GPU upload requires an OpenGL-enabled DRM build. See
[configuration records](https://hexdocs.pm/emerge/Emerge.Config.html) and each config
module for the settings relevant to your output.
