fix #376/#388 review round: post-condition truth, idempotence, one

position memory, unified monitor, maximized restore; AD-92

Dual-lens Opus review of e56aa511 (reports committed under
docs/research/). The consolidated corrections:

- Mechanism M1 (load-bearing): on Windows, Silk's GLFW error callback
  QUEUES exceptions on a static list instead of throwing - they detonate
  later at window close, which is exactly #388's original two-stage
  crash shape. catch(GlfwException) was dead code here and a failed
  SetWindowMonitor "succeeded". Success is now judged by the NATIVE
  POST-CONDITION (GetWindowMonitor after the call) on both enter and
  exit; the catches remain only for the throwing platforms.
- M2 (both lenses): same-mode fullscreen re-apply is a no-op BEFORE any
  native work (new IDisplayModeSwitcher.CurrentFullscreenMode). Every
  Display-backed Config row applies per change - sliders per DRAG TICK -
  so without this every tick while fullscreen re-issued a real
  display-mode change.
- M3/M5 (both): the remembered windowed placement is process state (two
  target instances exist - startup and live-save); a fullscreen boot now
  exits through either instance to the real placement, not the (60,60)
  literal.
- M4 (both): the switcher resolves the WINDOW'S monitor (attached
  monitor when fullscreen, else IWindow.Monitor's index into the GLFW
  array - the same monitor DisplayModeCatalog enumerated), primary only
  as a last resort; the offered-list/switch-target mismatch is gone.
- Blast M2b: the offered-mode validator falls back to the SAME static
  ladder the dropdown falls back to - Full Screen is no longer a
  permanent silent no-op on catalog-less hosts (the switcher's own
  monitor-mode-list check remains the hard guard).
- Blast M3: a windowed pick on a MAXIMIZED window restores it first
  (Size writes are silently ignored while maximized; the deleted
  WindowState=Normal write used to do this incidentally). New
  IWindowedSizeSurface.IsMaximized/Restore.
- Mechanism M5: no silent bail-outs - the unparseable-resolution
  fullscreen path logs, and the failure line no longer claims "staying
  windowed" when the state is unchanged (#392 noted inline).
- Q1 nit: one cached Glfw wrapper (per-call GetApi allocated + took a
  native refcount); IsFullscreen/CurrentFullscreenMode guarded.
- AD-92: highest-refresh-for-WxH + refuse-and-log versus retail's
  pass-through-and-error ForceDisplayResolution.

Known-open tail, filed not hidden: #392 (persisted-flag divergence on a
refused enter - needs an apply-result seam); the mechanism report's
pacing-refresh WATCH rides the same seam.

Tests: +3 (same-mode no-op, unparseable-while-fullscreen refusal,
maximized restore-before-write). App suite 4,975/3 skips.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-13 18:10:07 +02:00
parent 229242e1fe
commit ec2a7b0cce
6 changed files with 1257 additions and 28 deletions

View file

@ -160,16 +160,26 @@ public sealed class RuntimeSettingsControllerTests
{
public Silk.NET.Maths.Vector2D<int> Size { get; set; } = new(1280, 720);
public int Writes { get; private set; }
public bool IsMaximized { get; set; }
public int Restores { get; private set; }
Silk.NET.Maths.Vector2D<int> IWindowedSizeSurface.Size
{
get => Size;
set { Size = value; Writes++; }
}
public void Restore()
{
IsMaximized = false;
Restores++;
}
}
private sealed class FakeModeSwitcher : IDisplayModeSwitcher
{
public bool IsFullscreen { get; set; }
public (int Width, int Height)? CurrentFullscreenMode { get; set; }
public bool EnterSucceeds { get; set; } = true;
public List<string> Calls { get; } = [];
@ -177,7 +187,11 @@ public sealed class RuntimeSettingsControllerTests
{
Calls.Add($"enter:{width}x{height}");
error = EnterSucceeds ? null : "injected failure";
if (EnterSucceeds) IsFullscreen = true;
if (EnterSucceeds)
{
IsFullscreen = true;
CurrentFullscreenMode = (width, height);
}
return EnterSucceeds;
}
@ -186,10 +200,74 @@ public sealed class RuntimeSettingsControllerTests
Calls.Add($"leave:{width}x{height}");
error = null;
IsFullscreen = false;
CurrentFullscreenMode = null;
return true;
}
}
[Fact]
public void DisplayApply_SameFullscreenMode_IsANoOp_BeforeAnyNativeWork()
{
// Review M2: sliders apply per drag tick; without this guard every
// tick while fullscreen re-issued a real display-mode change.
var surface = new FakeSizeSurface();
var switcher = new FakeModeSwitcher
{
IsFullscreen = true,
CurrentFullscreenMode = (1920, 1080),
};
var target = new SilkRuntimeDisplayWindowTarget(
surface, switcher, _ => true);
target.Apply(DisplaySettings.Default with
{
Fullscreen = true,
Resolution = "1920x1080",
});
Assert.Empty(switcher.Calls);
Assert.Equal(0, surface.Writes);
}
[Fact]
public void DisplayApply_UnparseableResolutionWhileFullscreen_RefusesWithoutCalls()
{
var surface = new FakeSizeSurface();
var switcher = new FakeModeSwitcher();
var target = new SilkRuntimeDisplayWindowTarget(
surface, switcher, _ => true);
target.Apply(DisplaySettings.Default with
{
Fullscreen = true,
Resolution = "garbage",
});
Assert.Empty(switcher.Calls);
Assert.Equal(0, surface.Writes);
}
[Fact]
public void DisplayApply_MaximizedWindowedPick_RestoresBeforeTheSizeWrite()
{
// Blast M3: a maximized window silently ignores Size writes; the
// deleted WindowState=Normal write used to un-maximize incidentally.
var surface = new FakeSizeSurface { IsMaximized = true };
var switcher = new FakeModeSwitcher();
var target = new SilkRuntimeDisplayWindowTarget(
surface, switcher, _ => true);
target.Apply(DisplaySettings.Default with
{
Fullscreen = false,
Resolution = "1600x900",
});
Assert.Equal(1, surface.Restores);
Assert.Equal(1, surface.Writes);
Assert.False(surface.IsMaximized);
}
[Fact]
public void DisplayApply_FullscreenPick_IsAValidatedModeSwitch_NeverASizeWrite()
{