1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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.
|