Loading services/inputflinger/PointerChoreographer.cpp +12 −7 Original line number Diff line number Diff line Loading @@ -298,9 +298,7 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg PointerControllerInterface& pc) { const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); vec2 filteredDelta = filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY}, newArgs.displayId); const vec2 filteredDelta = filterPointerMotionForAccessibilityLocked(pc, vec2{deltaX, deltaY}); vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y); if (InputFlags::connectedDisplaysCursorEnabled() && (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) { Loading Loading @@ -1067,18 +1065,25 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so } vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked( const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) { const PointerControllerInterface& pc, const vec2& delta) { if (!mPointerMotionFilterEnabled) { return delta; } std::optional<vec2> filterResult = mPolicy.filterPointerMotionForAccessibility(current, delta, displayId); // PointerController.getPosition and mouse delta are both in physical display coordinates. // PointerMotionFilter in Java expects coordinates in logical display coordinates. const auto& displayTransform = pc.getDisplayTransform(); const vec2 current = displayTransform.transform(pc.getPosition()); const vec2 deltaInDisplay = transformWithoutTranslation(displayTransform, delta); const ui::LogicalDisplayId displayId = pc.getDisplayId(); const std::optional<vec2> filterResult = mPolicy.filterPointerMotionForAccessibility(current, deltaInDisplay, displayId); if (!filterResult.has_value()) { // Disable filter when there's any error. mPointerMotionFilterEnabled = false; return delta; } return *filterResult; return transformWithoutTranslation(displayTransform.inverse(), *filterResult); } // --- PointerChoreographer::PointerChoreographerDisplayInfoListener --- Loading services/inputflinger/PointerChoreographer.h +2 −3 Original line number Diff line number Diff line Loading @@ -182,9 +182,8 @@ private: const DisplayTopologyPosition sourceBoundary, int32_t sourceCursorOffsetPx) const REQUIRES(getLock()); vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) REQUIRES(getLock()); vec2 filterPointerMotionForAccessibilityLocked(const PointerControllerInterface& pc, const vec2& delta) REQUIRES(getLock()); /* Topology is initialized with default-constructed value, which is an empty topology. Till we * receive setDisplayTopology call. Loading services/inputflinger/tests/FakePointerController.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& FakePointerControlle return mSpotsByDisplay; } void FakePointerController::setTransform(ui::Transform transform) { mTransform = transform; } void FakePointerController::setPosition(float x, float y) { if (!mEnabled) return; Loading Loading @@ -196,7 +200,7 @@ void FakePointerController::clearSpots() { } ui::Transform FakePointerController::getDisplayTransform() const { return ui::Transform(); return mTransform; } } // namespace android services/inputflinger/tests/FakePointerController.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ public: void setBounds(float minX, float minY, float maxX, float maxY); void clearBounds(); const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& getSpots(); void setTransform(ui::Transform transform); void setPosition(float x, float y) override; vec2 getPosition() const override; Loading Loading @@ -81,6 +82,7 @@ private: bool mIsPointerShown{false}; std::optional<PointerIconStyle> mIconStyle; std::optional<PointerIconStyle> mCustomIconStyle; ui::Transform mTransform{}; std::map<ui::LogicalDisplayId, std::vector<int32_t>> mSpotsByDisplay; std::unordered_set<ui::LogicalDisplayId> mDisplaysToSkipScreenshot; Loading services/inputflinger/tests/PointerChoreographer_test.cpp +52 −20 Original line number Diff line number Diff line Loading @@ -76,14 +76,21 @@ static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source, return info; } DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width = DISPLAY_WIDTH, int32_t height = DISPLAY_HEIGHT, ui::Rotation orientation = ui::ROTATION_0) { DisplayViewport viewport; viewport.displayId = displayId; viewport.logicalRight = width; viewport.logicalBottom = height; viewport.orientation = orientation; return viewport; } static std::vector<DisplayViewport> createViewports(std::vector<ui::LogicalDisplayId> displayIds) { std::vector<DisplayViewport> viewports; for (auto displayId : displayIds) { DisplayViewport viewport; viewport.displayId = displayId; viewport.logicalRight = DISPLAY_WIDTH; viewport.logicalBottom = DISPLAY_HEIGHT; viewports.push_back(viewport); viewports.push_back(createViewport(displayId)); } return viewports; } Loading Loading @@ -1907,6 +1914,44 @@ TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) { WithRelativeMotion(10, 20))); } TEST_F(PointerChoreographerTest, A11yPointerMotionFilterApplyTransform) { mChoreographer.setDisplayViewports( {createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90)}); setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); pc->setTransform(ui::Transform(ui::Transform::toRotationFlags(ui::ROTATION_90), DISPLAY_HEIGHT, DISPLAY_WIDTH)); pc->setPosition(200, 700); // (100, 200) in the logical display space. mChoreographer.setAccessibilityPointerMotionFilterEnabled(true); // Pointer moves (10, 20) in the physical space, which is (-20, 10) in the logical space. EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}), testing::Eq(vec2{-20.f, 10.f}), testing::Eq(DISPLAY_ID))) .Times(1) .WillOnce(testing::Return(vec2{-12, 6})); mChoreographer.notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) .pointer(MOUSE_POINTER) .deviceId(DEVICE_ID) .displayId(ui::LogicalDisplayId::INVALID) .build()); // Cursor position is decided by filtered delta, but pointer coord's relative values are kept. pc->assertPosition(206, 712); mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(206, 712), WithDisplayId(DISPLAY_ID), WithCursorPosition(206, 712), WithRelativeMotion(10, 20))); } TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) { mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}}); Loading Loading @@ -2744,18 +2789,7 @@ TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCom metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT); } class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest { protected: 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; } }; using PointerChoreographerDisplayTopologyTests = PointerChoreographerTest; using PointerChoreographerDisplayTopologyCursorTestFixtureParam = std::tuple<std::string_view /*name*/, int32_t /*source device*/, Loading Loading @@ -2976,9 +3010,7 @@ protected: static constexpr int32_t DISPLAY_HEIGHT = 100; DisplayViewport createViewport(ui::LogicalDisplayId displayId) { return PointerChoreographerDisplayTopologyTests::createViewport(displayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0); return ::android::createViewport(displayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0); } void setDisplayTopologyWithDisplays( Loading Loading
services/inputflinger/PointerChoreographer.cpp +12 −7 Original line number Diff line number Diff line Loading @@ -298,9 +298,7 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg PointerControllerInterface& pc) { const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); vec2 filteredDelta = filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY}, newArgs.displayId); const vec2 filteredDelta = filterPointerMotionForAccessibilityLocked(pc, vec2{deltaX, deltaY}); vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y); if (InputFlags::connectedDisplaysCursorEnabled() && (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) { Loading Loading @@ -1067,18 +1065,25 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so } vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked( const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) { const PointerControllerInterface& pc, const vec2& delta) { if (!mPointerMotionFilterEnabled) { return delta; } std::optional<vec2> filterResult = mPolicy.filterPointerMotionForAccessibility(current, delta, displayId); // PointerController.getPosition and mouse delta are both in physical display coordinates. // PointerMotionFilter in Java expects coordinates in logical display coordinates. const auto& displayTransform = pc.getDisplayTransform(); const vec2 current = displayTransform.transform(pc.getPosition()); const vec2 deltaInDisplay = transformWithoutTranslation(displayTransform, delta); const ui::LogicalDisplayId displayId = pc.getDisplayId(); const std::optional<vec2> filterResult = mPolicy.filterPointerMotionForAccessibility(current, deltaInDisplay, displayId); if (!filterResult.has_value()) { // Disable filter when there's any error. mPointerMotionFilterEnabled = false; return delta; } return *filterResult; return transformWithoutTranslation(displayTransform.inverse(), *filterResult); } // --- PointerChoreographer::PointerChoreographerDisplayInfoListener --- Loading
services/inputflinger/PointerChoreographer.h +2 −3 Original line number Diff line number Diff line Loading @@ -182,9 +182,8 @@ private: const DisplayTopologyPosition sourceBoundary, int32_t sourceCursorOffsetPx) const REQUIRES(getLock()); vec2 filterPointerMotionForAccessibilityLocked(const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) REQUIRES(getLock()); vec2 filterPointerMotionForAccessibilityLocked(const PointerControllerInterface& pc, const vec2& delta) REQUIRES(getLock()); /* Topology is initialized with default-constructed value, which is an empty topology. Till we * receive setDisplayTopology call. Loading
services/inputflinger/tests/FakePointerController.cpp +5 −1 Original line number Diff line number Diff line Loading @@ -36,6 +36,10 @@ const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& FakePointerControlle return mSpotsByDisplay; } void FakePointerController::setTransform(ui::Transform transform) { mTransform = transform; } void FakePointerController::setPosition(float x, float y) { if (!mEnabled) return; Loading Loading @@ -196,7 +200,7 @@ void FakePointerController::clearSpots() { } ui::Transform FakePointerController::getDisplayTransform() const { return ui::Transform(); return mTransform; } } // namespace android
services/inputflinger/tests/FakePointerController.h +2 −0 Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ public: void setBounds(float minX, float minY, float maxX, float maxY); void clearBounds(); const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& getSpots(); void setTransform(ui::Transform transform); void setPosition(float x, float y) override; vec2 getPosition() const override; Loading Loading @@ -81,6 +82,7 @@ private: bool mIsPointerShown{false}; std::optional<PointerIconStyle> mIconStyle; std::optional<PointerIconStyle> mCustomIconStyle; ui::Transform mTransform{}; std::map<ui::LogicalDisplayId, std::vector<int32_t>> mSpotsByDisplay; std::unordered_set<ui::LogicalDisplayId> mDisplaysToSkipScreenshot; Loading
services/inputflinger/tests/PointerChoreographer_test.cpp +52 −20 Original line number Diff line number Diff line Loading @@ -76,14 +76,21 @@ static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source, return info; } DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width = DISPLAY_WIDTH, int32_t height = DISPLAY_HEIGHT, ui::Rotation orientation = ui::ROTATION_0) { DisplayViewport viewport; viewport.displayId = displayId; viewport.logicalRight = width; viewport.logicalBottom = height; viewport.orientation = orientation; return viewport; } static std::vector<DisplayViewport> createViewports(std::vector<ui::LogicalDisplayId> displayIds) { std::vector<DisplayViewport> viewports; for (auto displayId : displayIds) { DisplayViewport viewport; viewport.displayId = displayId; viewport.logicalRight = DISPLAY_WIDTH; viewport.logicalBottom = DISPLAY_HEIGHT; viewports.push_back(viewport); viewports.push_back(createViewport(displayId)); } return viewports; } Loading Loading @@ -1907,6 +1914,44 @@ TEST_F(PointerChoreographerTest, A11yPointerMotionFilterTouchpad) { WithRelativeMotion(10, 20))); } TEST_F(PointerChoreographerTest, A11yPointerMotionFilterApplyTransform) { mChoreographer.setDisplayViewports( {createViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90)}); setDefaultMouseDisplayId(DISPLAY_ID); mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::LogicalDisplayId::INVALID)}}); auto pc = assertPointerControllerCreated(ControllerType::MOUSE); ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); pc->setTransform(ui::Transform(ui::Transform::toRotationFlags(ui::ROTATION_90), DISPLAY_HEIGHT, DISPLAY_WIDTH)); pc->setPosition(200, 700); // (100, 200) in the logical display space. mChoreographer.setAccessibilityPointerMotionFilterEnabled(true); // Pointer moves (10, 20) in the physical space, which is (-20, 10) in the logical space. EXPECT_CALL(mMockPolicy, filterPointerMotionForAccessibility(testing::Eq(vec2{100, 200}), testing::Eq(vec2{-20.f, 10.f}), testing::Eq(DISPLAY_ID))) .Times(1) .WillOnce(testing::Return(vec2{-12, 6})); mChoreographer.notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) .pointer(MOUSE_POINTER) .deviceId(DEVICE_ID) .displayId(ui::LogicalDisplayId::INVALID) .build()); // Cursor position is decided by filtered delta, but pointer coord's relative values are kept. pc->assertPosition(206, 712); mTestListener.assertNotifyMotionWasCalled(AllOf(WithCoords(206, 712), WithDisplayId(DISPLAY_ID), WithCursorPosition(206, 712), WithRelativeMotion(10, 20))); } TEST_F(PointerChoreographerTest, A11yPointerMotionFilterNotFilterTouch) { mChoreographer.notifyInputDevicesChanged( {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}}); Loading Loading @@ -2744,18 +2789,7 @@ TEST_P(PointerVisibilityAndTouchpadTapStateOnKeyPressTestFixture, TestMetaKeyCom metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT); } class PointerChoreographerDisplayTopologyTests : public PointerChoreographerTest { protected: 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; } }; using PointerChoreographerDisplayTopologyTests = PointerChoreographerTest; using PointerChoreographerDisplayTopologyCursorTestFixtureParam = std::tuple<std::string_view /*name*/, int32_t /*source device*/, Loading Loading @@ -2976,9 +3010,7 @@ protected: static constexpr int32_t DISPLAY_HEIGHT = 100; DisplayViewport createViewport(ui::LogicalDisplayId displayId) { return PointerChoreographerDisplayTopologyTests::createViewport(displayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0); return ::android::createViewport(displayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0); } void setDisplayTopologyWithDisplays( Loading