Enable cursor to transition across multiple displays
This CL enables cursor to move between displays. It uses a fake
topology that assumes all available displays are connected in the
following order:
default-display (top-edge) -> next-display (right-edge)
-> next-display (right-edge) ...
Test: atest inputflinger_tests
Test: verify cursor can move between displays as expected
Bug: 367659738
Bug: 367660694
Flag: com.android.input.flags.connected_displays_cursor
Change-Id: I8b3b7c3c0e68dca1e7ce281cb7ff6efab263a500
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 887a939..c1606a7 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -148,15 +148,20 @@
return mIsPointerShown;
}
-void FakePointerController::move(float deltaX, float deltaY) {
- if (!mEnabled) return;
+FloatPoint FakePointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return {0, 0};
mX += deltaX;
+ mY += deltaY;
+
+ const FloatPoint position(mX, mY);
+
if (mX < mMinX) mX = mMinX;
if (mX > mMaxX) mX = mMaxX;
- mY += deltaY;
if (mY < mMinY) mY = mMinY;
if (mY > mMaxY) mY = mMaxY;
+
+ return {position.x - mX, position.y - mY};
}
void FakePointerController::fade(Transition) {
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 9b773a7..04adff8 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -65,7 +65,7 @@
private:
std::string dump() override { return ""; }
- void move(float deltaX, float deltaY) override;
+ FloatPoint move(float deltaX, float deltaY) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 411c7ba..f427658 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -2601,6 +2601,126 @@
metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
}
+using PointerChoreographerDisplayTopologyTestFixtureParam =
+ std::tuple<std::string_view /*name*/, int32_t /*source device*/,
+ ControllerType /*PointerController*/, ToolType /*pointer tool type*/,
+ FloatPoint /*source position */, FloatPoint /*hover move X/Y */,
+ ui::LogicalDisplayId /*destination display*/>;
+
+class PointerChoreographerDisplayTopologyTestFixture
+ : public PointerChoreographerTest,
+ public testing::WithParamInterface<PointerChoreographerDisplayTopologyTestFixtureParam> {
+public:
+ static constexpr ui::LogicalDisplayId DISPLAY_CENTER_ID = ui::LogicalDisplayId{10};
+ static constexpr ui::LogicalDisplayId DISPLAY_TOP_ID = ui::LogicalDisplayId{20};
+ static constexpr ui::LogicalDisplayId DISPLAY_RIGHT_ID = ui::LogicalDisplayId{30};
+ static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
+ static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
+
+ PointerChoreographerDisplayTopologyTestFixture() {
+ com::android::input::flags::connected_displays_cursor(true);
+ }
+
+protected:
+ std::vector<DisplayViewport> mViewports{
+ createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100),
+ createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90),
+ createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90),
+ createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90),
+ createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90),
+ };
+
+ std::unordered_map<ui::LogicalDisplayId, std::vector<PointerChoreographer::AdjacentDisplay>>
+ mTopology{
+ {DISPLAY_CENTER_ID,
+ {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 0.0f},
+ {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 0.0f},
+ {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 0.0f},
+ {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 0.0f}}},
+ };
+
+private:
+ DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height) {
+ DisplayViewport viewport;
+ viewport.displayId = displayId;
+ viewport.logicalRight = width;
+ viewport.logicalBottom = height;
+ return viewport;
+ }
+};
+
+TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) {
+ const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove,
+ destinationDisplay] = GetParam();
+
+ mChoreographer.setDisplayViewports(mViewports);
+ mChoreographer.setDefaultMouseDisplayId(
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID);
+ mChoreographer.setDisplayTopology(mTopology);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, device, ui::LogicalDisplayId::INVALID)}});
+
+ auto pc = assertPointerControllerCreated(pointerControllerType);
+ ASSERT_EQ(PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
+ pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(initialPosition.x, initialPosition.y);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ auto pointerBuilder = PointerBuilder(/*id=*/0, pointerToolType)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, hoverMove.x)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, hoverMove.y);
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, device)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ // TODO(b/362719483) assert pointer controller position here
+ ASSERT_TRUE(pc->isPointerShown());
+ ASSERT_EQ(pc->getDisplayId(), destinationDisplay);
+
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
+ // TODO(b/362719483) assert Coords and cursor position here
+ mTestListener.assertNotifyMotionWasCalled(WithDisplayId(destinationDisplay));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture,
+ testing::Values(
+ std::make_tuple("UnchangedDisplay", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE,
+ ToolType::MOUSE, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(25, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID),
+ std::make_tuple("TransitionToRightDisplay", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_RIGHT_ID),
+ std::make_tuple("TransitionToLeftDisplay", AINPUT_SOURCE_MOUSE,
+ ControllerType::MOUSE, ToolType::MOUSE,
+ FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(-100, 25) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_LEFT_ID),
+ std::make_tuple("TransitionToTopDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(25, -100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID),
+ std::make_tuple("TransitionToBottomDisplay",
+ AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
+ ToolType::FINGER, FloatPoint(50, 50) /* initial x/y */,
+ FloatPoint(25, 100) /* delta x/y */,
+ PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID)),
+ [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
TEST_F_WITH_FLAGS(