fix(host): force a CCD mode re-enumeration after UPDATE_MODES (in-place resize)

First on-glass run: the driver accepted every UpdateModes2 (0x0 in the driver
log) but the OS never re-enumerated the target's settable modes on its own —
'OS did not advertise 800x1050 within 2s' → re-arrival fallback every time.
Re-commit the current config with SDC_FORCE_MODE_ENUMERATION (the same nudge
the isolate/layout paths already rely on) before the advertised-wait, re-kick
up to 3x, and log the actually-offered resolutions when it still misses.
Driver: dbglog the *2 mode-query/parse callbacks so the re-enumeration story
is visible in pfvd-driver.log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 17:48:44 +02:00
parent 55e59458a2
commit a738de6cd8
3 changed files with 101 additions and 6 deletions
@@ -1189,15 +1189,33 @@ impl VirtualDisplayManager {
// SAFETY: `dev` is the live control handle (this fn's contract); `update_modes` forwards it
// to a synchronous IOCTL with owned/borrowed locals only.
unsafe { self.driver.update_modes(dev, &mon.key, mode) }?;
// The OS re-evaluates the target's settable modes asynchronously after UpdateModes2 — wait
// (bounded) for the new resolution to become enumerable before forcing it, else the
// CDS_TEST inside `set_active_mode` would reject it and silently keep the old mode.
// The OS does NOT re-evaluate an indirect display's settable modes on its own after
// UpdateModes2 (on-glass: the new mode never became enumerable within 2 s) — force a mode
// re-enumeration by re-committing the current config (the same SDC_FORCE_MODE_ENUMERATION
// re-commit the isolate/layout paths use), then wait for the new resolution to appear,
// re-kicking a couple of times. Without it the CDS_TEST inside `set_active_mode` would
// reject the mode and silently keep the old one.
let t0 = Instant::now();
if !crate::win_display::wait_mode_advertised(&gdi, mode, Duration::from_millis(2000)) {
let mut advertised = false;
for kick in 0..3u32 {
// SAFETY: CCD query/apply FFI under the held `state` lock (this fn's contract).
unsafe { crate::win_display::force_mode_reenumeration() };
if crate::win_display::wait_mode_advertised(&gdi, mode, Duration::from_millis(1000)) {
advertised = true;
break;
}
tracing::debug!(
kick,
"in-place resize: new mode not yet enumerable — forcing another mode re-enumeration"
);
}
if !advertised {
anyhow::bail!(
"OS did not advertise {}x{} within 2s of the driver mode-list update",
"OS did not advertise {}x{} within {}ms of the driver mode-list update (offers: {:?})",
mode.width,
mode.height
mode.height,
t0.elapsed().as_millis(),
crate::win_display::advertised_resolutions(&gdi)
);
}
let advertised_ms = t0.elapsed().as_millis() as u64;