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>
6 KiB
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 D4–D6 (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.
- 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.
- 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).
- F11 → Config → Field of View slider. It now means retail's gameFOV in degrees (10–160, default 90) and applies LIVE as you drag — the world widens/narrows immediately, no restart. Defaults restores 90.
- Set it back to 90 when done (or click Defaults).
§D2 — Curated resolution list (#391)
- 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.
- Pick a few. Every pick must land exactly (window resizes AND re-renders crisp — §the #387 chain), including 2560x1440.
- 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).
- At 2560x1440, drag several windows to the edges — chat to the bottom-right corner, the Options panel far right, a floating chat too.
- 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.)
- Drag-resize the window edge smaller (not via the dropdown). Windows must stay reachable continuously while you drag — the clamp runs live.
- 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)
- 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.
- 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@300→vulkan: swapchain recreated 1920x1080). - 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.
- 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)
- 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/refusedline and leaves you in a usable window instead. - 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.