Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c090f80 authored by Arpit Singh's avatar Arpit Singh
Browse files

[CD cursor] Adjust for density when moving cursor between displays

Connected displays may have different densities we need to adjust our
calculations to consider this when cursor moves between displays.

Test: atest inputflinger_tests
Bug: 367660694
Flag: com.android.input.flags.connected_displays_cursor
Change-Id: Iecc262cf2efdda3249aff69d5ac157b4c434a575
parent ddb2f99e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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;
};

+35 −14
Original line number Diff line number Diff line
@@ -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;
+4 −4
Original line number Diff line number Diff line
@@ -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.
+38 −13
Original line number Diff line number Diff line
@@ -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,
    DisplayTopologyGraph
            mTopology{DISPLAY_CENTER_ID,
                      {{DISPLAY_CENTER_ID,
                                     {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f},
                        {{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_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)};
        });