The empty-caps un-declare is REJECTED by IddCx (STATUS_INVALID_PARAMETER,
on-glass driver 9.9.0722.1407) — there is no un-declare DDI. The
composite flip now works with the only lever the OS gives us: every
mode COMMIT reverts the path to the software cursor, and the driver
skips its per-commit re-declare while cursor_forward_on is off. So:
- driver: disable stores the flag only (no DDI call); enable still
declares immediately against the live worker's event.
- host capturer: after a successful disable flip, force a same-mode
re-commit (win_display::force_mode_reenumeration) — DWM composites
the pointer from the very next commit; the swap-chain flap is the
class the driver's preserved-publisher machinery already rides out.
- state now survives re-arrivals end-to-end: the capturer caches the
APPLIED state (cleared on every channel (re)delivery, which
re-created driver entries need — their flag defaults to declared),
and the stream loop re-applies every tick instead of edge-gating
(steady-state cost: one Option compare).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On-glass (.173, match-window session): every window-size convergence
re-creates the driver-side monitor (re-arrival resize / sibling-session
slot churn), destroying the cursor worker the channel was delivered to —
the flip then died NOT_FOUND (no entry with target_id ∧ worker) and the
declared state was lost entirely.
Driver: set_cursor_forward becomes STATE, not an edge on one monitor
generation — find by target_id ∧ hw_cursor, store cursor_forward_on
even without a live worker (it steers the next delivery/re-setup);
declare/un-declare only when a worker exists. Channel delivery honors
the flag: setup_and_spawn(declare=false) adopts + spawns WITHOUT
declaring (composite mode), so a later enable-flip declares against the
worker's event.
Host: retain the SET_CURSOR_CHANNEL sender; extract
deliver_cursor_channel and RE-deliver the surviving section on every
ring recreate and — with one retry — when a flip IOCTL fails
(the re-arrived entry gets a fresh worker, declared per its flag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Belongs to 493f4fae (which shipped only the crates/ side — this
packaging/ half was left unstaged, so the deployed driver silently
lacked the IOCTL and every flip died NOT_FOUND at dispatch):
IOCTL_SET_CURSOR_FORWARD dispatch + monitor::set_cursor_forward
(cursor_forward_on flag, resetup gate) + cursor_worker::
unsetup_hardware_cursor (empty-caps un-declare experiment).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Brings the cursor channel to Windows hosts. The pf-vdisplay driver
declares an IddCx hardware cursor for sessions that negotiated
cursor-forward — DWM then EXCLUDES the pointer from the IDD frame and
delivers shape/position out-of-band, into the same CursorOverlay →
forwarder → wire → client pipeline the Linux portal path uses.
- pf-driver-proto v5 (additive, host floor stays 3): AddRequest's spare
tail becomes hw_cursor (same size/offsets); IOCTL_SET_CURSOR_CHANNEL
delivers a host-created CursorShm section (64-byte seqlock header +
256² shape buffer, layout pinned + tested). No event crosses the
boundary — the host polls at encode-tick pace.
- driver: wdk-iddcx grows the two cursor DDI wrappers; a per-monitor
cursor worker (event wait → QueryHardwareCursor → seqlock publish)
starts only when BOTH the ADD asked and the channel arrived, so a
failed delivery leaves DWM compositing as today. Shape bytes ship raw
(BGRA/masked + pitch); the host converts.
- host: the section rides the existing sealed-channel broker (least-
privilege dup, remote reap on failure); IddPushCapturer::cursor()
seqlock-reads → CursorOverlay (BGRA→RGBA, masked-color approximation,
desktop→frame origin shift, per-shape conversion cache). New
Capturer::cursor() trait hook — the encode loop prefers it over the
frame-attached overlay because hardware-cursor moves produce NO new
frame on a static desktop. hw_cursor survives the re-arrival resize
(carried on the manager's Monitor).
- negotiation: cursor_forward grows the Windows arm (client cap ∧
driver proto ≥ 5, probed once via the control device); SessionPlan
carries cursor_forward → OutputFormat.hw_cursor.
- drive-by: wdk-probe's two pre-existing same-type casts (clippy) and
pf-vdisplay's stale spike-test refs (crate::win_display moved to
pf-win-display; tracing-subscriber was never a dep) repaired.
Verified: proto tests on Mac AND MSVC (15/15 incl. the CursorShm
layout pin); clippy -D warnings for proto/frame/capture/vdisplay/host
on Linux (.21) and native Windows (.173); the DRIVER workspace clippy
-D warnings green against the real WDK 10.0.26100 bindgen (DDI names,
enum variants and IDARG layouts all bind). On-box driver deploy +
on-glass validation follow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>