# Windowing & the Application Loop

Stage 2 reference for `oxide_engine::window` — opening a window, running the
event loop, and receiving raw input. For what happens *inside* a frame (GPU
setup, clearing, resize handling) see [render-context.md](render-context.md).

## Overview

The window module wraps [`winit`](https://docs.rs/winit) so applications never
talk to the event loop directly. You implement the `WindowApp` trait, hand it
to `run()` together with a `WindowConfig`, and the engine:

1. creates the window and the GPU [`RenderContext`](render-context.md),
2. calls `WindowApp::init` once,
3. then loops: forwards every raw window event to `WindowApp::event`, calls
   `WindowApp::update` once per frame, and clears + presents the surface.

The loop runs in `Poll` mode (continuous rendering, as a game expects), not
event-driven `Wait` mode (as a desktop utility would use).

> **Stage 6 rename.** This trait was originally `App`. Stage 6 renamed it to
> `WindowApp` so the engine's [`oxide_engine::app::App`](modules.md) container
> (scene, assets, scheduled systems) could live in the prelude unambiguously.
> The two cover different roles: this trait is the per-frame window/event
> handler; the container is engine state your handler typically wraps around.

## Minimal application

```rust
use oxide_engine::prelude::*;
use oxide_engine::window::event::{Key, NamedKey, ElementState, WindowEvent};

#[derive(Default)]
struct MyApp;

impl WindowApp for MyApp {
    fn init(&mut self, ctx: &mut AppCtx<'_>) {
        ctx.set_clear_color(Color::rgb(0.39, 0.58, 0.93));
    }

    fn event(&mut self, ctx: &mut AppCtx<'_>, event: &WindowEvent) {
        if let WindowEvent::KeyboardInput { event: key, .. } = event {
            if key.state == ElementState::Pressed
                && key.logical_key == Key::Named(NamedKey::Escape)
            {
                ctx.request_exit();
            }
        }
    }

    fn update(&mut self, ctx: &mut AppCtx<'_>) {
        let _seconds_since_last_frame = ctx.dt;
    }
}

fn main() -> anyhow::Result<()> {
    run(WindowConfig::default(), MyApp)
}
```

`run()` blocks the calling thread until the app exits — an OS requirement (the
event loop must own the main thread), not an engine choice.

## `WindowConfig`

Initial window settings. All fields are plain data:

| Field | Default | Meaning |
|-------|---------|---------|
| `title` | `"Oxide"` | Window title |
| `width`, `height` | 1280 × 720 | Initial inner size, logical pixels |
| `resizable` | `true` | Whether the user can resize |
| `clear_color` | `Color::BLACK` | Initial per-frame clear color |

## The `WindowApp` trait

Three callbacks, all optional (empty default bodies):

- **`init(ctx)`** — once, after the window and GPU exist, before the first
  frame. Set the title, clear color, load resources.
- **`event(ctx, event)`** — for *every* raw `WindowEvent`, including ones the
  engine also reacts to (close request, resize), so apps can observe
  everything. Stage 2 exposes events untranslated; the
  [Stage-7 input system](input.md) layers per-key edge detection and
  remappable named actions on top, surfaced through `ctx.input()`.
- **`update(ctx)`** — once per frame, before the frame is cleared and
  presented. `ctx.dt` is the seconds elapsed since the previous frame (`0.0`
  on the first).

Per frame the order is: pending `event` calls → `update` → render.

## `AppCtx`

Every callback receives `&mut AppCtx`, the engine state an app may touch:

| Member | Purpose |
|--------|---------|
| `dt` | Frame delta time in seconds (field) |
| `set_clear_color(color)` / `clear_color()` | Per-frame clear color; changes apply on the next frame |
| `size()` | Current surface size in physical pixels |
| `set_title(title)` | Change the window title |
| `request_exit()` | Leave the event loop after the current callback |
| `render()` | Direct access to the [`RenderContext`](render-context.md) |
| `input()` | The per-frame [`InputState`](input.md) snapshot |

## Raw event types

`oxide_engine::window::event` re-exports the `winit` event vocabulary
(`WindowEvent`, `KeyEvent`, `MouseButton`, `ElementState`, `KeyCode`,
`PhysicalKey`, `Key`, `NamedKey`, `ModifiersState`, …) so applications don't
need their own `winit` dependency. The whole crates are also available as
`oxide_engine::winit` and `oxide_engine::wgpu` for anything not curated.

Two keyboard representations matter:

- `KeyEvent::physical_key` (`PhysicalKey::Code(KeyCode::KeyW)`) — the physical
  key position, layout-independent. Use for game-style controls.
- `KeyEvent::logical_key` (`Key::Named(NamedKey::Escape)` or
  `Key::Character(…)`) — what the key means under the user's layout. Use for
  shortcuts and text.

## Engine-handled events

The runner reacts to these before forwarding them:

| Event | Engine behavior |
|-------|-----------------|
| `CloseRequested` | Exits the loop (apps can't veto it in Stage 2) |
| `Resized` | Reconfigures the surface (see [render-context.md](render-context.md)) |
| `RedrawRequested` | Computes `dt`, calls `update`, renders the frame |

Errors during window/GPU creation or rendering are returned from `run()`;
winit callbacks can't propagate `Result`, so the runner stashes the first
error and exits the loop.

## Trying it

```sh
cargo run -p oxide-examples --bin hello_window
```

Keys `1`–`5` switch clear-color presets, `Space` cycles, `Esc` quits; average
FPS is logged once per second. The editor (`cargo run -p oxide-editor`) uses
the same infrastructure and quits with `Ctrl+Q`.
