fix #407: windowed resolution offering decoupled from the video-mode list
Campaign CC gate round 1. The Config Resolution dropdown now offers DisplayModeCatalog.WindowedResolutions — the curated hardware modes UNIONed with the static modern-ladder sizes that fit the desktop — because a windowed pick is a plain Size write needing no video mode, and remote/RDP virtual displays advertise almost none (the live RDP display exposed exactly 1920x1080 + the 2056x1290 desktop, leaving the dropdown with nothing below 1920). The fullscreen apply still validates against the hardware Resolutions list plus the switcher's monitor-mode-list hard guard, so a fullscreen pick of a windowed-only entry refuses safely (log-and-stay, #388/#392) — IA-22's offered-implies-supported invariant narrows to the fullscreen half and its register row carries the amendment. Three new pure-union tests including the exact live RDP shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
26e6f984f4
commit
e601a496db
4 changed files with 119 additions and 5 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AcDream.UI.Abstractions.Panels.Settings;
|
||||
using Silk.NET.Windowing;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
|
@ -29,13 +30,28 @@ namespace AcDream.App.Rendering;
|
|||
internal static class DisplayModeCatalog
|
||||
{
|
||||
private static IReadOnlyList<string>? _resolutions;
|
||||
private static IReadOnlyList<string>? _windowedResolutions;
|
||||
private static string? _desktopResolution;
|
||||
|
||||
/// <summary>The curated list, or null when no catalog was installed
|
||||
/// (fixture/headless callers — consumers fall back to the static
|
||||
/// preset ladder).</summary>
|
||||
/// <summary>The curated HARDWARE mode list, or null when no catalog was
|
||||
/// installed (fixture/headless callers — consumers fall back to the
|
||||
/// static preset ladder). This is the fullscreen mode-switch validation
|
||||
/// source (#376/#388): a fullscreen pick must be a real adapter mode.</summary>
|
||||
public static IReadOnlyList<string>? Resolutions => _resolutions;
|
||||
|
||||
/// <summary>#407: the WINDOWED size offering — the curated hardware
|
||||
/// modes UNIONed with the static modern ladder entries that fit the
|
||||
/// desktop. A windowed client needs no video mode (a Size write is
|
||||
/// displayable at any size ≤ desktop), so gating the windowed dropdown
|
||||
/// on the adapter's mode list starved remote/virtual displays whose
|
||||
/// drivers advertise almost nothing (the RDP display that exposed only
|
||||
/// 1920x1080 + the desktop mode, found live at the Campaign CC gate).
|
||||
/// Null when no catalog was installed. The fullscreen APPLY still
|
||||
/// validates against <see cref="Resolutions"/> + the switcher's own
|
||||
/// monitor-mode-list hard guard, so a fullscreen pick of a
|
||||
/// windowed-only entry refuses safely (log-and-stay, #388/#392).</summary>
|
||||
public static IReadOnlyList<string>? WindowedResolutions => _windowedResolutions;
|
||||
|
||||
/// <summary>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
|
||||
|
|
@ -69,6 +85,7 @@ internal static class DisplayModeCatalog
|
|||
return;
|
||||
|
||||
_resolutions = curated;
|
||||
_windowedResolutions = BuildWindowedOffering(curated, (desktop.X, desktop.Y));
|
||||
_desktopResolution = $"{desktop.X}x{desktop.Y}";
|
||||
}
|
||||
|
||||
|
|
@ -76,9 +93,59 @@ internal static class DisplayModeCatalog
|
|||
internal static void ResetForTests()
|
||||
{
|
||||
_resolutions = null;
|
||||
_windowedResolutions = null;
|
||||
_desktopResolution = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #407's pure union rule: the windowed offering is every curated
|
||||
/// hardware mode plus every static-ladder entry that fits the desktop,
|
||||
/// deduped, ascending by width then height — the same ordering
|
||||
/// <see cref="Curate"/> emits so the dropdown reads identically on
|
||||
/// physical and remote displays.
|
||||
/// </summary>
|
||||
internal static IReadOnlyList<string> BuildWindowedOffering(
|
||||
IReadOnlyList<string> curated,
|
||||
(int W, int H) desktop)
|
||||
{
|
||||
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 (string spec in curated)
|
||||
{
|
||||
if (TryParse(spec, out (int W, int H) mode))
|
||||
keep.Add(mode);
|
||||
}
|
||||
foreach (string spec in DisplaySettings.AvailableResolutions)
|
||||
{
|
||||
if (TryParse(spec, out (int W, int H) mode)
|
||||
&& mode.W <= desktop.W
|
||||
&& mode.H <= desktop.H)
|
||||
{
|
||||
keep.Add(mode);
|
||||
}
|
||||
}
|
||||
|
||||
return keep.Select(static m => $"{m.W}x{m.H}").ToArray();
|
||||
|
||||
static bool TryParse(string spec, out (int W, int H) mode)
|
||||
{
|
||||
mode = default;
|
||||
string[] parts = spec.Split('x', 2);
|
||||
if (parts.Length == 2
|
||||
&& int.TryParse(parts[0], out int w)
|
||||
&& int.TryParse(parts[1], out int h)
|
||||
&& w > 0
|
||||
&& h > 0)
|
||||
{
|
||||
mode = (w, h);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The pure curation rule (#391): keep a mode iff
|
||||
/// - it is a modern widescreen format (16:9, 16:10, or ultrawide 21:9 /
|
||||
|
|
|
|||
|
|
@ -2578,7 +2578,12 @@ public sealed class RetailUiRuntime : IDisposable
|
|||
// default, installed at startup by the graphical host;
|
||||
// fixture/headless mounts leave the catalog empty and the
|
||||
// controller falls back to the static preset ladder.
|
||||
availableResolutions: Rendering.DisplayModeCatalog.Resolutions,
|
||||
// #407: the dropdown offers the WINDOWED union (hardware
|
||||
// modes + static-ladder sizes that fit the desktop) — a
|
||||
// windowed Size write needs no video mode, and remote/RDP
|
||||
// displays advertise almost none. The fullscreen APPLY
|
||||
// still validates against the hardware list only.
|
||||
availableResolutions: Rendering.DisplayModeCatalog.WindowedResolutions,
|
||||
resolutionDefault: Rendering.DisplayModeCatalog.DesktopResolution);
|
||||
if (!configBound)
|
||||
Console.WriteLine("[UI] options panel: Config tab rows did not bind.");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue