Repository navigation
Fix completed WISP touch promotion across modal dialogs - #12034
Open
amarinov-msft wants to merge 3 commits into
Open
amarinov-msft wants to merge 3 commits into
amarinov-msft wants to merge 3 commits into
Conversation
Defer cancelled manipulation replay until touch-up and deactivation finish. Track contact generations and owned mouse presses across nested replay, source disable/disposal and contact reuse. Add guarded native regressions and managed ownership coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1fe51848-46fe-4323-8746-5267d21e7788
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Physical mouse input and handled StylusUp paths can still leave promotion state inconsistent or replay before deactivation completes.
2 open findings
What changed in this PR
Fixes #2491 by deferring WISP touch-to-mouse replay until touch-up deactivation completes.
Changes:
- Adds generation-aware replay and mouse-button ownership tracking.
- Handles source disposal, nested input, and reentrancy.
- Adds managed and opt-in native touch regression coverage.
| File | Description |
|---|---|
WispLogic.cs |
Implements deferred replay and ownership bookkeeping. |
WispStylusDevice.cs |
Integrates replay lifecycle with mouse promotion. |
WispStylusTouchDevice.cs |
Tracks contact generations and deferred promotion. |
WispStylusTouchDeviceTests.cs |
Tests replay bookkeeping and ownership. |
WispModalTouchTests.cs |
Adds native modal-touch regressions. |
PresentationCore.Tests.csproj |
Adds required test dependencies. |
PresentationFramework.Tests.csproj |
Adds required UI test dependencies. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Retire completed replay bookkeeping before real mouse takeover and reconcile the affected button through its physical release. Share the completed-touch deactivation scope with handled PreviewStylusUp and StylusUp without reporting TouchUp. Add managed ownership and native modal regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1fe51848-46fe-4323-8746-5267d21e7788
Clear matching replay device state before invalidating button ownership on drag/drop exit and source re-enable. Preserve physically held buttons and newer contacts, and cover both resynchronization paths with managed regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 466a2938-008f-4030-912a-904ed8296d05
amarinov-msft
marked this pull request as ready for review
October 21, 2026 07:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Fixes #2491
Main PR: Not applicable; this is the main-branch fix, not a backport.
Description
Fix WISP touch-to-mouse promotion when a cancelled manipulation replays a mouse-down handler that synchronously opens a modal dialog.
With a panning ScrollViewer, a tap can be buffered until touch-up cancels manipulation. Replaying the buffered mouse down from inside touch-up can enter
ShowDialog()before the original touch device deactivates. That device retains mouse-promotion ownership, preventing subsequent dialog touches from generating mouse events until WISP reuses its cursor ID. The reproduction on this machine ignored ten touches and first clicked on touch eleven; that count is device/environment dependent.This change:
PreviewStylusUpandStylusUpuse the same completion scope without reporting a suppressedTouchUp. Mid-contact cancellation remains immediate.Adds 46 deterministic bookkeeping/ownership cases in
PresentationCore.Testsand 14 opt-in native cases inPresentationFramework.Tests, plus the project references needed by the exercised input/UI paths.The product change is WISP-only: no public API or pointer-stack changes. The separate non-panning, live-contact modal variant is outside this fix's scope.
Related: #194 and #11999. I also tested the unchanged native regressions with only the product patch from #11999 at
5f54e6051b42fa8f27ca1c1b5a67cdcc183570da, on the same upstream baseline and without this PR's product changes. Its Release build improved the first successful dialog click from touch eleven to touch one, but still missed the Click on touch ten. A fresh process running only the three modal cases reproduced the same failure; both Click-based controls passed. Its full Release native suite had 9 passes and 3 failures (the modal case, handled-TouchUp completion, and throwing-handler recovery). Its Debug suite aborted on a WISP assertion during the modal case. This is evidence of overlapping behavior, not a claim that every scenario reported under the two issues is identical or that the other failures were introduced by that PR.Customer Impact
Touch-operated applications opening modal dialogs from promoted mouse-down handlers can appear unresponsive: touch events reach the dialog, but Buttons and other mouse-dependent controls do not receive the corresponding mouse events. Customers may need repeated taps before controls start responding.
The regression verifies that every one of 36 dialog touches clicks across three open/close cycles, rather than checking only eventual recovery.
Regression
No known recent-release regression. The issue is longstanding and has also been reported on .NET Framework.
Testing
Validated locally on Windows, x64 Debug, at commit
98a75a9bf310853d185635b75a86e1bdbb64a922, based on upstream5edf2b293. Tests targetnet11.0-windowsand ran on.NET 12.0.0-alpha.1.26509.101. The commands/results below were rerun after addressing the two review findings.Before the corrections, seven new managed cases failed on the original PR product code: handled-preview-up deactivation, left/right physical press/release followed by another touch, and held physical-button cleanup. All pass after correction. Additional coverage checks release after the replay has left the active stack, nested left/right takeover, release without an observed press, and preservation of unrelated legacy button behavior.
Builds:
Result: passed, 0 warnings, 0 errors; exit 0.
Result: passed, 0 warnings, 0 errors; exit 0. Builds used existing installed tools/artifacts; clean-machine bootstrap is not validated.
Core input compatibility:
Result: 249 passed, 0 failed, 0 skipped; exit 0. Includes all 46 new WISP cases, including nested-operation coverage. These managed ownership/lifetime models are not physical-device tests.
Native desktop touch regressions:
Result: 14 passed, 0 failed, 0 skipped; exit 0. Covers the modal/control matrix, handled touch input, successful panning/inertia, mid-contact cancellation, source closure, throwing-handler recovery, simultaneous contacts, and cross-window mouse capture transfer. The two new modal cases handle
PreviewStylusUpandStylusUp; both require deactivation before opening the dialog, no promotedTouchUp, and every one of 36 dialog touches clicking across three cycles.This is guarded native touch injection on an explicitly idle interactive desktop. The tests assert locally loaded WPF assemblies and observed WISP touch devices; foreground, process-ownership and unobscured-window guards stay enabled. The modal regression failed before the fix with first successful dialog touch eleven and passes with this fix for every touch across all three cycles.
Default opt-in gating:
Result: 14 skipped, 0 executed; expected all-skipped exit 8. Do not enable native injection on a shared/unattended desktop or run another desktop test executable concurrently.
Neighboring MessageBox coverage:
Result: 58 passed, 0 failed, 0 skipped; exit 0.
git diff --checkResult: passed, exit 0. The staged review-correction diff was also checked with
git diff --cached --check: passed.Pending / not run: physical touch-device validation by a teammate; physical mouse/pen and monitor/DPI combinations; native nested contact reuse/hot-unplug and source closure specifically during a pre-press move; full OLE drag/drop resynchronization; pointer-stack runtime testing; the full repository suite. Targeted DRT binaries were unavailable, so DRTs were not run. The original non-panning live-contact variant was not revalidated on the final binaries.
Risk
Medium. This changes reentrant WISP touch-up/manipulation-cancellation ordering and shared mouse-state bookkeeping. The completed-touch path intentionally finishes touch-up and deactivation before replaying buffered mouse input.
Risk is constrained by leaving immediate mid-contact cancellation and the pointer implementation unchanged, checking source identity/contact generation/queue revision, and releasing only a provably owned mouse press. Deterministic and native tests cover nested operations, primary/secondary contacts, source lifetime and capture transfer, but do not replace hardware validation.
This PR is a draft pending physical-device testing and review of the input-lifecycle changes.
Microsoft Reviewers: Open in CodeFlow