# The frame loop

> What to call each frame, in what order, and how to forward input.

Masque does not own a window or a loop. You call it from yours.

## Each frame

```rust
// 1. Pick up saved edits (with Masque::watch).
if ui.check_for_changes() {
    if let Err(err) = ui.reload() {
        eprintln!("reload failed: {err}");
    }
}

// 2. Re-run the document if its state changed, then reconcile.
let rerendered = ui.update()?;

// 3. Advance springs and exits. Cap dt so a stalled frame
//    doesn't skip a whole animation.
ui.get_ui_mut().tick_animations(dt.min(0.033));

// 4. Lay out at the window's logical size.
ui.get_ui_mut().layout(width, height);

// 5. Paint.
surface.draw(ui.get_ui_mut());

// 6. Flush unmount hooks queued by settled exits.
ui.process_drain_queues();
```

> Call `tick_animations` **every** frame, not only when `update` re-rendered. Animations are ticked independently of state, and skipping ticks freezes transitions in flight.

## Updating host state

```rust
ui.with_runtime_mut(|rt| {
    rt.inject_host_state_if_changed("player_count".into(), Value::Integer(4));
    rt.request_rerender();
});
```

`inject_host_state_if_changed` skips the re-render when the value is unchanged.

## Forwarding input

```rust
use masque::widget::event::Event;
use masque::widget::point::Point;

let event = Event::with_point("mouse_down".into(), Point::new(x, y));
ui.get_ui_mut().call_event(&event);
```

For drags and context menus, your input code owns the dead-zone logic and calls the dedicated `dispatch_drag_*` and `dispatch_contextmenu` methods.

## Asking what the UI wants

- `wants_cursor_free()`: the UI is showing something that needs the pointer, such as a menu.
- `consumes_character_key()`: a text field has focus, so don't treat letters as shortcuts.
- `has_input_blocking_portal()`: a modal is open.

TODO: a complete winit + Skia host, kept compiling in CI.
