fix(windows/cursor): record the declare from the ADD request — the only place every one passes

CURSOR_DECLARED was set in capture_virtual_output, which is not on the Windows
prep path, so it was never true and the between-session clean returned at its
first gate every time, silently. Both on-glass runs that appeared to 'not fire'
were this; the one run that did fire was a build with no gate at all.

Move it to the ADD request in the driver module, which every session that wants
a hardware cursor goes through by construction.
This commit is contained in:
2026-08-09 00:16:44 +02:00
parent 0ed4b51104
commit 7b91afd721
2 changed files with 12 additions and 15 deletions
@@ -248,18 +248,12 @@ fn recycle_wudfhost() -> bool {
}
/// Has this host process DECLARED an IddCx hardware cursor since the adapter was last restarted?
/// Set by [`note_cursor_declared`] when a session delivers a cursor channel; cleared when
/// [`clean_cursor_for_next_session`] restarts the device. This is the host's own mirror of the
/// Set by the ADD path when a session ASKS for a hardware cursor (the one place every declare
/// passes through); cleared when the declare is dropped. The host's own mirror of the
/// driver's `DECLARED_TARGETS` — cheaper than probing, and it only ever needs to be right about
/// "did WE dirty it", because a declare from an earlier BOOT is handled by the start-up clean.
static CURSOR_DECLARED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
/// Record that a session declared the hardware cursor (it delivered a cursor channel), so the next
/// session that does NOT want one knows the adapter needs cleaning.
pub fn note_cursor_declared() {
CURSOR_DECLARED.store(true, std::sync::atomic::Ordering::Relaxed);
}
/// Give the NEXT session back the lossless cursor: if an earlier session on this host declared the
/// hardware cursor and this one does not want it, restart the device to clear the sticky declare.
///
@@ -928,7 +922,16 @@ impl VdisplayDriver for PfVdisplayDriver {
// (DWM stops compositing the pointer into the frame); the capture layer delivers the
// CursorShm section right after its ring. Zero toward older drivers is harmless —
// the host only sets this when the handshake-reported proto is >= 5.
hw_cursor: hw_cursor as u32,
hw_cursor: {
// The ONE place guaranteed to see every declare: the ADD request that asks for it.
// (An earlier attempt recorded this from `capture_virtual_output`, which is not on
// the Windows prep path — the flag stayed false and the between-session clean
// silently never ran.)
if hw_cursor {
CURSOR_DECLARED.store(true, std::sync::atomic::Ordering::Relaxed);
}
hw_cursor as u32
},
};
// SET_RENDER_ADAPTER (opt-in; pf-vdisplay IMPLEMENTS it). Non-fatal on failure: the driver reports
// its real render LUID in the shared header, so the host binds correctly even if this is ignored.
-6
View File
@@ -231,12 +231,6 @@ pub fn capture_virtual_output(
// Cursor-forward sessions (M2c): hand the capturer the v5 cursor-channel delivery closure —
// its presence opts the session in (the capturer creates + delivers the CursorShm section,
// the driver declares the IddCx hardware cursor). Built exactly like `sender` above.
// Remember that this host declared, so the NEXT session that wants no hardware cursor knows to
// clear it (`clean_cursor_for_next_session`) instead of self-compositing for its whole life.
#[cfg(target_os = "windows")]
if want.hw_cursor {
crate::vdisplay::driver::note_cursor_declared();
}
let cursor_sender: Option<pf_capture::CursorChannelSender> = want.hw_cursor.then(|| {
std::sync::Arc::new(
move |req: &pf_driver_proto::control::SetCursorChannelRequest| {