Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d3d561b authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

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
parent aa8e0640
Loading
Loading
Loading
Loading
+14 −10
Original line number Diff line number Diff line
@@ -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.
    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();

    if (mFocusedDisplayId == changes.displayId) {
        sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
    }
}
+31 −0
Original line number Diff line number Diff line
@@ -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,