Skip to content

Commit c2516b6

Browse files
Arpit SinghAndroid (Google) Code Review
authored andcommitted
Merge "[3/n CD Cursor] Enable cross-display mouse gestures" into main
2 parents a682f07 + 7a6bfc5 commit c2516b6

4 files changed

Lines changed: 147 additions & 20 deletions

File tree

services/inputflinger/dispatcher/InputDispatcher.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@ constexpr int LOGTAG_INPUT_INTERACTION = 62000;
165165
constexpr int LOGTAG_INPUT_FOCUS = 62001;
166166
constexpr int LOGTAG_INPUT_CANCEL = 62003;
167167

168-
static const bool USE_TOPOLOGY = com::android::input::flags::connected_displays_cursor();
169-
170168
const ui::Transform kIdentityTransform;
171169

172170
inline nsecs_t now() {
@@ -2398,7 +2396,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
23982396
// Copy current touch state into tempTouchState.
23992397
// This state will be used to update saved touch state at the end of this function.
24002398
// If no state for the specified display exists, then our initial state will be empty.
2401-
const TouchState* oldState = getTouchStateForMotionEntry(entry);
2399+
const TouchState* oldState = getTouchStateForMotionEntry(entry, windowInfos);
24022400
TouchState tempTouchState;
24032401
if (oldState != nullptr) {
24042402
tempTouchState = *oldState;
@@ -2787,9 +2785,9 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
27872785
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
27882786
if (displayId >= ui::LogicalDisplayId::DEFAULT) {
27892787
tempTouchState.clearWindowsWithoutPointers();
2790-
saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
2788+
saveTouchStateForMotionEntry(entry, std::move(tempTouchState), windowInfos);
27912789
} else {
2792-
eraseTouchStateForMotionEntry(entry);
2790+
eraseTouchStateForMotionEntry(entry, windowInfos);
27932791
}
27942792
}
27952793

@@ -5182,6 +5180,14 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
51825180
: getDisplayTransform(windowInfo.displayId);
51835181
}
51845182

5183+
ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId(
5184+
ui::LogicalDisplayId displayId) const {
5185+
if (mTopology.graph.contains(displayId)) {
5186+
return mTopology.primaryDisplayId;
5187+
}
5188+
return displayId;
5189+
}
5190+
51855191
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
51865192
std::string dump;
51875193
if (!mWindowHandlesByDisplay.empty()) {
@@ -7475,32 +7481,40 @@ void InputDispatcher::DispatcherTouchState::clear() {
74757481

74767482
void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
74777483
const android::inputdispatcher::MotionEntry& entry,
7478-
android::inputdispatcher::TouchState&& touchState) {
7484+
android::inputdispatcher::TouchState&& touchState,
7485+
const DispatcherWindowInfo& windowInfos) {
74797486
if (touchState.windows.empty()) {
7480-
eraseTouchStateForMotionEntry(entry);
7487+
eraseTouchStateForMotionEntry(entry, windowInfos);
74817488
return;
74827489
}
74837490

7484-
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7485-
mCursorStateByDisplay[entry.displayId] = std::move(touchState);
7491+
if (com::android::input::flags::connected_displays_cursor() &&
7492+
isMouseOrTouchpad(entry.source)) {
7493+
mCursorStateByDisplay[windowInfos.getPrimaryDisplayId(entry.displayId)] =
7494+
std::move(touchState);
74867495
} else {
74877496
mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
74887497
}
74897498
}
74907499

74917500
void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
7492-
const android::inputdispatcher::MotionEntry& entry) {
7493-
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7494-
mCursorStateByDisplay.erase(entry.displayId);
7501+
const android::inputdispatcher::MotionEntry& entry,
7502+
const DispatcherWindowInfo& windowInfos) {
7503+
if (com::android::input::flags::connected_displays_cursor() &&
7504+
isMouseOrTouchpad(entry.source)) {
7505+
mCursorStateByDisplay.erase(windowInfos.getPrimaryDisplayId(entry.displayId));
74957506
} else {
74967507
mTouchStatesByDisplay.erase(entry.displayId);
74977508
}
74987509
}
74997510

75007511
const TouchState* InputDispatcher::DispatcherTouchState::getTouchStateForMotionEntry(
7501-
const android::inputdispatcher::MotionEntry& entry) const {
7502-
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7503-
auto touchStateIt = mCursorStateByDisplay.find(entry.displayId);
7512+
const android::inputdispatcher::MotionEntry& entry,
7513+
const DispatcherWindowInfo& windowInfos) const {
7514+
if (com::android::input::flags::connected_displays_cursor() &&
7515+
isMouseOrTouchpad(entry.source)) {
7516+
auto touchStateIt =
7517+
mCursorStateByDisplay.find(windowInfos.getPrimaryDisplayId(entry.displayId));
75047518
if (touchStateIt != mCursorStateByDisplay.end()) {
75057519
return &touchStateIt->second;
75067520
}

services/inputflinger/dispatcher/InputDispatcher.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,10 @@ class InputDispatcher : public android::InputDispatcherInterface {
336336

337337
bool isTouchTrusted(const TouchOcclusionInfo& occlusionInfo) const;
338338

339+
// Returns topology's primary display if the display belongs to it, otherwise the
340+
// same displayId.
341+
ui::LogicalDisplayId getPrimaryDisplayId(ui::LogicalDisplayId displayId) const;
342+
339343
std::string dumpDisplayAndWindowInfo() const;
340344

341345
private:
@@ -466,12 +470,15 @@ class InputDispatcher : public android::InputDispatcherInterface {
466470
ftl::Flags<InputTarget::Flags> newTargetFlags,
467471
const DispatcherWindowInfo& windowInfos, const ConnectionManager& connections);
468472

469-
void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState);
473+
void saveTouchStateForMotionEntry(const MotionEntry& entry, TouchState&& touchState,
474+
const DispatcherWindowInfo& windowInfos);
470475

471-
void eraseTouchStateForMotionEntry(const MotionEntry& entry);
476+
void eraseTouchStateForMotionEntry(const MotionEntry& entry,
477+
const DispatcherWindowInfo& windowInfos);
472478

473479
const TouchState* getTouchStateForMotionEntry(
474-
const android::inputdispatcher::MotionEntry& entry) const;
480+
const android::inputdispatcher::MotionEntry& entry,
481+
const DispatcherWindowInfo& windowInfos) const;
475482

476483
bool canWindowReceiveMotion(const sp<gui::WindowInfoHandle>& window,
477484
const MotionEntry& motionEntry,

services/inputflinger/dispatcher/InputState.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@
2424

2525
namespace android::inputdispatcher {
2626

27+
namespace {
28+
29+
bool isMouseOrTouchpad(uint32_t sources) {
30+
// Check if this is a mouse or touchpad, but not a drawing tablet.
31+
return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
32+
(isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
33+
!isFromSource(sources, AINPUT_SOURCE_STYLUS));
34+
}
35+
36+
} // namespace
37+
2738
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
2839

2940
InputState::~InputState() {}
@@ -221,10 +232,15 @@ ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
221232
}
222233

223234
ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
235+
// If we have connected displays a mouse can move between displays and displayId may change
236+
// while a gesture is in-progress.
237+
const bool skipDisplayCheck = com::android::input::flags::connected_displays_cursor() &&
238+
isMouseOrTouchpad(entry.source);
224239
for (size_t i = 0; i < mMotionMementos.size(); i++) {
225240
const MotionMemento& memento = mMotionMementos[i];
226241
if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
227-
memento.displayId == entry.displayId && memento.hovering == hovering) {
242+
memento.hovering == hovering &&
243+
(skipDisplayCheck || memento.displayId == entry.displayId)) {
228244
return i;
229245
}
230246
}
@@ -338,7 +354,10 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons
338354
// would receive different events from each display. Since the TouchStates are per-display,
339355
// it's unlikely that those two streams would be consistent with each other. Therefore,
340356
// cancel the previous gesture if the display id changes.
341-
if (motionEntry.displayId != lastMemento.displayId) {
357+
// Except when we have connected-displays where a mouse may move across display boundaries.
358+
const bool skipDisplayCheck = (com::android::input::flags::connected_displays_cursor() &&
359+
isMouseOrTouchpad(motionEntry.source));
360+
if (!skipDisplayCheck && motionEntry.displayId != lastMemento.displayId) {
342361
LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
343362
<< " and new event is " << motionEntry;
344363
return true;

services/inputflinger/tests/InputDispatcher_test.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15185,4 +15185,91 @@ TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevi
1518515185

1518615186
INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, testing::Bool());
1518715187

15188+
class InputDispatcherConnectedDisplayTest : public InputDispatcherTest {
15189+
constexpr static int DENSITY_MEDIUM = 160;
15190+
15191+
const DisplayTopologyGraph
15192+
mTopology{.primaryDisplayId = DISPLAY_ID,
15193+
.graph = {{DISPLAY_ID,
15194+
{{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}},
15195+
{SECOND_DISPLAY_ID,
15196+
{{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}},
15197+
.displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM},
15198+
{SECOND_DISPLAY_ID, DENSITY_MEDIUM}}};
15199+
15200+
protected:
15201+
sp<FakeWindowHandle> mWindow;
15202+
15203+
void SetUp() override {
15204+
InputDispatcherTest::SetUp();
15205+
mDispatcher->setDisplayTopology(mTopology);
15206+
mWindow = sp<FakeWindowHandle>::make(std::make_shared<FakeApplicationHandle>(), mDispatcher,
15207+
"Window", DISPLAY_ID);
15208+
mWindow->setFrame({0, 0, 100, 100});
15209+
15210+
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
15211+
}
15212+
};
15213+
15214+
TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) {
15215+
SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true);
15216+
15217+
// pointer-down
15218+
mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
15219+
.displayId(DISPLAY_ID)
15220+
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
15221+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
15222+
.build());
15223+
mWindow->consumeMotionEvent(
15224+
AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
15225+
15226+
mDispatcher->notifyMotion(
15227+
MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
15228+
.displayId(DISPLAY_ID)
15229+
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
15230+
.actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
15231+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
15232+
.build());
15233+
mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
15234+
WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
15235+
15236+
// pointer-move
15237+
mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
15238+
.displayId(DISPLAY_ID)
15239+
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
15240+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(60).y(60))
15241+
.build());
15242+
mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
15243+
WithDisplayId(DISPLAY_ID), WithRawCoords(60, 60)));
15244+
15245+
// pointer-move with different display
15246+
// TODO (b/383092013): by default windows will not be topology aware and receive events as it
15247+
// was in the same display. This behaviour has not been implemented yet.
15248+
mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
15249+
.displayId(SECOND_DISPLAY_ID)
15250+
.buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
15251+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
15252+
.build());
15253+
mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
15254+
WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 70)));
15255+
15256+
// pointer-up
15257+
mDispatcher->notifyMotion(
15258+
MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, AINPUT_SOURCE_MOUSE)
15259+
.displayId(SECOND_DISPLAY_ID)
15260+
.buttonState(0)
15261+
.actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
15262+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
15263+
.build());
15264+
mWindow->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
15265+
WithDisplayId(SECOND_DISPLAY_ID), WithRawCoords(70, 70)));
15266+
15267+
mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE)
15268+
.displayId(SECOND_DISPLAY_ID)
15269+
.buttonState(0)
15270+
.pointer(PointerBuilder(0, ToolType::MOUSE).x(70).y(70))
15271+
.build());
15272+
mWindow->consumeMotionUp(SECOND_DISPLAY_ID);
15273+
}
15274+
1518815275
} // namespace android::inputdispatcher

0 commit comments

Comments
 (0)