Loading libs/input/MouseCursorController.cpp +21 −11 Original line number Diff line number Diff line Loading @@ -28,12 +28,14 @@ #define INDENT " " #define INDENT2 " " namespace android { namespace { // Time to spend fading out the pointer completely. const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms } // namespace namespace android { } // namespace // --- MouseCursorController --- Loading Loading @@ -64,17 +66,21 @@ MouseCursorController::~MouseCursorController() { mLocked.pointerSprite.clear(); } void MouseCursorController::move(float deltaX, float deltaY) { vec2 MouseCursorController::move(float deltaX, float deltaY) { #if DEBUG_MOUSE_CURSOR_UPDATES ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); #endif if (deltaX == 0.0f && deltaY == 0.0f) { return; return {}; } // When transition occurs, the MouseCursorController object may or may not be deleted, depending // if there's another display on the other side of the transition. At this point we still need // to move the cursor to the boundary. std::scoped_lock lock(mLock); setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); const vec2 pos{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY}; setPositionLocked(pos.x, pos.y); return vec2{pos.x - mLocked.pointerX, pos.y - mLocked.pointerY}; } void MouseCursorController::setPosition(float x, float y) { Loading @@ -82,22 +88,26 @@ void MouseCursorController::setPosition(float x, float y) { ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); #endif std::scoped_lock lock(mLock); setPositionLocked(x, y); } void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { const auto& v = mLocked.viewport; if (!v.isValid()) return; FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) { // The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside // the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily // close to the outside edge. const FloatRect bounds{ return FloatRect{ static_cast<float>(mLocked.viewport.logicalLeft), static_cast<float>(mLocked.viewport.logicalTop), static_cast<float>(mLocked.viewport.logicalRight - 1), static_cast<float>(mLocked.viewport.logicalBottom - 1), }; } void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { const auto& v = mLocked.viewport; if (!v.isValid()) return; const FloatRect bounds = getBoundsLocked(); mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x)); mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y)); Loading libs/input/MouseCursorController.h +3 −4 Original line number Diff line number Diff line Loading @@ -20,9 +20,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> #include <functional> #include <map> Loading @@ -43,7 +40,8 @@ public: MouseCursorController(PointerControllerContext& context); ~MouseCursorController(); void move(float deltaX, float deltaY); // Move the pointer and return unconsumed delta vec2 move(float deltaX, float deltaY); void setPosition(float x, float y); FloatPoint getPosition() const; ui::LogicalDisplayId getDisplayId() const; Loading Loading @@ -113,6 +111,7 @@ private: bool doFadingAnimationLocked(nsecs_t timestamp); void startAnimationLocked(); FloatRect getBoundsLocked(); }; } // namespace android Loading libs/input/PointerController.cpp +8 −5 Original line number Diff line number Diff line Loading @@ -138,15 +138,18 @@ std::mutex& PointerController::getLock() const { return mDisplayInfoListener->mLock; } void PointerController::move(float deltaX, float deltaY) { vec2 PointerController::move(float deltaX, float deltaY) { const ui::LogicalDisplayId displayId = mCursorController.getDisplayId(); vec2 transformed; ui::Transform transform; { std::scoped_lock lock(getLock()); const auto& transform = getTransformForDisplayLocked(displayId); transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); transform = getTransformForDisplayLocked(displayId); } mCursorController.move(transformed.x, transformed.y); vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); vec2 unconsumedDelta = mCursorController.move(transformed.x, transformed.y); return transformWithoutTranslation(transform.inverse(), unconsumedDelta); } void PointerController::setPosition(float x, float y) { Loading libs/input/PointerController.h +2 −2 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ public: ~PointerController() override; void move(float deltaX, float deltaY) override; vec2 move(float deltaX, float deltaY) override; void setPosition(float x, float y) override; FloatPoint getPosition() const override; ui::LogicalDisplayId getDisplayId() const override; Loading Loading @@ -165,7 +165,7 @@ public: ~TouchPointerController() override; void move(float, float) override { vec2 move(float, float) override { LOG_ALWAYS_FATAL("Should not be called"); } void setPosition(float, float) override { Loading libs/input/tests/PointerController_test.cpp +131 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ enum TestCursorType { CURSOR_TYPE_CUSTOM = -1, }; static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; using ::testing::AllOf; using ::testing::Field; using ::testing::NiceMock; Loading Loading @@ -399,6 +401,135 @@ INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest, testing::Values(PointerControllerInterface::ControllerType::MOUSE, PointerControllerInterface::ControllerType::STYLUS)); class MousePointerControllerTest : public PointerControllerTest { protected: MousePointerControllerTest() { sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>); EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite)); // create a mouse pointer controller mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController, PointerControllerInterface::ControllerType::MOUSE); // set display viewport DisplayViewport viewport; viewport.displayId = ui::LogicalDisplayId::DEFAULT; viewport.logicalRight = 5; viewport.logicalBottom = 5; viewport.physicalRight = 5; viewport.physicalBottom = 5; viewport.deviceWidth = 5; viewport.deviceHeight = 5; mPointerController->setDisplayViewport(viewport); } }; struct MousePointerControllerTestParam { vec2 startPosition; vec2 delta; vec2 endPosition; vec2 unconsumedDelta; }; class PointerControllerViewportTransitionTest : public MousePointerControllerTest, public testing::WithParamInterface<MousePointerControllerTestParam> {}; TEST_P(PointerControllerViewportTransitionTest, testPointerViewportTransition) { const auto& params = GetParam(); mPointerController->setPosition(params.startPosition.x, params.startPosition.y); auto unconsumedDelta = mPointerController->move(params.delta.x, params.delta.y); auto position = mPointerController->getPosition(); EXPECT_NEAR(position.x, params.endPosition.x, EPSILON); EXPECT_NEAR(position.y, params.endPosition.y, EPSILON); EXPECT_NEAR(unconsumedDelta.x, params.unconsumedDelta.x, EPSILON); EXPECT_NEAR(unconsumedDelta.y, params.unconsumedDelta.y, EPSILON); } INSTANTIATE_TEST_SUITE_P(PointerControllerViewportTransitionTest, PointerControllerViewportTransitionTest, testing::Values( // no transition MousePointerControllerTestParam{{2.0f, 2.0f}, {2.0f, 2.0f}, {4.0f, 4.0f}, {0.0f, 0.0f}}, // right boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 0.0f}, {4.0f, 2.0f}, {1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, -1.0f}, {4.0f, 1.0f}, {1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 1.0f}, {4.0f, 3.0f}, {1.0f, 0.0f}}, // left boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 0.0f}, {0.0f, 2.0f}, {-1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, -1.0f}, {0.0f, 1.0f}, {-1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 1.0f}, {0.0f, 3.0f}, {-1.0f, 0.0f}}, // bottom boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {0.0f, 3.0f}, {2.0f, 4.0f}, {0.0f, 1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-1.0f, 3.0f}, {1.0f, 4.0f}, {0.0f, 1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {1.0f, 3.0f}, {3.0f, 4.0f}, {0.0f, 1.0f}}, // top boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {0.0f, -3.0f}, {2.0f, 0.0f}, {0.0f, -1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-1.0f, -3.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {1.0f, -3.0f}, {3.0f, 0.0f}, {0.0f, -1.0f}}, // top-left corner MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, -3.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}}, // top-right corner MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, -3.0f}, {4.0f, 0.0f}, {1.0f, -1.0f}}, // bottom-right corner MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 3.0f}, {4.0f, 4.0f}, {1.0f, 1.0f}}, // bottom-left corner MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 3.0f}, {0.0f, 4.0f}, {-1.0f, 1.0f}})); class PointerControllerWindowInfoListenerTest : public Test {}; TEST_F(PointerControllerWindowInfoListenerTest, Loading Loading
libs/input/MouseCursorController.cpp +21 −11 Original line number Diff line number Diff line Loading @@ -28,12 +28,14 @@ #define INDENT " " #define INDENT2 " " namespace android { namespace { // Time to spend fading out the pointer completely. const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms } // namespace namespace android { } // namespace // --- MouseCursorController --- Loading Loading @@ -64,17 +66,21 @@ MouseCursorController::~MouseCursorController() { mLocked.pointerSprite.clear(); } void MouseCursorController::move(float deltaX, float deltaY) { vec2 MouseCursorController::move(float deltaX, float deltaY) { #if DEBUG_MOUSE_CURSOR_UPDATES ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); #endif if (deltaX == 0.0f && deltaY == 0.0f) { return; return {}; } // When transition occurs, the MouseCursorController object may or may not be deleted, depending // if there's another display on the other side of the transition. At this point we still need // to move the cursor to the boundary. std::scoped_lock lock(mLock); setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); const vec2 pos{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY}; setPositionLocked(pos.x, pos.y); return vec2{pos.x - mLocked.pointerX, pos.y - mLocked.pointerY}; } void MouseCursorController::setPosition(float x, float y) { Loading @@ -82,22 +88,26 @@ void MouseCursorController::setPosition(float x, float y) { ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); #endif std::scoped_lock lock(mLock); setPositionLocked(x, y); } void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { const auto& v = mLocked.viewport; if (!v.isValid()) return; FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) { // The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside // the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily // close to the outside edge. const FloatRect bounds{ return FloatRect{ static_cast<float>(mLocked.viewport.logicalLeft), static_cast<float>(mLocked.viewport.logicalTop), static_cast<float>(mLocked.viewport.logicalRight - 1), static_cast<float>(mLocked.viewport.logicalBottom - 1), }; } void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { const auto& v = mLocked.viewport; if (!v.isValid()) return; const FloatRect bounds = getBoundsLocked(); mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x)); mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y)); Loading
libs/input/MouseCursorController.h +3 −4 Original line number Diff line number Diff line Loading @@ -20,9 +20,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> #include <functional> #include <map> Loading @@ -43,7 +40,8 @@ public: MouseCursorController(PointerControllerContext& context); ~MouseCursorController(); void move(float deltaX, float deltaY); // Move the pointer and return unconsumed delta vec2 move(float deltaX, float deltaY); void setPosition(float x, float y); FloatPoint getPosition() const; ui::LogicalDisplayId getDisplayId() const; Loading Loading @@ -113,6 +111,7 @@ private: bool doFadingAnimationLocked(nsecs_t timestamp); void startAnimationLocked(); FloatRect getBoundsLocked(); }; } // namespace android Loading
libs/input/PointerController.cpp +8 −5 Original line number Diff line number Diff line Loading @@ -138,15 +138,18 @@ std::mutex& PointerController::getLock() const { return mDisplayInfoListener->mLock; } void PointerController::move(float deltaX, float deltaY) { vec2 PointerController::move(float deltaX, float deltaY) { const ui::LogicalDisplayId displayId = mCursorController.getDisplayId(); vec2 transformed; ui::Transform transform; { std::scoped_lock lock(getLock()); const auto& transform = getTransformForDisplayLocked(displayId); transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); transform = getTransformForDisplayLocked(displayId); } mCursorController.move(transformed.x, transformed.y); vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); vec2 unconsumedDelta = mCursorController.move(transformed.x, transformed.y); return transformWithoutTranslation(transform.inverse(), unconsumedDelta); } void PointerController::setPosition(float x, float y) { Loading
libs/input/PointerController.h +2 −2 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ public: ~PointerController() override; void move(float deltaX, float deltaY) override; vec2 move(float deltaX, float deltaY) override; void setPosition(float x, float y) override; FloatPoint getPosition() const override; ui::LogicalDisplayId getDisplayId() const override; Loading Loading @@ -165,7 +165,7 @@ public: ~TouchPointerController() override; void move(float, float) override { vec2 move(float, float) override { LOG_ALWAYS_FATAL("Should not be called"); } void setPosition(float, float) override { Loading
libs/input/tests/PointerController_test.cpp +131 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,8 @@ enum TestCursorType { CURSOR_TYPE_CUSTOM = -1, }; static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; using ::testing::AllOf; using ::testing::Field; using ::testing::NiceMock; Loading Loading @@ -399,6 +401,135 @@ INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest, testing::Values(PointerControllerInterface::ControllerType::MOUSE, PointerControllerInterface::ControllerType::STYLUS)); class MousePointerControllerTest : public PointerControllerTest { protected: MousePointerControllerTest() { sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>); EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite)); // create a mouse pointer controller mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController, PointerControllerInterface::ControllerType::MOUSE); // set display viewport DisplayViewport viewport; viewport.displayId = ui::LogicalDisplayId::DEFAULT; viewport.logicalRight = 5; viewport.logicalBottom = 5; viewport.physicalRight = 5; viewport.physicalBottom = 5; viewport.deviceWidth = 5; viewport.deviceHeight = 5; mPointerController->setDisplayViewport(viewport); } }; struct MousePointerControllerTestParam { vec2 startPosition; vec2 delta; vec2 endPosition; vec2 unconsumedDelta; }; class PointerControllerViewportTransitionTest : public MousePointerControllerTest, public testing::WithParamInterface<MousePointerControllerTestParam> {}; TEST_P(PointerControllerViewportTransitionTest, testPointerViewportTransition) { const auto& params = GetParam(); mPointerController->setPosition(params.startPosition.x, params.startPosition.y); auto unconsumedDelta = mPointerController->move(params.delta.x, params.delta.y); auto position = mPointerController->getPosition(); EXPECT_NEAR(position.x, params.endPosition.x, EPSILON); EXPECT_NEAR(position.y, params.endPosition.y, EPSILON); EXPECT_NEAR(unconsumedDelta.x, params.unconsumedDelta.x, EPSILON); EXPECT_NEAR(unconsumedDelta.y, params.unconsumedDelta.y, EPSILON); } INSTANTIATE_TEST_SUITE_P(PointerControllerViewportTransitionTest, PointerControllerViewportTransitionTest, testing::Values( // no transition MousePointerControllerTestParam{{2.0f, 2.0f}, {2.0f, 2.0f}, {4.0f, 4.0f}, {0.0f, 0.0f}}, // right boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 0.0f}, {4.0f, 2.0f}, {1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, -1.0f}, {4.0f, 1.0f}, {1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 1.0f}, {4.0f, 3.0f}, {1.0f, 0.0f}}, // left boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 0.0f}, {0.0f, 2.0f}, {-1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, -1.0f}, {0.0f, 1.0f}, {-1.0f, 0.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 1.0f}, {0.0f, 3.0f}, {-1.0f, 0.0f}}, // bottom boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {0.0f, 3.0f}, {2.0f, 4.0f}, {0.0f, 1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-1.0f, 3.0f}, {1.0f, 4.0f}, {0.0f, 1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {1.0f, 3.0f}, {3.0f, 4.0f}, {0.0f, 1.0f}}, // top boundary MousePointerControllerTestParam{{2.0f, 2.0f}, {0.0f, -3.0f}, {2.0f, 0.0f}, {0.0f, -1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {-1.0f, -3.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}}, MousePointerControllerTestParam{{2.0f, 2.0f}, {1.0f, -3.0f}, {3.0f, 0.0f}, {0.0f, -1.0f}}, // top-left corner MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, -3.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}}, // top-right corner MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, -3.0f}, {4.0f, 0.0f}, {1.0f, -1.0f}}, // bottom-right corner MousePointerControllerTestParam{{2.0f, 2.0f}, {3.0f, 3.0f}, {4.0f, 4.0f}, {1.0f, 1.0f}}, // bottom-left corner MousePointerControllerTestParam{{2.0f, 2.0f}, {-3.0f, 3.0f}, {0.0f, 4.0f}, {-1.0f, 1.0f}})); class PointerControllerWindowInfoListenerTest : public Test {}; TEST_F(PointerControllerWindowInfoListenerTest, Loading