fix(vdisplay/capture): channel-less sessions composite the pointer on a sticky-declared target
A declared IddCx hardware cursor is IRREVOCABLE for its OS target's life (§8.6), and the sticky exclusion survives monitor REMOVE→ADD because each client gets a STABLE target id — so once any desktop-mode session declared, every later pure-capture session on that target streamed a cursor-less desktop: DWM excluded the pointer, no channel forwarded it, no blend drew it (the exact no-regression gap §8.6's per-session cap gate cannot see). Driver: track every successful SetupHardwareCursor per target (DECLARED_TARGETS — scoped to the WUDFHost's life, exactly the sticky state's scope) and report it in a new AddReply::cursor_excluded tail field (dual-size discipline, both skews degrade cleanly; no proto bump). Host: the flag rides AddedMonitor → Monitor → WinCaptureTarget; a session WITHOUT the cursor channel on a flagged target forces composite mode in the IDD-push capturer — GDI poller + blend for the session's life, pinned on (set_cursor_forward cannot clear it: with no client drawing, un-compositing would erase the pointer entirely). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,12 @@ struct Monitor {
|
||||
/// This monitor was ADDed with the v5 hardware-cursor flag (already driver-proto-gated) —
|
||||
/// preserved across a re-arrival resize so the recreated monitor keeps the cursor channel.
|
||||
hw_cursor: bool,
|
||||
/// The ADD reply flagged this monitor's OS target as carrying an IRREVOCABLE hardware-cursor
|
||||
/// declare from an earlier session (§8.6): DWM excludes the pointer from its frames forever
|
||||
/// (the sticky state survives monitor REMOVE→ADD via the stable per-client target id), so a
|
||||
/// session WITHOUT the cursor channel must self-composite — carried into
|
||||
/// [`WinCaptureTarget`] for the IDD-push capturer's forced-composite gate.
|
||||
cursor_excluded: bool,
|
||||
/// The driver's WUDFHost pid (from the ADD reply) — carried into [`WinCaptureTarget`] so the
|
||||
/// IDD-push capturer knows where to duplicate the sealed frame channel's handles. The SAME
|
||||
/// process for every parallel monitor (one devnode → one WUDFHost hosts all publishers), which
|
||||
@@ -101,6 +107,7 @@ impl Monitor {
|
||||
gdi_name: n,
|
||||
target_id: self.target_id,
|
||||
wudf_pid: self.wudf_pid,
|
||||
cursor_excluded: self.cursor_excluded,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1092,6 +1099,7 @@ impl VirtualDisplayManager {
|
||||
position: (0, 0),
|
||||
gen: self.gen.fetch_add(1, Ordering::Relaxed),
|
||||
hw_cursor,
|
||||
cursor_excluded: added.cursor_excluded,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1315,6 +1323,9 @@ impl VirtualDisplayManager {
|
||||
position: old.position,
|
||||
gen: old.gen,
|
||||
hw_cursor: old.hw_cursor,
|
||||
// Fresh from THIS reply, not `old`: the driver's per-target declare registry is the
|
||||
// ground truth (this session may itself have declared since the original ADD).
|
||||
cursor_excluded: added.cursor_excluded,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,11 @@ pub(crate) struct AddedMonitor {
|
||||
pub luid: LUID,
|
||||
pub wudf_pid: u32,
|
||||
pub resolved_monitor_id: u32,
|
||||
/// The driver reports the OS target already carries an IRREVOCABLE hardware-cursor declare
|
||||
/// from an earlier session (`AddReply::cursor_excluded`, remote-desktop-sweep §8.6): DWM
|
||||
/// excludes the pointer from this target's frames forever, so a session without the cursor
|
||||
/// channel must self-composite (GDI poller + blend) or stream a cursor-less desktop.
|
||||
pub cursor_excluded: bool,
|
||||
}
|
||||
|
||||
/// The backend-specific IOCTL surface — the *only* thing that differs between SudoVDA and pf-vdisplay.
|
||||
|
||||
@@ -605,10 +605,13 @@ impl VdisplayDriver for PfVdisplayDriver {
|
||||
})?;
|
||||
// Fail closed on a short reply — `target_id`/`wudf_pid`/`luid` below feed OpenProcess + the
|
||||
// WUDFHost verification, so don't decode a partially-written (zeroed) reply as authoritative.
|
||||
if (n as usize) < size_of::<control::AddReply>() {
|
||||
// The LEGACY size, not the full struct: an un-upgraded driver writes only the prefix before
|
||||
// the `cursor_excluded` tail; `out` is zero-initialized, so the missing tail reads `0`
|
||||
// (= unknown/clean — exactly what a driver that can't track declares should report).
|
||||
if (n as usize) < control::ADD_REPLY_LEGACY_SIZE {
|
||||
anyhow::bail!(
|
||||
"pf-vdisplay ADD returned {n} bytes, expected {}",
|
||||
size_of::<control::AddReply>()
|
||||
"pf-vdisplay ADD returned {n} bytes, expected at least {}",
|
||||
control::ADD_REPLY_LEGACY_SIZE
|
||||
);
|
||||
}
|
||||
// `pod_read_unaligned` (NOT `from_bytes`): `out` is a stack `[u8; N]` with no guaranteed 4-byte
|
||||
@@ -623,6 +626,7 @@ impl VdisplayDriver for PfVdisplayDriver {
|
||||
target_id = reply.target_id,
|
||||
adapter_luid = %format_args!("{:#x}", luid.LowPart),
|
||||
wudf_pid = reply.wudf_pid,
|
||||
cursor_excluded = reply.cursor_excluded != 0,
|
||||
"pf-vdisplay monitor created {}x{}@{}",
|
||||
mode.width,
|
||||
mode.height,
|
||||
@@ -659,6 +663,7 @@ impl VdisplayDriver for PfVdisplayDriver {
|
||||
luid,
|
||||
wudf_pid: reply.wudf_pid,
|
||||
resolved_monitor_id: reply.resolved_monitor_id,
|
||||
cursor_excluded: reply.cursor_excluded != 0,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user