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>
This commit is contained in:
Erik 2026-08-13 17:49:17 +02:00
parent 2153bee247
commit e56aa5115c
5 changed files with 451 additions and 27 deletions

View file

@ -67,14 +67,46 @@ saving anything until you save).
immediately under the current resolution; the automatic clamp/reload on
a resolution change never writes anything).
## §D4§D6 — Fullscreen family (#377/#376/#388) — PENDING SLICES
## §D4 — Fullscreen startup (#377)
*(To be filled in when the fullscreen slices land. Will include: fullscreen
on/off via the Config checkbox, resolution picks while fullscreen becoming
REAL display-mode switches — **[BLACK-SCREEN RISK]** steps will be flagged:
have Alt+F4 / the physical power of patience ready — and the #388 crash
scenario surviving: a fullscreen-state settings apply must never take the
client down.)*
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@300`
`vulkan: 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.
---