fix #391: curated modern-only resolution list from the monitor's modes

User-directed (2026-08-13): "we should only support modern resolutions.
Not any old format." New DisplayModeCatalog enumerates the window's
monitor (Silk IMonitor.GetAllVideoModes) once at GameWindow load and
curates via a pure, tested rule: modern widescreen families only
(16:9/16:10/21:9/32:9 within 2.5%), at least 1280 wide, must fit the
desktop (an impossible windowed pick is not offered - the measured
3840x2160-on-2560x1440 silent clamp class), desktop mode always
included, refresh-rate duplicates collapsed, ascending order.

The Config Resolution row consumes the catalog through two new optional
Bind parameters; its Defaults value becomes the desktop's own mode.
Fixture/headless callers keep the static preset ladder, which now drops
800x600 and is pinned by test to pass the same curation rule (the OP6 S4
"default must be re-selectable" invariant holds on both paths).

Deliberate retail deviation, register row IA-22: retail listed the
adapter's complete enumeration including 4:3 legacy modes and authored
800x600 as the Config default (gmConfigUI::InitOptions
SetDefaultValue(0x03200258); gmClient::Init @0x004047af). The catalog is
also the designated fullscreen mode-switch validation source for
#376/#388 - an offered mode is supported by construction.

Tests: DisplayModeCatalogTests (8 - filter/clamp/dedupe/sort/ultrawide/
desktop-inclusion/fallback-consistency); ConfigOptionsPageControllerTests
row-12 default updated. App suite 4,961/3 skips; UI.Abstractions 916.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-13 17:16:09 +02:00
parent 7e0c130344
commit 13d388e5a9
9 changed files with 324 additions and 23 deletions

View file

@ -97,23 +97,22 @@ public sealed record DisplaySettings(
ParticleRange: ParticleRange.Extended);
/// <summary>
/// Resolution presets offered in the dropdown. <c>800x600</c> is retail's
/// OWN Config-tab default (OP6 rework, review S4) — a genuine legacy
/// display mode, not an invented entry: <c>gmClient::Init @0x004047af</c>
/// calls <c>Device::ForceDisplayResolution(1, 0x320, 0x258)</c> (0x320 =
/// 800, 0x258 = 600) at startup, and <c>gmConfigUI::InitOptions</c>'s own
/// <c>SetDefaultValue(0x03200258)</c> (byte-verified) names it as the
/// Resolution row's default. Without it in this list, clicking Defaults
/// resized the window correctly but left the dropdown showing an entry
/// that could never be re-selected — the same "opaque default" shape
/// LandscapeDrawDistance has for a genuinely different reason (AP-198's
/// sub-note); this one has a one-line fix instead of an opaque default,
/// so it gets the fix. The rest of the list is acdream's own modern
/// 16:9 preset ladder, not retail-authored.
/// FALLBACK resolution presets — used only when the monitor's real mode
/// list is unavailable (fixture/conformance callers, headless mounts). In
/// production the Config dropdown is populated from the display's actual
/// modes, curated to modern formats (#391, user-directed 2026-08-13:
/// "we should only support modern resolutions. Not any old format") —
/// see <c>AcDream.App.Rendering.DisplayModeCatalog</c>, whose curation
/// filter this fallback list also passes through. Retail's own list was
/// the adapter's full mode enumeration including 4:3 legacy modes, with
/// <c>800x600</c> as the authored Config-tab default
/// (<c>gmConfigUI::InitOptions SetDefaultValue(0x03200258)</c>,
/// <c>gmClient::Init @0x004047af</c>) — the curation and the desktop-mode
/// default that replaces it are a deliberate deviation carried in the
/// divergence register (see the #391 row).
/// </summary>
public static IReadOnlyList<string> AvailableResolutions { get; } = new[]
{
"800x600",
"1280x720",
"1366x768",
"1600x900",