From b2020396c9be093778293e72313939c9b4cc13c2 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 8 Aug 2026 18:52:19 +0200 Subject: [PATCH] fix(windows/cursor): stop gating the between-session clean on host-process state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reconnect case still failed after the keep-alive guard fix, and the logs said why by omission: neither outcome line appeared, so clean_cursor_for_next_session was returning at its first early-out — CURSOR_DECLARED was false even though the previous session had logged 'driver declares the hardware cursor'. The flag is written in capture_virtual_output and read in the handshake; one of those is not on the path that actually runs. Rather than chase that, delete the dependency on it. The operation being guarded is idempotent and costs 0.07 s: restarting an already-clean adapter wastes 70 ms once per capture-mode connect, while failing to restart a dirty one costs that session a full-frame copy for every frame with a visible pointer, for its entire life. The flag stays only as a log field, so the next run still shows whether the hint was set. The real guard remains the one that matters: no session is streaming. --- .../src/vdisplay/windows/pf_vdisplay.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/pf-vdisplay/src/vdisplay/windows/pf_vdisplay.rs b/crates/pf-vdisplay/src/vdisplay/windows/pf_vdisplay.rs index 90d3b3a3..bd141098 100644 --- a/crates/pf-vdisplay/src/vdisplay/windows/pf_vdisplay.rs +++ b/crates/pf-vdisplay/src/vdisplay/windows/pf_vdisplay.rs @@ -209,9 +209,18 @@ pub fn note_cursor_declared() { /// Returns `true` only when it actually restarted. pub fn clean_cursor_for_next_session(session_wants_declare: bool) -> bool { use std::sync::atomic::Ordering; - if session_wants_declare || !CURSOR_DECLARED.load(Ordering::Relaxed) { + if session_wants_declare { return false; } + // NOT gated on `CURSOR_DECLARED`. Tracking "did we declare" in host-process memory was tried + // and silently never fired (on-glass .173, 2026-08-08: the reconnect kept getting + // `cursor_excluded=true` while BOTH outcome logs stayed absent — i.e. this function returned + // right here). The flag is set in `capture_virtual_output` and read in the handshake, and one + // of those is evidently not on the path that runs; chasing which is not worth it when the + // operation it guards is IDEMPOTENT and costs 0.07 s. Restarting an already-clean adapter + // wastes 70 ms once per capture-mode connect; NOT restarting a dirty one costs that session a + // full-frame copy for every frame with a visible pointer, for its whole life. Take the 70 ms. + let previously_declared = CURSOR_DECLARED.load(Ordering::Relaxed); // Refuse only while another session is STREAMING — a keep-alive (lingering/pinned) monitor has // no session attached and a reconnect recreates it regardless, so restarting the adapter costs // it nothing. Gating on keep-alive too made this dead code in the one case it exists for: after @@ -226,7 +235,11 @@ pub fn clean_cursor_for_next_session(session_wants_declare: bool) -> bool { if restart_device_for_clean_cursor() { CURSOR_DECLARED.store(false, Ordering::Relaxed); tracing::info!( - "cursor: cleared the previous session's hardware-cursor declare — this capture-mode session gets the OS's own pointer compositing back" + previously_declared, + "cursor: restarted the adapter for this capture-mode session — any hardware-cursor \ + declare is gone, so the OS composites the pointer itself (full fidelity, no host \ + blend). previously_declared=false only means the host-side hint was unset; the \ + restart is idempotent either way" ); return true; }