Skip to content

Commit 3c090f8

Browse files
author
Arpit Singh
committed
[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
1 parent ddb2f99 commit 3c090f8

4 files changed

Lines changed: 79 additions & 31 deletions

File tree

include/input/DisplayTopologyGraph.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ enum class DisplayTopologyPosition : int32_t {
4242
*/
4343
struct DisplayTopologyAdjacentDisplay {
4444
ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID;
45+
// Position of the adjacent display, relative to the source display.
4546
DisplayTopologyPosition position;
47+
// The offset in DP of the adjacent display, relative to the source display.
4648
float offsetDp;
4749
};
4850

services/inputflinger/PointerChoreographer.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define LOG_TAG "PointerChoreographer"
1818

1919
#include <android-base/logging.h>
20+
#include <android/configuration.h>
2021
#include <com_android_input_flags.h>
2122
#if defined(__ANDROID__)
2223
#include <gui/SurfaceComposerClient.h>
@@ -114,6 +115,17 @@ vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationVi
114115
}
115116
}
116117

118+
// The standardised medium display density for which 1 px = 1 dp
119+
constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
120+
121+
inline float pxToDp(int px, int dpi) {
122+
return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi);
123+
}
124+
125+
inline int dpToPx(float dp, int dpi) {
126+
return static_cast<int>((dp * dpi) / DENSITY_MEDIUM);
127+
}
128+
117129
} // namespace
118130

119131
// --- PointerChoreographer ---
@@ -385,8 +397,7 @@ void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterfac
385397
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
386398
pc.setDisplayViewport(destinationViewport);
387399
vec2 destinationPosition =
388-
calculatePositionOnDestinationViewport(destinationViewport,
389-
cursorOffset - destinationOffset,
400+
calculatePositionOnDestinationViewport(destinationViewport, destinationOffset,
390401
sourceBoundary);
391402

392403
// Transform position back to un-rotated coordinate space before sending it to controller
@@ -990,10 +1001,10 @@ PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusContr
9901001
return ConstructorDelegate(std::move(ctor));
9911002
}
9921003

993-
std::optional<std::pair<const DisplayViewport*, float /*offset*/>>
1004+
std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
9941005
PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
9951006
const DisplayTopologyPosition sourceBoundary,
996-
float cursorOffset) const {
1007+
int32_t sourceCursorOffsetPx) const {
9971008
const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
9981009
if (sourceNode == mTopology.graph.end()) {
9991010
// 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
10041015
if (adjacentDisplay.position != sourceBoundary) {
10051016
continue;
10061017
}
1007-
const DisplayViewport* destinationViewport =
1008-
findViewportByIdLocked(adjacentDisplay.displayId);
1009-
if (destinationViewport == nullptr) {
1018+
const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId);
1019+
if (adjacentViewport == nullptr) {
10101020
// Topology is likely out of sync with viewport info, wait for them to be updated
10111021
LOG(WARNING) << "Cannot find viewport for adjacent display "
10121022
<< adjacentDisplay.displayId << "of source display " << sourceDisplayId;
10131023
continue;
10141024
}
1015-
// target position must be within target display boundary
1016-
const int32_t edgeSize = sourceBoundary == DisplayTopologyPosition::TOP ||
1025+
// As displays can have different densities we need to do all calculations in
1026+
// density-independent-pixels a.k.a. dp values.
1027+
const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId);
1028+
const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId);
1029+
const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity);
1030+
const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP ||
10171031
sourceBoundary == DisplayTopologyPosition::BOTTOM
1018-
? (destinationViewport->logicalRight - destinationViewport->logicalLeft)
1019-
: (destinationViewport->logicalBottom - destinationViewport->logicalTop);
1020-
if (cursorOffset >= adjacentDisplay.offsetDp &&
1021-
cursorOffset <= adjacentDisplay.offsetDp + edgeSize) {
1022-
return std::make_pair(destinationViewport, adjacentDisplay.offsetDp);
1032+
? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft)
1033+
: (adjacentViewport->logicalBottom - adjacentViewport->logicalTop);
1034+
const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity);
1035+
// Target position must be within target display boundary.
1036+
// Cursor should also be able to cross displays when only display corners are touching and
1037+
// there may be zero overlapping pixels. To accommodate this we have margin of one pixel
1038+
// around the end of the overlapping edge.
1039+
if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp &&
1040+
sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) {
1041+
const int destinationOffsetPx =
1042+
dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity);
1043+
return std::make_pair(adjacentViewport, destinationOffsetPx);
10231044
}
10241045
}
10251046
return std::nullopt;

services/inputflinger/PointerChoreographer.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,10 @@ class PointerChoreographer : public PointerChoreographerInterface {
163163
void handleUnconsumedDeltaLocked(PointerControllerInterface& pc, const vec2& unconsumedDelta)
164164
REQUIRES(getLock());
165165

166-
std::optional<std::pair<const DisplayViewport*, float /*offset*/>> findDestinationDisplayLocked(
167-
const ui::LogicalDisplayId sourceDisplayId,
168-
const DisplayTopologyPosition sourceBoundary, float cursorOffset) const
169-
REQUIRES(getLock());
166+
std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
167+
findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
168+
const DisplayTopologyPosition sourceBoundary,
169+
int32_t sourceCursorOffsetPx) const REQUIRES(getLock());
170170

171171
/* Topology is initialized with default-constructed value, which is an empty topology. Till we
172172
* receive setDisplayTopology call.

services/inputflinger/tests/PointerChoreographer_test.cpp

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,12 +2617,17 @@ class PointerChoreographerDisplayTopologyTestFixture
26172617
static constexpr ui::LogicalDisplayId DISPLAY_BOTTOM_ID = ui::LogicalDisplayId{40};
26182618
static constexpr ui::LogicalDisplayId DISPLAY_LEFT_ID = ui::LogicalDisplayId{50};
26192619
static constexpr ui::LogicalDisplayId DISPLAY_TOP_RIGHT_CORNER_ID = ui::LogicalDisplayId{60};
2620+
static constexpr ui::LogicalDisplayId DISPLAY_HIGH_DENSITY_ID = ui::LogicalDisplayId{70};
2621+
2622+
static constexpr int DENSITY_MEDIUM = 160;
2623+
static constexpr int DENSITY_HIGH = 320;
26202624

26212625
PointerChoreographerDisplayTopologyTestFixture() {
26222626
com::android::input::flags::connected_displays_cursor(true);
26232627
}
26242628

26252629
protected:
2630+
// Note: viewport size is in pixels and offsets in topology are in dp
26262631
std::vector<DisplayViewport> mViewports{
26272632
createViewport(DISPLAY_CENTER_ID, /*width*/ 100, /*height*/ 100, ui::ROTATION_0),
26282633
createViewport(DISPLAY_TOP_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_0),
@@ -2631,16 +2636,28 @@ class PointerChoreographerDisplayTopologyTestFixture
26312636
createViewport(DISPLAY_LEFT_ID, /*width*/ 90, /*height*/ 90, ui::ROTATION_270),
26322637
createViewport(DISPLAY_TOP_RIGHT_CORNER_ID, /*width*/ 90, /*height*/ 90,
26332638
ui::ROTATION_0),
2639+
// Create a high density display size 100x100 dp i.e. 200x200 px
2640+
createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0),
26342641
};
26352642

2636-
DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID,
2637-
{{DISPLAY_CENTER_ID,
2638-
{{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 10.0f},
2639-
{DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
2640-
{DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
2641-
{DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
2642-
{DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT,
2643-
-90.0f}}}}};
2643+
DisplayTopologyGraph
2644+
mTopology{DISPLAY_CENTER_ID,
2645+
{{DISPLAY_CENTER_ID,
2646+
{{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f},
2647+
// Place a high density display on the left of DISPLAY_TOP_ID with 25 dp
2648+
// gap
2649+
{DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f},
2650+
{DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f},
2651+
{DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f},
2652+
{DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f},
2653+
{DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}},
2654+
{{DISPLAY_CENTER_ID, DENSITY_MEDIUM},
2655+
{DISPLAY_TOP_ID, DENSITY_MEDIUM},
2656+
{DISPLAY_RIGHT_ID, DENSITY_MEDIUM},
2657+
{DISPLAY_BOTTOM_ID, DENSITY_MEDIUM},
2658+
{DISPLAY_LEFT_ID, DENSITY_MEDIUM},
2659+
{DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM},
2660+
{DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}};
26442661

26452662
private:
26462663
DisplayViewport createViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
@@ -2731,19 +2748,20 @@ INSTANTIATE_TEST_SUITE_P(
27312748
ToolType::FINGER, vec2(50, 50) /* initial x/y */,
27322749
vec2(25, -100) /* delta x/y */,
27332750
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_ID,
2734-
vec2(50 + 25 - 10,
2751+
vec2(50 + 25 - 50,
27352752
90) /* Bottom edge: (source + delta - offset, height) */),
27362753
std::make_tuple("TransitionToBottomDisplay",
27372754
AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD, ControllerType::MOUSE,
27382755
ToolType::FINGER, vec2(50, 50) /* initial x/y */,
27392756
vec2(25, 100) /* delta x/y */,
27402757
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_BOTTOM_ID,
27412758
vec2(50 + 25 - 10, 0) /* Top edge: (source + delta - offset, 0) */),
2759+
// move towards 25 dp gap between DISPLAY_HIGH_DENSITY_ID and DISPLAY_TOP_ID
27422760
std::make_tuple("NoTransitionAtTopOffset", AINPUT_SOURCE_MOUSE,
27432761
ControllerType::MOUSE, ToolType::MOUSE,
2744-
vec2(5, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
2762+
vec2(35, 50) /* initial x/y */, vec2(0, -100) /* Move Up */,
27452763
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_CENTER_ID,
2746-
vec2(5, 0) /* Top edge */),
2764+
vec2(35, 0) /* Top edge */),
27472765
std::make_tuple("NoTransitionAtRightOffset", AINPUT_SOURCE_MOUSE,
27482766
ControllerType::MOUSE, ToolType::MOUSE,
27492767
vec2(95, 5) /* initial x/y */, vec2(100, 0) /* Move Right */,
@@ -2764,9 +2782,16 @@ INSTANTIATE_TEST_SUITE_P(
27642782
std::make_tuple(
27652783
"TransitionAtTopRightCorner", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
27662784
ControllerType::MOUSE, ToolType::FINGER, vec2(95, 5) /* initial x/y */,
2767-
vec2(10, -10) /* Move dignally to top right corner */,
2785+
vec2(10, -10) /* Move diagonally to top right corner */,
27682786
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_TOP_RIGHT_CORNER_ID,
2769-
vec2(0, 90) /* bottom left corner */)),
2787+
vec2(0, 90) /* bottom left corner */),
2788+
std::make_tuple(
2789+
"TransitionToHighDpDisplay", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
2790+
ControllerType::MOUSE, ToolType::MOUSE, vec2(20, 20) /* initial x/y */,
2791+
vec2(0, -50) /* delta x/y */,
2792+
PointerChoreographerDisplayTopologyTestFixture::DISPLAY_HIGH_DENSITY_ID,
2793+
/* Bottom edge: ((source + delta - offset) * density, height) */
2794+
vec2((20 + 0 + 75) * 2, 200))),
27702795
[](const testing::TestParamInfo<PointerChoreographerDisplayTopologyTestFixtureParam>& p) {
27712796
return std::string{std::get<0>(p.param)};
27722797
});

0 commit comments

Comments
 (0)