diff options
6 files changed, 207 insertions, 93 deletions
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 2434d2badb..8eb6bdd02f 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -313,30 +313,89 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const FloatPoint& unconsumedDelta) { + // Display topology is in rotated coordinate space and Pointer controller returns and expects + // values in the un-rotated coordinate space. So we need to transform delta and cursor position + // back to the rotated coordinate space to lookup adjacent display in the display topology. + const auto& sourceDisplayTransform = pc.getDisplayTransform(); + const vec2 rotatedUnconsumedDelta = + transformWithoutTranslation(sourceDisplayTransform, + {unconsumedDelta.x, unconsumedDelta.y}); + const FloatPoint cursorPosition = pc.getPosition(); + const vec2 rotatedCursorPosition = + sourceDisplayTransform.transform(cursorPosition.x, cursorPosition.y); + + // To find out the boundary that cursor is crossing we are checking delta in x and y direction + // respectively. This prioritizes x direction over y. + // In practise, majority of cases we only have non-zero values in either x or y coordinates, + // except sometimes near the corners. + // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on + // the destination display for the same reason. + DisplayPosition sourceBoundary; + float cursorOffset = 0.0f; + if (rotatedUnconsumedDelta.x > 0) { + sourceBoundary = DisplayPosition::RIGHT; + cursorOffset = rotatedCursorPosition.y; + } else if (rotatedUnconsumedDelta.x < 0) { + sourceBoundary = DisplayPosition::LEFT; + cursorOffset = rotatedCursorPosition.y; + } else if (rotatedUnconsumedDelta.y > 0) { + sourceBoundary = DisplayPosition::BOTTOM; + cursorOffset = rotatedCursorPosition.x; + } else { + sourceBoundary = DisplayPosition::TOP; + cursorOffset = rotatedCursorPosition.x; + } + const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId(); - const auto& sourceViewport = *findViewportByIdLocked(sourceDisplayId); - std::optional<const DisplayViewport*> destination = - findDestinationDisplayLocked(sourceViewport, unconsumedDelta); - if (!destination) { - // no adjacent display + std::optional<std::pair<const DisplayViewport*, float /*offset*/>> destination = + findDestinationDisplayLocked(sourceDisplayId, sourceBoundary, cursorOffset); + if (!destination.has_value()) { + // No matching adjacent display return; } - const DisplayViewport* destinationViewport = *destination; - - if (mMousePointersByDisplay.find(destinationViewport->displayId) != + const DisplayViewport& destinationViewport = *destination->first; + const float destinationOffset = destination->second; + if (mMousePointersByDisplay.find(destinationViewport.displayId) != mMousePointersByDisplay.end()) { LOG(FATAL) << "A cursor already exists on destination display" - << destinationViewport->displayId; + << destinationViewport.displayId; } - mDefaultMouseDisplayId = destinationViewport->displayId; + mDefaultMouseDisplayId = destinationViewport.displayId; auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId); - pcNode.key() = destinationViewport->displayId; + pcNode.key() = destinationViewport.displayId; mMousePointersByDisplay.insert(std::move(pcNode)); - // This will place cursor at the center of the target display for now - // TODO (b/367660694) place the cursor at the appropriate position in destination display - pc.setDisplayViewport(*destinationViewport); + // Before updating the viewport and moving the cursor to appropriate location in the destination + // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the + // center of the display in any intermediate frames. + pc.fade(PointerControllerInterface::Transition::IMMEDIATE); + pc.setDisplayViewport(destinationViewport); + vec2 destinationPosition = + calculateDestinationPosition(destinationViewport, cursorOffset - destinationOffset, + sourceBoundary); + + // Transform position back to un-rotated coordinate space before sending it to controller + destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x, + destinationPosition.y); + pc.setPosition(destinationPosition.x, destinationPosition.y); + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); +} + +vec2 PointerChoreographer::calculateDestinationPosition(const DisplayViewport& destinationViewport, + float pointerOffset, + DisplayPosition sourceBoundary) { + // destination is opposite of the source boundary + switch (sourceBoundary) { + case DisplayPosition::RIGHT: + return {0, pointerOffset}; // left edge + case DisplayPosition::TOP: + return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge + case DisplayPosition::LEFT: + return {destinationViewport.logicalRight, pointerOffset}; // right edge + case DisplayPosition::BOTTOM: + return {pointerOffset, 0}; // top edge + } } void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) { @@ -951,10 +1010,11 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked( // create a fake topology assuming following order // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ... - // ┌─────────┬─────────┐ - // │ next │ next 2 │ ... - // ├─────────┼─────────┘ - // │ default │ + // This also adds a 100px offset on corresponding edge for better manual testing + // ┌────────┐ + // │ next ├─────────┐ + // ┌─└───────┐┤ next 2 │ ... + // │ default │└─────────┘ // └─────────┘ mTopology.clear(); @@ -965,13 +1025,15 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked( continue; } if (previousDisplay == ui::LogicalDisplayId::DEFAULT) { - mTopology[previousDisplay].push_back({displayInfo.displayId, DisplayPosition::TOP, 0}); + mTopology[previousDisplay].push_back( + {displayInfo.displayId, DisplayPosition::TOP, 100}); mTopology[displayInfo.displayId].push_back( - {previousDisplay, DisplayPosition::BOTTOM, 0}); + {previousDisplay, DisplayPosition::BOTTOM, -100}); } else { mTopology[previousDisplay].push_back( - {displayInfo.displayId, DisplayPosition::RIGHT, 0}); - mTopology[displayInfo.displayId].push_back({previousDisplay, DisplayPosition::LEFT, 0}); + {displayInfo.displayId, DisplayPosition::RIGHT, 100}); + mTopology[displayInfo.displayId].push_back( + {previousDisplay, DisplayPosition::LEFT, -100}); } previousDisplay = displayInfo.displayId; } @@ -982,34 +1044,17 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked( } } -std::optional<const DisplayViewport*> PointerChoreographer::findDestinationDisplayLocked( - const DisplayViewport& sourceViewport, const FloatPoint& unconsumedDelta) const { - DisplayPosition sourceBoundary; - if (unconsumedDelta.x > 0) { - sourceBoundary = DisplayPosition::RIGHT; - } else if (unconsumedDelta.x < 0) { - sourceBoundary = DisplayPosition::LEFT; - } else if (unconsumedDelta.y > 0) { - sourceBoundary = DisplayPosition::BOTTOM; - } else { - sourceBoundary = DisplayPosition::TOP; - } - - // Choreographer works in un-rotate coordinate space so we need to rotate boundary by viewport - // orientation to find the rotated boundary - constexpr int MOD = ftl::to_underlying(ui::Rotation::ftl_last) + 1; - sourceBoundary = static_cast<DisplayPosition>( - (ftl::to_underlying(sourceBoundary) + ftl::to_underlying(sourceViewport.orientation)) % - MOD); - - const auto& destination = mTopology.find(sourceViewport.displayId); - if (destination == mTopology.end()) { +std::optional<std::pair<const DisplayViewport*, float /*offset*/>> +PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId, + const DisplayPosition sourceBoundary, + float cursorOffset) const { + const auto& sourceNode = mTopology.find(sourceDisplayId); + if (sourceNode == mTopology.end()) { // Topology is likely out of sync with viewport info, wait for it to be updated - LOG(WARNING) << "Source display missing from topology " << sourceViewport.displayId; + LOG(WARNING) << "Source display missing from topology " << sourceDisplayId; return std::nullopt; } - - for (const auto& adjacentDisplay : destination->second) { + for (const AdjacentDisplay& adjacentDisplay : sourceNode->second) { if (adjacentDisplay.position != sourceBoundary) { continue; } @@ -1018,11 +1063,18 @@ std::optional<const DisplayViewport*> PointerChoreographer::findDestinationDispl if (destinationViewport == nullptr) { // Topology is likely out of sync with viewport info, wait for them to be updated LOG(WARNING) << "Cannot find viewport for adjacent display " - << adjacentDisplay.displayId << "of source display " - << sourceViewport.displayId; - break; + << adjacentDisplay.displayId << "of source display " << sourceDisplayId; + continue; + } + // target position must be within target display boundary + const int32_t edgeSize = + sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM + ? (destinationViewport->logicalRight - destinationViewport->logicalLeft) + : (destinationViewport->logicalBottom - destinationViewport->logicalTop); + if (cursorOffset >= adjacentDisplay.offsetPx && + cursorOffset <= adjacentDisplay.offsetPx + edgeSize) { + return std::make_pair(destinationViewport, adjacentDisplay.offsetPx); } - return destinationViewport; } return std::nullopt; } diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index a9699351f4..4ca7323bfc 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -114,11 +114,11 @@ public: void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; // TODO(b/362719483) remove these when real topology is available - enum class DisplayPosition : int32_t { - RIGHT = 0, - TOP = 1, - LEFT = 2, - BOTTOM = 3, + enum class DisplayPosition { + RIGHT, + TOP, + LEFT, + BOTTOM, ftl_last = BOTTOM, }; @@ -177,9 +177,12 @@ private: void populateFakeDisplayTopologyLocked(const std::vector<gui::DisplayInfo>& displayInfos) REQUIRES(getLock()); - std::optional<const DisplayViewport*> findDestinationDisplayLocked( - const DisplayViewport& sourceViewport, const FloatPoint& unconsumedDelta) const - REQUIRES(getLock()); + std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked( + const ui::LogicalDisplayId sourceDisplayId, const DisplayPosition sourceBoundary, + float cursorOffset) const REQUIRES(getLock()); + + static vec2 calculateDestinationPosition(const DisplayViewport& destinationViewport, + float pointerOffset, DisplayPosition sourceBoundary); std::unordered_map<ui::LogicalDisplayId, std::vector<AdjacentDisplay>> mTopology GUARDED_BY(getLock()); diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 3391f794b1..abca209cec 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -73,7 +73,7 @@ public: virtual std::string dump() = 0; /* Move the pointer and return unconsumed delta if the pointer has crossed the current - * viewport bounds . + * viewport bounds. * * Return value may be used to move pointer to corresponding adjacent display, if it exists in * the display-topology */ @@ -149,6 +149,8 @@ public: /* Resets the flag to skip screenshot of the pointer indicators for all displays. */ virtual void clearSkipScreenshotFlags() = 0; + + virtual ui::Transform getDisplayTransform() const = 0; }; } // namespace android diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index c1606a7ca5..f53e63ba25 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -195,4 +195,8 @@ void FakePointerController::clearSpots() { mSpotsByDisplay.clear(); } +ui::Transform FakePointerController::getDisplayTransform() const { + return ui::Transform(); +} + } // namespace android diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 04adff8234..0ee3123d35 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -48,6 +48,7 @@ public: void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override; void clearSkipScreenshotFlags() override; void fade(Transition) override; + ui::Transform getDisplayTransform() const override; void assertViewportSet(ui::LogicalDisplayId displayId); void assertViewportNotSet(); diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index f427658d75..453c156665 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -2604,8 +2604,9 @@ TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCom 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*/>; + FloatPoint /*source position*/, FloatPoint /*hover move X/Y*/, + ui::LogicalDisplayId /*destination display*/, + FloatPoint /*destination position*/>; class PointerChoreographerDisplayTopologyTestFixture : public PointerChoreographerTest, @@ -2616,6 +2617,7 @@ public: 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}; + static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60}; PointerChoreographerDisplayTopologyTestFixture() { com::android::input::flags::connected_displays_cursor(true); @@ -2623,35 +2625,41 @@ public: 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), + createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0), + createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0), + createViewport(DISPLAY_RIGHT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_90), + createViewport(DISPLAY_BOTTOM_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_180), + createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270), + createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90, + ui::ROTATION_0), }; 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}}}, + {{DISPLAY_TOP_ID, PointerChoreographer::DisplayPosition::TOP, 10.0f}, + {DISPLAY_RIGHT_ID, PointerChoreographer::DisplayPosition::RIGHT, 10.0f}, + {DISPLAY_BOTTOM_ID, PointerChoreographer::DisplayPosition::BOTTOM, 10.0f}, + {DISPLAY_LEFT_ID, PointerChoreographer::DisplayPosition::LEFT, 10.0f}, + {DISPLAY_TOP_RIGHT_CORNER_ID, PointerChoreographer::DisplayPosition::RIGHT, + -90.0f}}}, }; private: - DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height) { + DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, + ui::Rotation orientation) { DisplayViewport viewport; viewport.displayId = displayId; viewport.logicalRight = width; viewport.logicalBottom = height; + viewport.orientation = orientation; return viewport; } }; TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDisplayTopologyTest) { const auto& [_, device, pointerControllerType, pointerToolType, initialPosition, hoverMove, - destinationDisplay] = GetParam(); + destinationDisplay, destinationPosition] = GetParam(); mChoreographer.setDisplayViewports(mViewports); mChoreographer.setDefaultMouseDisplayId( @@ -2681,42 +2689,86 @@ TEST_P(PointerChoreographerDisplayTopologyTestFixture, PointerChoreographerDispl .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); + auto position = pc->getPosition(); + ASSERT_EQ(position.x, destinationPosition.x); + ASSERT_EQ(position.y, destinationPosition.y); // 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)); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithCoords(destinationPosition.x, destinationPosition.y), + WithDisplayId(destinationDisplay), + WithCursorPosition(destinationPosition.x, destinationPosition.y))); } INSTANTIATE_TEST_SUITE_P( PointerChoreographerTest, PointerChoreographerDisplayTopologyTestFixture, testing::Values( + // Note: Upon viewport transition cursor will be positioned at the boundary of the + // destination, as we drop any unconsumed delta. 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, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + FloatPoint(75, 75) /* destination x/y */), + 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, + FloatPoint(0, 50 + 25 - 10) /* Left edge: (0, source + delta - offset) */), + 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, + FloatPoint(90, + 50 + 25 - 10) /* Right edge: (width, source + delta - offset*/), + 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, + FloatPoint(50 + 25 - 10, + 90) /* Bottom edge: (source + delta - offset, height) */), + 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, + FloatPoint(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */), + std::make_tuple("NoTransitionAtTopOffset", 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, + FloatPoint(5, 50) /* initial x/y */, + FloatPoint(0, -100) /* Move Up */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + FloatPoint(5, 0) /* Top edge */), + std::make_tuple("NoTransitionAtRightOffset", 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", + FloatPoint(95, 5) /* initial x/y */, + FloatPoint(100, 0) /* Move Right */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + FloatPoint(99, 5) /* Top edge */), + std::make_tuple("NoTransitionAtBottomOffset", 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", + ToolType::FINGER, FloatPoint(5, 95) /* initial x/y */, + FloatPoint(0, 100) /* Move Down */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + FloatPoint(5, 99) /* Bottom edge */), + std::make_tuple("NoTransitionAtLeftOffset", 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)), + ToolType::FINGER, FloatPoint(5, 5) /* initial x/y */, + FloatPoint(-100, 0) /* Move Left */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, + FloatPoint(0, 5) /* Left edge */), + std::make_tuple( + "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::FINGER, + FloatPoint(95, 5) /* initial x/y */, + FloatPoint(10, -10) /* Move dignally to top right corner */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID, + FloatPoint(0, 90) /* bottom left corner */)), [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) { return std::string{std::get<0>(p.param)}; }); |