diff options
| author | 2024-06-06 20:34:26 +0000 | |
|---|---|---|
| committer | 2024-06-10 13:54:39 +0000 | |
| commit | 9d3d561b46a49588162ad47c62ad01f7e37bc811 (patch) | |
| tree | 0e8308e06fbfb344738b62f40804faea93704623 | |
| parent | aa8e0640e68c596ac71a1369803c90def78eb7eb (diff) | |
InputDispatcher: Fix multi-display Pointer Capture
There is an existing requirement that a window must both have focus and
be on the focused display to be able to gain Pointer Capture.
This means that focus changes on non-focused displays should not affect
Pointer Capture, and that a window must lose capture if its display
loses focus.
Verify these requirements with a test.
Bug: 342229227
Test: atest inputflinger_tests
Change-Id: I7b1c73b7759d8f20436ee401ba657a5dc8ead7a5
| -rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 24 | ||||
| -rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 31 |
2 files changed, 45 insertions, 10 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 527edb6fe6..d22f3197bf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -5529,6 +5529,10 @@ void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) { } mFocusedDisplayId = displayId; + // Only a window on the focused display can have Pointer Capture, so disable the active + // Pointer Capture session if there is one, since the focused display changed. + disablePointerCaptureForcedLocked(); + // Find new focused window and validate sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId); sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken); @@ -6929,17 +6933,17 @@ void InputDispatcher::onFocusChangedLocked( enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason); } - // If a window has pointer capture, then it must have focus. We need to ensure that this - // contract is upheld when pointer capture is being disabled due to a loss of window focus. - // If the window loses focus before it loses pointer capture, then the window can be in a state - // where it has pointer capture but not focus, violating the contract. Therefore we must - // dispatch the pointer capture event before the focus event. Since focus events are added to - // the front of the queue (above), we add the pointer capture event to the front of the queue - // after the focus events are added. This ensures the pointer capture event ends up at the - // front. - disablePointerCaptureForcedLocked(); - if (mFocusedDisplayId == changes.displayId) { + // If a window has pointer capture, then it must have focus and must be on the top-focused + // display. We need to ensure that this contract is upheld when pointer capture is being + // disabled due to a loss of window focus. If the window loses focus before it loses pointer + // capture, then the window can be in a state where it has pointer capture but not focus, + // violating the contract. Therefore we must dispatch the pointer capture event before the + // focus event. Since focus events are added to the front of the queue (above), we add the + // pointer capture event to the front of the queue after the focus events are added. This + // ensures the pointer capture event ends up at the front. + disablePointerCaptureForcedLocked(); + sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus); } } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 8de28c680f..780bc13f90 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -11054,6 +11054,37 @@ TEST_F(InputDispatcherPointerCaptureTests, MouseHoverAndPointerCapture) { mWindow->assertNoEvents(); } +TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) { + // The default display is the focused display to begin with. + requestAndVerifyPointerCapture(mWindow, true); + + // Move the second window to a second display, make it the focused window on that display. + mSecondWindow->editInfo()->displayId = SECOND_DISPLAY_ID; + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mSecondWindow); + mSecondWindow->consumeFocusEvent(true); + + mWindow->assertNoEvents(); + + // The second window cannot gain capture because it is not on the focused display. + mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true); + mFakePolicy->assertSetPointerCaptureNotCalled(); + mSecondWindow->assertNoEvents(); + + // Make the second display the focused display. + mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); + + // This causes the first window to lose pointer capture, and it's unable to request capture. + mWindow->consumeCaptureEvent(false); + mFakePolicy->assertSetPointerCaptureCalled(mWindow, false); + + mDispatcher->requestPointerCapture(mWindow->getToken(), true); + mFakePolicy->assertSetPointerCaptureNotCalled(); + + // The second window is now able to gain pointer capture successfully. + requestAndVerifyPointerCapture(mSecondWindow, true); +} + using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests; TEST_F(InputDispatcherPointerCaptureDeathTest, |