# 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.