using System; using System.Collections.Generic; using System.Linq; using Silk.NET.Windowing; namespace AcDream.App.Rendering; /// /// #391 (user-directed, 2026-08-13): the ONE source of the resolutions the /// client offers. Production enumerates the display's real mode list /// () and curates it to modern /// widescreen formats that fit the desktop; the Config dropdown, and later /// the fullscreen mode-switch validation (#376/#388), read the same catalog /// so an offered mode is by construction a supported one. /// /// Retail deviation, register-rowed with #391: retail listed the /// adapter's complete enumeration including 4:3 legacy modes and authored /// 800x600 as the Config default /// (gmConfigUI::InitOptions SetDefaultValue(0x03200258)). We curate /// deliberately — modern formats only — and the Defaults value becomes the /// desktop's own mode (always present in the curated list). /// /// Write-once static owner: the catalog is immutable hardware truth /// captured at startup on the windowing thread (the same shape as the /// platform facts GraphicalHostPlatformServices owns). Fixture, /// UI-Studio, and headless callers never install one and fall back to /// DisplaySettings.AvailableResolutions at the consuming seam. /// internal static class DisplayModeCatalog { private static IReadOnlyList? _resolutions; private static string? _desktopResolution; /// The curated list, or null when no catalog was installed /// (fixture/headless callers — consumers fall back to the static /// preset ladder). public static IReadOnlyList? Resolutions => _resolutions; /// The desktop's current mode as a "WxH" string — the Config /// Resolution row's Defaults value in production (see the class doc for /// why this replaces retail's authored 800x600). Null when no catalog /// was installed. public static string? DesktopResolution => _desktopResolution; /// Captures the catalog from the window's monitor at startup. /// A null monitor or an empty curated result leaves the catalog /// uninstalled (consumers keep the static fallback). Safe to call once /// per process launch; a repeat call overwrites with equally-fresh /// hardware truth. public static void InstallFromWindow(IWindow window) { ArgumentNullException.ThrowIfNull(window); IMonitor? monitor = window.Monitor; if (monitor is null) return; VideoMode current = monitor.VideoMode; if (current.Resolution is not { } desktop || desktop.X <= 0 || desktop.Y <= 0) return; IEnumerable<(int W, int H)> modes = monitor .GetAllVideoModes() .Select(m => m.Resolution) .Where(r => r.HasValue) .Select(r => (r!.Value.X, r.Value.Y)); IReadOnlyList curated = Curate(modes, (desktop.X, desktop.Y)); if (curated.Count == 0) return; _resolutions = curated; _desktopResolution = $"{desktop.X}x{desktop.Y}"; } /// Test seam: clears the installed catalog. internal static void ResetForTests() { _resolutions = null; _desktopResolution = null; } /// /// The pure curation rule (#391): keep a mode iff /// - it is a modern widescreen format (16:9, 16:10, or ultrawide 21:9 / /// 32:9, matched with a small tolerance so 1366x768 and friends pass), /// - it is at least 1280 wide (no legacy-era sizes), and /// - it fits the desktop (a windowed pick larger than the desktop can /// only silently clamp — if it cannot exist, it is not offered). /// The desktop mode itself is always included even if its aspect is /// unusual (it is by definition displayable), refresh-rate duplicates /// collapse to one WxH entry, and the list sorts ascending by width then /// height so the dropdown reads naturally. /// internal static IReadOnlyList Curate( IEnumerable<(int W, int H)> modes, (int W, int H) desktop) { // The modern aspect families, as width/height ratios. ReadOnlySpan modernAspects = [ 16f / 9f, 16f / 10f, 21f / 9f, 32f / 9f, ]; var keep = new SortedSet<(int W, int H)>( Comparer<(int W, int H)>.Create(static (a, b) => a.W != b.W ? a.W.CompareTo(b.W) : a.H.CompareTo(b.H))); foreach ((int w, int h) in modes) { if (w <= 0 || h <= 0) continue; if (w > desktop.W || h > desktop.H) continue; if ((w, h) == desktop) { keep.Add((w, h)); continue; } if (w < 1280) continue; float aspect = w / (float)h; bool modern = false; foreach (float family in modernAspects) { // ±2.5% covers the near-miss members of a family (1366x768 is // 1.7786 vs 16:9's 1.7778; 3440x1440 is 2.3889 vs 21:9's // 2.3333, a 2.4% miss — while a real 4:3 (1.3333) or 5:4 // (1.25) stays an order of magnitude outside every family). if (MathF.Abs(aspect - family) <= family * 0.025f) { modern = true; break; } } if (modern) keep.Add((w, h)); } return keep.Select(static m => $"{m.W}x{m.H}").ToArray(); } }