fix(windows/vdisplay): QueryHardwareCursor3 + per-mode-commit cursor re-setup — M2c findings

Durable results of the M2c on-glass bring-up (.173, 2026-07-22):

- The base IddCxMonitorQueryHardwareCursor DDI slot is stubbed to
  STATUS_NOT_SUPPORTED on WDK 26100 — add the ...QueryHardwareCursor3
  wrapper (wdk-iddcx) and drain it instead; v3 X/Y are only meaningful
  when PositionValid, so a position-invalid tick keeps the prior position.
- Hardware-cursor setup is per-mode-commit: the OS silently reverts to a
  software cursor on every mode commit, so re-issue the setup on each
  swap-chain assignment (monitor::resetup_cursor, called from
  assign_swap_chain) or the query fails NOT_SUPPORTED forever.
- One caps definition for initial setup and re-setup, resting at XOR
  FULL: the query delivers ONLY alpha shapes in every configuration
  (all three ColorXorCursorSupport levels, event-driven and 30 Hz
  polled) — masked/monochrome cursors never arrive. FULL keeps the
  frame cursor-free for ALL cursor types; the full-fidelity shape comes
  from the session-side cursor source in the host
  (design/remote-desktop-sweep.md §8).
- Poll the query at ~30 Hz instead of pure event-wait: masked cursors
  never fire the data event, and the seqlock's position/visibility
  should stay fresh regardless.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-22 23:43:23 +02:00
co-authored by Claude Opus 4.8
parent 2bbcbc81c6
commit d2cc770938
4 changed files with 126 additions and 24 deletions
@@ -333,6 +333,10 @@ pub unsafe extern "C" fn assign_swap_chain(
);
// Install on the monitor; drop any processor it replaced (a race lost above) OUTSIDE the lock.
drop(crate::monitor::set_swap_chain_processor(monitor, processor));
// A mode is now committed on this path — re-declare the hardware cursor (the OS reverts
// to a software cursor on every mode commit, which otherwise makes the cursor worker's
// QueryHardwareCursor fail STATUS_NOT_SUPPORTED). No-op unless a cursor worker is live.
crate::monitor::resetup_cursor(monitor);
} else {
// D3D init failed: delete the swap-chain so the OS generates a fresh one + retries.
dbglog!(