diff options
Diffstat (limited to 'docs/notes/avalonia-gotchas.md')
| -rw-r--r-- | docs/notes/avalonia-gotchas.md | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/docs/notes/avalonia-gotchas.md b/docs/notes/avalonia-gotchas.md new file mode 100644 index 0000000..5150505 --- /dev/null +++ b/docs/notes/avalonia-gotchas.md @@ -0,0 +1,53 @@ +# Avalonia / Desktop gotchas + +Each one cost debugging time once. The code has comments at each site; +this file is the index. + +- **The GL view isn't hit-testable by default.** `OpenGlControlBase` draws + through GL, not Avalonia's scene. Pointer events and `Cursor` go to the + panel beneath unless the control implements `ICustomHitTest` + (`GameView.HitTest`). Display-only overlays need `IsHitTestVisible = + false` so the pointer reaches the game. +- **Deactivation ≠ lost focus.** Clicking another app deactivates the window + but keeps logical focus (`IsFocused` stays true), so `OnLostFocus` never + fires. Release held keys on `Window.Deactivated` too, because `OnKeyUp` + never arrives for a key released elsewhere. +- **The sim only advances while frames render.** `GameLoop.Frame` runs + inside `OnOpenGlRender`. Stopping `RequestNextFrameRendering` (paused) + stops the sim, and "keep simulating while minimized" would need a timer. +- **The render callback runs on the UI thread** (verified on this Linux/X11 + setup). The overlay and `FrameTimeStats` share state without locks for + that reason. Re-check if a platform moves rendering off-thread. +- **No pointer lock API** in Avalonia 12.1.2. Free look reads deltas between + pointer positions and stalls at the window edge. Recentering uses a + platform call (`CursorWarp`, X11 `XWarpPointer`; off where it fails). +- **Escape vs focused controls.** A clicked `CheckBox` takes keyboard focus, + so `Esc` for the options menu is a window-level *tunnel* handler, not + `GameView.OnKeyDown`. +- **Tests: float vs double.** `FixedStepClock.TickSeconds` is `float`. Compare + it against double arithmetic with a tolerance (~1e-6), not 9 places. +- **A clicked in-game `Button` steals the game's keys.** It takes focus, so + WASD stops reaching `GameView` and Space presses the button. HUD buttons + are `Focusable = false` (`Ui.Action`). +- **Raising `Button.ClickEvent` doesn't toggle a `CheckBox`.** Toggle logic + lives in `ToggleButton.OnClick`; scripted/pad presses use `Ui.Press`. +- **`KeyBindings` inside a control resolves to Avalonia's + `InputElement.KeyBindings`**, not ours — qualify the game's own `KeyBindings` type fully. +- **Invisible controls aren't templated.** `IsVisible = false` skips + measure/template, so the first real open pays for it (first NPC dialog + ~170 ms). `Ui.WarmUp()` shows a zero-opacity card briefly instead. +- **Routed key events only reach controls on the route.** A `KeyDown` raised + on the focused `GameView` tunnels window → `GameScreen` → `GameView`; a + sibling panel (options) never sees it. Screen-wide key handling lives on + `GameScreen` (e.g. key-rebind capture). +- **Scripted input vs X injection.** On KDE Wayland `xdotool` keys don't reach + a focused XWayland window (and may type into the user's terminal); drive + smoke runs with `smoke_run.sh --input FILE` (routed events in-process). +- **The HUD is redrawn every frame over the GL view.** The GL control changes every frame + and covers the window, so the compositor re-draws every overlay each frame — text is + the expensive part. `CacheMode = new BitmapCache()` on HUD cards (`Ui.Card`) makes them + re-render only on change; set `RenderAtScale` to any scale transform above them or they + blur. Merging text blocks into fewer visuals doesn't help. +- **Don't judge perf by CPU % on this machine** (±50 % with other load). Use the `instr:` + smoke step, interleaved runs, `PauseWhenInactive` off; see game-loop spec §7. + |
