Revert "fix(host/windows): rebuild the output fresh on every WGC↔DDA source switch"

This reverts commit 3f191ba2ea.
This commit is contained in:
2026-06-16 10:44:06 +00:00
parent 3f191ba2ea
commit 555ec2a3b7
2 changed files with 27 additions and 62 deletions
@@ -17,21 +17,19 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use windows::core::w; use windows::core::{w, PCWSTR};
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::Graphics::Gdi::{GetStockObject, BLACK_BRUSH, HBRUSH};
use windows::Win32::System::LibraryLoader::GetModuleHandleW; use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::StationsAndDesktops::{ use windows::Win32::System::StationsAndDesktops::{
CloseDesktop, GetUserObjectInformationW, OpenInputDesktop, SetThreadDesktop, CloseDesktop, GetUserObjectInformationW, OpenInputDesktop, SetThreadDesktop,
DESKTOP_ACCESS_FLAGS, DESKTOP_CONTROL_FLAGS, UOI_NAME, DESKTOP_ACCESS_FLAGS, DESKTOP_CONTROL_FLAGS, UOI_NAME,
}; };
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetSystemMetrics, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, PeekMessageW, RegisterClassW,
PeekMessageW, RegisterClassW, SetLayeredWindowAttributes, SetWindowPos, ShowWindow, SetLayeredWindowAttributes, SetWindowPos, ShowWindow, TranslateMessage, HWND_TOPMOST,
TranslateMessage, HWND_TOPMOST, LWA_COLORKEY, MSG, PM_REMOVE, SM_CXVIRTUALSCREEN, LWA_ALPHA, MSG, PM_REMOVE, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SW_SHOWNOACTIVATE,
SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN, SWP_NOACTIVATE, SWP_NOMOVE, WNDCLASSW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_EX_TRANSPARENT,
SWP_NOSIZE, SW_SHOWNOACTIVATE, WNDCLASSW, WS_EX_LAYERED, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_POPUP,
WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_POPUP,
}; };
/// A running force-composed-flip overlay. Drop signals the thread to tear down its window + exit. /// A running force-composed-flip overlay. Drop signals the thread to tear down its window + exit.
@@ -107,8 +105,6 @@ unsafe fn make_overlay() -> Option<HWND> {
lpfnWndProc: Some(wndproc), lpfnWndProc: Some(wndproc),
hInstance: hinst.into(), hInstance: hinst.into(),
lpszClassName: class, lpszClassName: class,
// Paint the window black so LWA_COLORKEY(black) keys it out → visually invisible.
hbrBackground: HBRUSH(GetStockObject(BLACK_BRUSH).0),
..Default::default() ..Default::default()
}; };
let atom = RegisterClassW(&wc); let atom = RegisterClassW(&wc);
@@ -119,24 +115,15 @@ unsafe fn make_overlay() -> Option<HWND> {
tracing::warn!(err = e.0, "force-composed-flip: RegisterClassW failed"); tracing::warn!(err = e.0, "force-composed-flip: RegisterClassW failed");
} }
} }
// Cover the WHOLE virtual screen (all outputs incl. the SudoVDA): a 1x1 corner window may not even
// sit on the captured output's rect, and independent-flip is per-output. A window overlapping the
// output is what disqualifies its flip.
let (vx, vy, vw, vh) = (
GetSystemMetrics(SM_XVIRTUALSCREEN),
GetSystemMetrics(SM_YVIRTUALSCREEN),
GetSystemMetrics(SM_CXVIRTUALSCREEN).max(1),
GetSystemMetrics(SM_CYVIRTUALSCREEN).max(1),
);
let hwnd = match CreateWindowExW( let hwnd = match CreateWindowExW(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW,
class, class,
w!(""), w!(""),
WS_POPUP, WS_POPUP,
vx, 0,
vy, 0,
vw, 1,
vh, 1,
None, None,
None, None,
Some(hinst.into()), Some(hinst.into()),
@@ -150,16 +137,8 @@ unsafe fn make_overlay() -> Option<HWND> {
return None; return None;
} }
}; };
// Color-key on black: the window is painted black (its WNDCLASS background brush) and black is keyed // alpha=1: technically visible (so it disqualifies independent-flip) but imperceptible.
// out, so it's visually invisible — but DWM counts it as a real opaque window covering the output, let _ = SetLayeredWindowAttributes(hwnd, windows::Win32::Foundation::COLORREF(0), 1, LWA_ALPHA);
// which is what disqualifies the fullscreen independent-flip (a near-zero ALPHA layered window is
// often ignored by the flip-eligibility check; a color-keyed one is not).
let _ = SetLayeredWindowAttributes(
hwnd,
windows::Win32::Foundation::COLORREF(0x0000_0000),
0,
LWA_COLORKEY,
);
let _ = ShowWindow(hwnd, SW_SHOWNOACTIVATE); let _ = ShowWindow(hwnd, SW_SHOWNOACTIVATE);
let _ = SetWindowPos( let _ = SetWindowPos(
hwnd, hwnd,
+15 -29
View File
@@ -2453,40 +2453,26 @@ fn virtual_stream_relay(
}, },
"two-process: source switch" "two-process: source switch"
); );
// Rebuild the SudoVDA output FRESH on every source switch — the key insight: a fresh
// reconnect captures the secure desktop but the live transition (which reused the
// session-start output, created while on the NORMAL desktop) does not. `build` does the
// exact reconnect setup: REMOVE + fresh ADD of the virtual monitor + re-isolate + a fresh
// capturer, so the new output is in the clean state that DDA can duplicate on the secure
// desktop. Drop the old DDA (it's bound to the old target); reopen on the new one.
match build(&mut vd, cur_mode) {
Ok((ka, rl, tg, hz)) => {
relay = rl; // fresh helper (drops the old) ...
_keepalive = ka; // ... then releases the old output
target = tg;
effective_hz = hz;
interval = std::time::Duration::from_secs_f64(1.0 / hz.max(1) as f64);
}
Err(e) => {
tracing::error!(error = %format!("{e:#}"),
"two-process: source-switch rebuild failed — staying on the current source");
}
}
dda = None; // old DDA was on the old target
if secure { if secure {
match open_dda(&target, cur_mode.width, cur_mode.height, effective_hz) { if dda.is_none() {
Ok(mut p) => { match open_dda(&target, cur_mode.width, cur_mode.height, effective_hz) {
p.enc.request_keyframe(); Ok(p) => dda = Some(p),
dda = Some(p); Err(e) => {
} tracing::error!(error = %format!("{e:#}"),
Err(e) => { "two-process: DDA open failed — secure desktop will freeze on last frame");
tracing::error!(error = %format!("{e:#}"), }
"two-process: DDA open failed — secure desktop will freeze on last frame");
} }
} }
if let Some(d) = dda.as_mut() {
d.enc.request_keyframe();
}
next = std::time::Instant::now(); next = std::time::Instant::now();
} else {
// Returning to the helper: drain stale buffered AUs (encoded while we ignored it) and
// force a fresh IDR; await_idr then skips the stale deltas until that IDR arrives.
while relay.try_recv().is_ok() {}
relay.request_keyframe();
} }
// (normal: the fresh helper's first frame is its opening IDR — await_idr clears on it.)
} }
if want_kf { if want_kf {
if secure { if secure {