Skip to content

Commit d6e8320

Browse files
author
Arpit Singh
committed
[3/n CD Cursor] Enable cross-display mouse gestures
Enable mouse based gestures to continue across connected displays. Bug: 367661487 Test: atest inputflinger_tests Test: adb shell setprop persist.device_config.aconfig_flags.\ lse_desktop_experience.com.android.input.flags.\ connected_displays_cursor true && atest inputflinger_tests Flag: com.android.input.flags.connected_displays_cursor Change-Id: Id82135259ce857020abfe65341bf3165b2f3acd8
1 parent 96efbfe commit d6e8320

4 files changed

Lines changed: 141 additions & 15 deletions

File tree

services/inputflinger/dispatcher/InputDispatcher.cpp

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,7 +2398,7 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
23982398
// Copy current touch state into tempTouchState.
23992399
// This state will be used to update saved touch state at the end of this function.
24002400
// If no state for the specified display exists, then our initial state will be empty.
2401-
const TouchState* oldState = getTouchStateForMotionEntry(entry);
2401+
const TouchState* oldState = getTouchStateForMotionEntry(entry, windowInfos);
24022402
TouchState tempTouchState;
24032403
if (oldState != nullptr) {
24042404
tempTouchState = *oldState;
@@ -2787,9 +2787,9 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
27872787
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
27882788
if (displayId >= ui::LogicalDisplayId::DEFAULT) {
27892789
tempTouchState.clearWindowsWithoutPointers();
2790-
saveTouchStateForMotionEntry(entry, std::move(tempTouchState));
2790+
saveTouchStateForMotionEntry(entry, std::move(tempTouchState), windowInfos);
27912791
} else {
2792-
eraseTouchStateForMotionEntry(entry);
2792+
eraseTouchStateForMotionEntry(entry, windowInfos);
27932793
}
27942794
}
27952795

@@ -5182,6 +5182,14 @@ ui::Transform InputDispatcher::DispatcherWindowInfo::getRawTransform(
51825182
: getDisplayTransform(windowInfo.displayId);
51835183
}
51845184

5185+
ui::LogicalDisplayId InputDispatcher::DispatcherWindowInfo::getPrimaryDisplayId(
5186+
ui::LogicalDisplayId displayId) const {
5187+
if (mTopology.graph.contains(displayId)) {
5188+
return mTopology.primaryDisplayId;
5189+
}
5190+
return displayId;
5191+
}
5192+
51855193
std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() const {
51865194
std::string dump;
51875195
if (!mWindowHandlesByDisplay.empty()) {
@@ -7475,32 +7483,37 @@ void InputDispatcher::DispatcherTouchState::clear() {
74757483

74767484
void InputDispatcher::DispatcherTouchState::saveTouchStateForMotionEntry(
74777485
const android::inputdispatcher::MotionEntry& entry,
7478-
android::inputdispatcher::TouchState&& touchState) {
7486+
android::inputdispatcher::TouchState&& touchState,
7487+
const DispatcherWindowInfo& windowInfos) {
74797488
if (touchState.windows.empty()) {
7480-
eraseTouchStateForMotionEntry(entry);
7489+
eraseTouchStateForMotionEntry(entry, windowInfos);
74817490
return;
74827491
}
74837492

74847493
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7485-
mCursorStateByDisplay[entry.displayId] = std::move(touchState);
7494+
mCursorStateByDisplay[windowInfos.getPrimaryDisplayId(entry.displayId)] =
7495+
std::move(touchState);
74867496
} else {
74877497
mTouchStatesByDisplay[entry.displayId] = std::move(touchState);
74887498
}
74897499
}
74907500

74917501
void InputDispatcher::DispatcherTouchState::eraseTouchStateForMotionEntry(
7492-
const android::inputdispatcher::MotionEntry& entry) {
7502+
const android::inputdispatcher::MotionEntry& entry,
7503+
const DispatcherWindowInfo& windowInfos) {
74937504
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7494-
mCursorStateByDisplay.erase(entry.displayId);
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 {
7512+
const android::inputdispatcher::MotionEntry& entry,
7513+
const DispatcherWindowInfo& windowInfos) const {
75027514
if (USE_TOPOLOGY && isMouseOrTouchpad(entry.source)) {
7503-
auto touchStateIt = mCursorStateByDisplay.find(entry.displayId);
7515+
auto touchStateIt =
7516+
mCursorStateByDisplay.find(windowInfos.getPrimaryDisplayId(entry.displayId));
75047517
if (touchStateIt != mCursorStateByDisplay.end()) {
75057518
return &touchStateIt->second;
75067519
}

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,19 @@
2424

2525
namespace android::inputdispatcher {
2626

27+
namespace {
28+
29+
const bool USE_TOPOLOGY = com::android::input::flags::connected_displays_cursor();
30+
31+
bool isMouseOrTouchpad(uint32_t sources) {
32+
// Check if this is a mouse or touchpad, but not a drawing tablet.
33+
return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
34+
(isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
35+
!isFromSource(sources, AINPUT_SOURCE_STYLUS));
36+
}
37+
38+
} // namespace
39+
2740
InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
2841

2942
InputState::~InputState() {}
@@ -221,10 +234,14 @@ ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
221234
}
222235

223236
ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
237+
// If we have connected displays a mouse can move between displays and displayId may change
238+
// while a gesture is in-progress.
239+
const bool skipDisplayCheck = USE_TOPOLOGY && isMouseOrTouchpad(entry.source);
224240
for (size_t i = 0; i < mMotionMementos.size(); i++) {
225241
const MotionMemento& memento = mMotionMementos[i];
226242
if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
227-
memento.displayId == entry.displayId && memento.hovering == hovering) {
243+
memento.hovering == hovering &&
244+
(skipDisplayCheck || memento.displayId == entry.displayId)) {
228245
return i;
229246
}
230247
}
@@ -338,7 +355,9 @@ bool InputState::shouldCancelPreviousStream(const MotionEntry& motionEntry) cons
338355
// would receive different events from each display. Since the TouchStates are per-display,
339356
// it's unlikely that those two streams would be consistent with each other. Therefore,
340357
// cancel the previous gesture if the display id changes.
341-
if (motionEntry.displayId != lastMemento.displayId) {
358+
// Except when we have connected-displays where a mouse may move across display boundaries.
359+
const bool skipDisplayCheck = (USE_TOPOLOGY && 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)