fix(windows/cursor): do the clean on the ADD path — the handshake call site was never reached

Third and last placement bug in this chain. clean_cursor_for_next_session was
called from the handshake, inside the '#[cfg(windows)] let prep = match (source,
compositor) { (Virtual, Some(comp)) => ...' arm — which is not the path taken on
this host, so the function never ran. The logs said so by omission: no 'skipping'
line, no 'recycled' line, and cursor_excluded stayed true across the reconnect.

Move it next to where the declare is recorded: the ADD request. Every session
that creates a monitor goes through it, with hw_cursor in hand, before the
monitor exists — which is also the moment the recycle is safe.
This commit is contained in:
2026-08-09 00:24:15 +02:00
parent 7b91afd721
commit 06086de328
2 changed files with 8 additions and 7 deletions
@@ -894,6 +894,14 @@ impl VdisplayDriver for PfVdisplayDriver {
client_hdr: Option<punktfunk_core::quic::HdrMeta>,
hw_cursor: bool,
) -> Result<AddedMonitor> {
// Give a capture-mode session the LOSSLESS pointer back: if an earlier session declared a
// hardware cursor and this one does not want it, recycle the driver's host process BEFORE
// this monitor is added. The ADD path is the only place guaranteed to see every session
// (the handshake call site this replaced sat in a `match (source, compositor)` arm that is
// not taken on this host, so it never ran).
if !hw_cursor {
clean_cursor_for_next_session(false);
}
let session_id = next_session_id();
// The client display's volume rides into the monitor's EDID CTA HDR block; all-zero =
// unknown → the driver keeps its built-in defaults (also what an un-upgraded driver, which
@@ -686,13 +686,6 @@ pub(super) async fn negotiate(
// The bit the Welcome just advertised — read back rather than recomputed, so the
// prepared display and the session wiring cannot disagree with it.
let cursor_fw = welcome.host_caps & punktfunk_core::quic::HOST_CAP_CURSOR != 0;
// Give a capture-mode session back the LOSSLESS pointer: if an earlier session on this
// host declared the hardware cursor (desktop mouse model) and this one did not ask for
// it, clear the sticky declare now — BEFORE this session's display is created, which is
// the only safe moment, and exactly the "desktop session, disconnect, reconnect in
// capture mode" case the start-up clean cannot reach. No-op when nothing declared, when
// this session wants the channel, or when any display is still held.
pf_vdisplay::driver::clean_cursor_for_next_session(cursor_fw);
// Same bit the data plane's SessionContext reads — the prepared plan and the
// session wiring must agree on the slicing ceiling (an encoder rebuilt from the
// prepared plan with a DIFFERENT max_slices would change the wire shape mid-flow).