Repository navigation
Fix pointer capture cancellation on Win32 - #22054
Conversation
A cancelled pen or mouse interaction was reported as an ordinary leave, and nothing released the capture that came with it, so the captured element never saw PointerCaptureLost. Win32 constants: POINTER_FLAG_CONFIDENCE and POINTER_FLAG_CANCELED were declared an order of magnitude too small - the SDK defines them as 0x4000 and 0x8000 - so the cancellation check in GetEventType could never match. Ctrl+Alt+Del or Win+L mid stroke therefore arrived as a normal button up, and the app committed an interaction the user had aborted. Win32 handling: WM_POINTERCAPTURECHANGED and POINTER_FLAG_CANCELED now map to CancelCapture for non touch pointers, matching what the legacy WM_CAPTURECHANGED path has always done. PenDevice: handle CancelCapture by releasing the capture while keeping the pointer alive, since the pen is still in range and a leave arrives separately. Pointer hardening: CaptureLost ends every capture the pointer holds, on the element and on a gesture recognizer, and the devices release through it instead of repeating the steps. Capturing to a recognizer clears Captured, so the null check in the platform path used to skip the whole release while a scroll, pull or swipe gesture owned the pointer, and a touch up never cleared the recognizer at all. Dispose releases as well rather than leaving that to every caller, a disposed pointer ignores further capture calls and asserts when a capturer is passed, and devices drop a pointer from their map before disposing it. Co-Authored-By: Claude Opus 5 (1M context)
|
You can test this PR using the following package version. |
There was a problem hiding this comment.
Pull request overview
This PR hardens pointer capture lifecycle handling and fixes Win32 pen/touch cancellation/capture-change mapping so that cancellations end capture correctly (instead of being misreported as pointer-up/leave).
Changes:
- Add a centralized
Pointer.CaptureLost(source)path and makePointer.Dispose()clear dangling captures (including gesture-recognizer capture). - Update Win32 message handling to map
WM_POINTERCAPTURECHANGEDandPOINTER_FLAG_CANCELEDtoCancelCapture/TouchCancelas appropriate. - Update mouse/pen/touch devices and unit test helpers to use
CaptureLost(...)rather than manually clearing multiple pointer fields.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Avalonia.UnitTests/TouchTestHelper.cs | Switch test helper cancellation to the new CaptureLost(...) API. |
| tests/Avalonia.UnitTests/MouseTestHelper.cs | Switch test helper capture release to CaptureLost(...). |
| src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs | Map Win32 pointer capture-change/canceled-flag to cancel-capture semantics. |
| src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | Correct PointerFlags bit values for confidence/canceled. |
| src/Avalonia.Base/Input/TouchDevice.cs | Use CaptureLost(...)/Dispose() for touch end/cancel cleanup. |
| src/Avalonia.Base/Input/Pointer.cs | Add disposed-guarding and the unified CaptureLost(...) capture teardown path. |
| src/Avalonia.Base/Input/PenDevice.cs | Handle CancelCapture without dropping the pointer until leave, and use CaptureLost(...) on pen up. |
| src/Avalonia.Base/Input/MouseDevice.cs | Use CaptureLost(...) on mouse up and honor cancel-capture events. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
You can test this PR using the following package version. |
f9d049c to
a4cc301
Compare
The helper captures through Pointer.Capture(target), which is an explicit capture, and used to release with Capture(null) - explicit as well. Releasing as implicit changed the source that capture changing handlers observe. Co-Authored-By: Claude Opus 5 (1M context)
a4cc301 to
a162279
Compare
|
You can test this PR using the following package version. |
* Fix pointer capture cancellation on Win32 A cancelled pen or mouse interaction was reported as an ordinary leave, and nothing released the capture that came with it, so the captured element never saw PointerCaptureLost. Win32 constants: POINTER_FLAG_CONFIDENCE and POINTER_FLAG_CANCELED were declared an order of magnitude too small - the SDK defines them as 0x4000 and 0x8000 - so the cancellation check in GetEventType could never match. Ctrl+Alt+Del or Win+L mid stroke therefore arrived as a normal button up, and the app committed an interaction the user had aborted. Win32 handling: WM_POINTERCAPTURECHANGED and POINTER_FLAG_CANCELED now map to CancelCapture for non touch pointers, matching what the legacy WM_CAPTURECHANGED path has always done. PenDevice: handle CancelCapture by releasing the capture while keeping the pointer alive, since the pen is still in range and a leave arrives separately. Pointer hardening: CaptureLost ends every capture the pointer holds, on the element and on a gesture recognizer, and the devices release through it instead of repeating the steps. Capturing to a recognizer clears Captured, so the null check in the platform path used to skip the whole release while a scroll, pull or swipe gesture owned the pointer, and a touch up never cleared the recognizer at all. Dispose releases as well rather than leaving that to every caller, a disposed pointer ignores further capture calls and asserts when a capturer is passed, and devices drop a pointer from their map before disposing it. Co-Authored-By: Claude Opus 5 (1M context)* Keep the explicit capture source in MouseTestHelper The helper captures through Pointer.Capture(target), which is an explicit capture, and used to release with Capture(null) - explicit as well. Releasing as implicit changed the source that capture changing handlers observe. Co-Authored-By: Claude Opus 5 (1M context) --------- Co-authored-by: Claude Opus 5 (1M context)
This PR has separate connected changes:
a) Dispose now properly releases any dangling captures and prevents pointer from being captured again
b) Gesture recognizer captures are now correctly released, platforms are required to call
CaptureLost(source)instead ofCapture(null)which doesn't affect recognizer capture.E2E-ish test coverage will arrive with headless pen/touch PR.
WM_POINTERCAPTURECHANGEDand previously mishandledPOINTER_FLAG_CANCELEDnow map toCancelCapture.Non-win32 backends are not yet updated, the only observable change is that any (invalid) leftover capture is cleared when pointer is disposed in
LeaveWindow.followup plan: