acdream/docs/research/2026-08-13-display-block-test-script.md
Erik e56aa5115c fix #376+#388: real fullscreen mode switching, state-aware display apply
Slice 5+6 of the display block, one coherent unit (they share the state
machine the goal's dual review covers).

GlfwDisplayModeSwitcher (#376) ports retail's fullscreen semantics -
Device::ForceDisplayResolution @gmClient::Init 0x004047af is a REAL
video-mode change - through native glfwSetWindowMonitor on the same
IWindow.Native.Glfw handle path #348's cursor cache proved. Primary
monitor (retail's primary display device); refresh = the monitor's
highest for the picked WxH; the windowed placement is remembered for the
exit path; every failure is a no-throw (bool, reason) result.

SilkRuntimeDisplayWindowTarget.Apply (#388) becomes the state-aware
machine: fullscreen target = validated native mode switch (mode must be
in #391's DisplayModeCatalog - an offered mode is supported by
construction, making the "Graphics mode not supported" crash class
unreachable from the dropdown); windowed target while fullscreen = the
native exit (which sets the client size itself); plain windowed pick =
the proven #387 size write. A raw Size write NEVER happens against a
fullscreen window - on GLFW that is a video-mode request, and an
unsupported one was the exact unhandled-GlfwException that killed the
user's 2026-08-13 session. The old Silk borderless WindowState path is
deleted from the apply. New IWindowedSizeSurface narrows the window
dependency so the machine is unit-testable (FakePacingSurface idiom).

Live-verified on this machine (goal-sanctioned automated run):
display: fullscreen mode switch 1920x1080@300 -> framebuffer resize
event 1920x1080 -> vulkan: swapchain recreated 1920x1080 ok=True ->
graceful close, desktop mode restored.

Tests: 5 state-machine facts (validated switch/never-size-write,
unoffered refusal, failed-switch usability, native exit, plain windowed
write). App suite 4,972/3 skips. Gate script sections D4-D6 written
(black-screen-risk steps flagged). Dual Opus review of the pair follows
as its own round.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 17:49:17 +02:00

6 KiB
Raw Blame History

Display & resolution block — connected-gate test script

Status: slices D1 (#389 FOV) and D2 (#391 curated list) are LANDED and testable now; D3 (#390 UI reposition) and D4D6 (fullscreen family #377/#376/#388) fill in as they land. Launch with ACDREAM_RETAIL_UI=1 against local ACE, Release build for anything you judge by feel or frame rate. The resize/display chain logs evidence permanently — after any step you can cross-check display: resolution pick, window: framebuffer resize event, and vulkan: swapchain recreated lines in the launch log.


§D1 — Widescreen FOV feel (#389, SmartboxFOV)

The world camera now uses retail's law: your horizontal view stays roughly constant (~84° at 16:9) and wide screens trim the vertical slice instead of bloating the sides. Your stored Field of View migrates 60 → 90 (the retail default) on first load.

  1. Just look at the world on your 16:9 window at your usual spot (Holtburg outdoors is ideal). Compared to yesterday: the view should feel slightly more "zoomed"/framed — retail's framing — rather than the old wide-angle look. If you have retail side-by-side, the framing should now match it at the same window shape.
  2. Resize the window across shapes (drag it narrow, then wide). The world must stay geometrically true at every shape — no squish, no stretch, circles stay circles (pre-#389, mid-session resizes distorted the world in chase mode; that bug is dead).
  3. F11 → Config → Field of View slider. It now means retail's gameFOV in degrees (10160, default 90) and applies LIVE as you drag — the world widens/narrows immediately, no restart. Defaults restores 90.
  4. Set it back to 90 when done (or click Defaults).

§D2 — Curated resolution list (#391)

  1. F11 → Config → Resolution dropdown. The list is now your monitor's real modes, modern widescreen only: no 800x600, no 1024x768, nothing your 2560x1440 desktop can't host (no 3840x2160 entry). Expect roughly: 1280x720 / 1366x768 / 1600x900 / 1920x1080 / 2560x1440 — exactly what your monitor reports, one entry per size.
  2. Pick a few. Every pick must land exactly (window resizes AND re-renders crisp — §the #387 chain), including 2560x1440.
  3. Click Defaults. The Resolution row restores 2560x1440 (your desktop's own mode — the deliberate replacement for retail's 800x600 default; register row IA-22), and the dropdown shows it selected.

§D3 — UI windows stay reachable on downscale (#390)

Retail's rule, ported exactly: on a display change every floating window re-clamps to x = max(0, min(x, screenW windowW)) — top-left priority — and the per-resolution saved layout for the NEW size reloads (without saving anything until you save).

  1. At 2560x1440, drag several windows to the edges — chat to the bottom-right corner, the Options panel far right, a floating chat too.
  2. Pick 1280x720 in the Resolution dropdown. Every window must end up fully inside the new screen — anything that was past the new bounds slides in flush with the edge; nothing needs a window-resize to fetch back. (This includes floating chat windows — retail actually strands those; we clamp them by the block's own requirement, register AD-91.)
  3. Drag-resize the window edge smaller (not via the dropdown). Windows must stay reachable continuously while you drag — the clamp runs live.
  4. Per-resolution memory: at 1280x720 move a window somewhere distinctive, pick 2560x1440, then pick 1280x720 again — the window returns to where 1280x720 last had it (each resolution keeps its own layout, like retail's per-resolution auto layouts — YOUR drags save immediately under the current resolution; the automatic clamp/reload on a resolution change never writes anything).

§D4 — Fullscreen startup (#377)

  1. F11 → Config → tick Full Screen, then close the client and relaunch. The client must come up fullscreen at your saved resolution, no crash (the old startup access-violation is 3/3-clean on current code, plus one automated fullscreen boot verified live).

§D5 — Real display-mode switching (#376) [BLACK-SCREEN RISK]

Every step here changes your monitor's actual video mode — the panel will blank/re-sync for a moment each time. If a switch ever leaves the screen black for more than ~5 seconds, Alt+F4 closes the client gracefully and the desktop mode restores.

  1. Windowed at 1920x1080 → tick Full Screen. The display itself switches to 1920x1080 (at your monitor's highest refresh for that mode — this panel reports 300 Hz): everything gets larger-grained, the monitor re-syncs. This is retail's fullscreen, not a stretched borderless window. Automated evidence for this step already exists (display: fullscreen mode switch 1920x1080@300vulkan: swapchain recreated 1920x1080).
  2. While fullscreen, pick a different resolution (e.g. 2560x1440). The DISPLAY switches modes again — a real re-sync, crisp at the new mode, never a scaled image.
  3. Untick Full Screen. The desktop returns to its native mode and the client becomes a normal window at your picked resolution, positioned where it was before entering fullscreen.

§D6 — No settings apply may crash the client (#388)

  1. Repeat your original crash recipe: while fullscreen, change resolution a few times quickly, toggle Full Screen off and on, drag sliders in between. The client must never die — any refused/failed switch logs a display: ... failed/refused line and leaves you in a usable window instead.
  2. The old crash's exact mechanism (a window-resize applied to a fullscreen window becoming an unsupported video-mode request) is now structurally impossible: fullscreen applies only validated modes from the dropdown's own list, and size writes never touch a fullscreen window.

Carried context for the gate: the FPS cost of large windows is real physics (more pixels = more GPU work) — judge frame rate in Release only. The title bar shows live FPS.