diff options
-rw-r--r-- | include/input/DisplayTopologyGraph.h | 2 | ||||
-rw-r--r-- | services/inputflinger/PointerChoreographer.cpp | 49 | ||||
-rw-r--r-- | services/inputflinger/PointerChoreographer.h | 8 | ||||
-rw-r--r-- | services/inputflinger/tests/PointerChoreographer_test.cpp | 51 |
4 files changed, 79 insertions, 31 deletions
diff --git a/include/input/DisplayTopologyGraph.h b/include/input/DisplayTopologyGraph.h index f3f5148540..3ae865a33a 100644 --- a/include/input/DisplayTopologyGraph.h +++ b/include/input/DisplayTopologyGraph.h @@ -42,7 +42,9 @@ enum class DisplayTopologyPosition : int32_t { */ struct DisplayTopologyAdjacentDisplay { ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID; + // Position of the adjacent display, relative to the source display. DisplayTopologyPosition position; + // The offset in DP of the adjacent display, relative to the source display. float offsetDp; }; diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 9f9128529d..3140dc86e9 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PointerChoreographer" #include <android-base/logging.h> +#include <android/configuration.h> #include <com_android_input_flags.h> #if defined(__ANDROID__) #include <gui/SurfaceComposerClient.h> @@ -114,6 +115,17 @@ vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationVi } } +// The standardised medium display density for which 1 px = 1 dp +constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM; + +inline float pxToDp(int px, int dpi) { + return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi); +} + +inline int dpToPx(float dp, int dpi) { + return static_cast<int>((dp * dpi) / DENSITY_MEDIUM); +} + } // namespace // --- PointerChoreographer --- @@ -385,8 +397,7 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac pc.fade(PointerControllerInterface::Transition::IMMEDIATE); pc.setDisplayViewport(destinationViewport); vec2 destinationPosition = - calculatePositionOnDestinationViewport(destinationViewport, - cursorOffset - destinationOffset, + calculatePositionOnDestinationViewport(destinationViewport, destinationOffset, sourceBoundary); // Transform position back to un-rotated coordinate space before sending it to controller @@ -990,10 +1001,10 @@ PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusContr return ConstructorDelegate(std::move(ctor)); } -std::optional<std::pair<const DisplayViewport*, float /*offset*/>> +std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>> PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId, const DisplayTopologyPosition sourceBoundary, - float cursorOffset) const { + int32_t sourceCursorOffsetPx) const { const auto& sourceNode = mTopology.graph.find(sourceDisplayId); if (sourceNode == mTopology.graph.end()) { // Topology is likely out of sync with viewport info, wait for it to be updated @@ -1004,22 +1015,32 @@ PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId so if (adjacentDisplay.position != sourceBoundary) { continue; } - const DisplayViewport* destinationViewport = - findViewportByIdLocked(adjacentDisplay.displayId); - if (destinationViewport == nullptr) { + const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId); + if (adjacentViewport == 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 " << sourceDisplayId; continue; } - // target position must be within target display boundary - const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP || + // As displays can have different densities we need to do all calculations in + // density-independent-pixels a.k.a. dp values. + const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId); + const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId); + const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity); + const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP || sourceBoundary == DisplayTopologyPosition::BOTTOM - ? (destinationViewport->logicalRight - destinationViewport->logicalLeft) - : (destinationViewport->logicalBottom - destinationViewport->logicalTop); - if (cursorOffset >= adjacentDisplay.offsetDp && - cursorOffset <= adjacentDisplay.offsetDp + edgeSize) { - return std::make_pair(destinationViewport, adjacentDisplay.offsetDp); + ? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft) + : (adjacentViewport->logicalBottom - adjacentViewport->logicalTop); + const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity); + // Target position must be within target display boundary. + // Cursor should also be able to cross displays when only display corners are touching and + // there may be zero overlapping pixels. To accommodate this we have margin of one pixel + // around the end of the overlapping edge. + if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp && + sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) { + const int destinationOffsetPx = + dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity); + return std::make_pair(adjacentViewport, destinationOffsetPx); } } return std::nullopt; diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index c2f5ec0e87..a9d971abb4 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -163,10 +163,10 @@ private: void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta) REQUIRES(getLock()); - std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked( - const ui::LogicalDisplayId sourceDisplayId, - const DisplayTopologyPosition sourceBoundary, float cursorOffset) const - REQUIRES(getLock()); + std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>> + findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId, + const DisplayTopologyPosition sourceBoundary, + int32_t sourceCursorOffsetPx) const REQUIRES(getLock()); /* Topology is initialized with default-constructed value, which is an empty topology. Till we * receive setDisplayTopology call. diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 1ca2998f6b..1286a3675c 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -2617,12 +2617,17 @@ public: 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}; + static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70}; + + static constexpr int DENSITY_MEDIUM = 160; + static constexpr int DENSITY_HIGH = 320; PointerChoreographerDisplayTopologyTestFixture() { com::android::input::flags::connected_displays_cursor(true); } protected: + // Note: viewport size is in pixels and offsets in topology are in dp std::vector<DisplayViewport> mViewports{ createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0), createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0), @@ -2631,16 +2636,28 @@ protected: createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270), createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0), + // Create a high density display size 100x100 dp i.e. 200x200 px + createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0), }; - DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID, - {{DISPLAY_CENTER_ID, - {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f}, - {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f}, - {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f}, - {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f}, - {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, - -90.0f}}}}}; + DisplayTopologyGraph + mTopology{DISPLAY_CENTER_ID, + {{DISPLAY_CENTER_ID, + {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f}, + // Place a high density display on the left of DISPLAY_TOP_ID with 25 dp + // gap + {DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f}, + {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f}, + {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f}, + {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f}, + {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}}, + {{DISPLAY_CENTER_ID, DENSITY_MEDIUM}, + {DISPLAY_TOP_ID, DENSITY_MEDIUM}, + {DISPLAY_RIGHT_ID, DENSITY_MEDIUM}, + {DISPLAY_BOTTOM_ID, DENSITY_MEDIUM}, + {DISPLAY_LEFT_ID, DENSITY_MEDIUM}, + {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM}, + {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}}; private: DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height, @@ -2731,7 +2748,7 @@ INSTANTIATE_TEST_SUITE_P( ToolType::FINGER, vec2(50, 50) /* initial x/y */, vec2(25, -100) /* delta x/y */, PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID, - vec2(50 + 25 - 10, + vec2(50 + 25 - 50, 90) /* Bottom edge: (source + delta - offset, height) */), std::make_tuple("TransitionToBottomDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, @@ -2739,11 +2756,12 @@ INSTANTIATE_TEST_SUITE_P( vec2(25, 100) /* delta x/y */, PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID, vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */), + // move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, ToolType::MOUSE, - vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */, + vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */, PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID, - vec2(5, 0) /* Top edge */), + vec2(35, 0) /* Top edge */), std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, ToolType::MOUSE, vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */, @@ -2764,9 +2782,16 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple( "TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */, - vec2(10, -10) /* Move dignally to top right corner */, + vec2(10, -10) /* Move diagonally to top right corner */, PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID, - vec2(0, 90) /* bottom left corner */)), + vec2(0, 90) /* bottom left corner */), + std::make_tuple( + "TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, + ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */, + vec2(0, -50) /* delta x/y */, + PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID, + /* Bottom edge: ((source + delta - offset) * density, height) */ + vec2((20 + 0 + 75) * 2, 200))), [](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) { return std::string{std::get<0>(p.param)}; }); |