@@ -103,6 +103,9 @@ std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowIn
103103
104104// --- PointerChoreographer ---
105105
106+ const bool PointerChoreographer::IS_TOPOLOGY_AWARE =
107+ com::android::input::flags::connected_displays_cursor ();
108+
106109PointerChoreographer::PointerChoreographer (InputListenerInterface& inputListener,
107110 PointerChoreographerPolicyInterface& policy)
108111 : PointerChoreographer(
@@ -204,20 +207,30 @@ void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArg
204207}
205208
206209NotifyMotionArgs PointerChoreographer::processMotion (const NotifyMotionArgs& args) {
207- std::scoped_lock _l (mLock );
210+ NotifyMotionArgs newArgs (args);
211+ PointerDisplayChange pointerDisplayChange;
212+ { // acquire lock
213+ std::scoped_lock _l (mLock );
214+ if (isFromMouse (args)) {
215+ newArgs = processMouseEventLocked (args);
216+ pointerDisplayChange = calculatePointerDisplayChangeToNotify ();
217+ } else if (isFromTouchpad (args)) {
218+ newArgs = processTouchpadEventLocked (args);
219+ pointerDisplayChange = calculatePointerDisplayChangeToNotify ();
220+ } else if (isFromDrawingTablet (args)) {
221+ processDrawingTabletEventLocked (args);
222+ } else if (mStylusPointerIconEnabled && isStylusHoverEvent (args)) {
223+ processStylusHoverEventLocked (args);
224+ } else if (isFromSource (args.source , AINPUT_SOURCE_TOUCHSCREEN)) {
225+ processTouchscreenAndStylusEventLocked (args);
226+ }
227+ } // release lock
208228
209- if (isFromMouse (args)) {
210- return processMouseEventLocked (args);
211- } else if (isFromTouchpad (args)) {
212- return processTouchpadEventLocked (args);
213- } else if (isFromDrawingTablet (args)) {
214- processDrawingTabletEventLocked (args);
215- } else if (mStylusPointerIconEnabled && isStylusHoverEvent (args)) {
216- processStylusHoverEventLocked (args);
217- } else if (isFromSource (args.source , AINPUT_SOURCE_TOUCHSCREEN)) {
218- processTouchscreenAndStylusEventLocked (args);
229+ if (pointerDisplayChange) {
230+ // pointer display may have changed if mouse crossed display boundary
231+ notifyPointerDisplayChange (pointerDisplayChange, mPolicy );
219232 }
220- return args ;
233+ return newArgs ;
221234}
222235
223236NotifyMotionArgs PointerChoreographer::processMouseEventLocked (const NotifyMotionArgs& args) {
@@ -242,16 +255,10 @@ NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotio
242255 pc.setPosition (args.xCursorPosition , args.yCursorPosition );
243256 } else {
244257 // This is a relative mouse, so move the cursor by the specified amount.
245- const float deltaX = args.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_X);
246- const float deltaY = args.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_Y);
247- pc.move (deltaX, deltaY);
248- const auto [x, y] = pc.getPosition ();
249- newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_X, x);
250- newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_Y, y);
251- newArgs.xCursorPosition = x;
252- newArgs.yCursorPosition = y;
258+ processPointerDeviceMotionEventLocked (/* byref*/ newArgs, /* byref*/ pc);
253259 }
254- if (canUnfadeOnDisplay (displayId)) {
260+ // Note displayId may have changed if the cursor moved to a different display
261+ if (canUnfadeOnDisplay (newArgs.displayId )) {
255262 pc.unfade (PointerControllerInterface::Transition::IMMEDIATE);
256263 }
257264 return newArgs;
@@ -265,24 +272,9 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo
265272 newArgs.displayId = displayId;
266273 if (args.getPointerCount () == 1 && args.classification == MotionClassification::NONE) {
267274 // This is a movement of the mouse pointer.
268- const float deltaX = args.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_X);
269- const float deltaY = args.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_Y);
270- pc.move (deltaX, deltaY);
271- if (canUnfadeOnDisplay (displayId)) {
272- pc.unfade (PointerControllerInterface::Transition::IMMEDIATE);
273- }
274-
275- const auto [x, y] = pc.getPosition ();
276- newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_X, x);
277- newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_Y, y);
278- newArgs.xCursorPosition = x;
279- newArgs.yCursorPosition = y;
275+ processPointerDeviceMotionEventLocked (/* byref*/ newArgs, /* byref*/ pc);
280276 } else {
281277 // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
282- if (canUnfadeOnDisplay (displayId)) {
283- pc.unfade (PointerControllerInterface::Transition::IMMEDIATE);
284- }
285-
286278 const auto [x, y] = pc.getPosition ();
287279 for (uint32_t i = 0 ; i < newArgs.getPointerCount (); i++) {
288280 newArgs.pointerCoords [i].setAxisValue (AMOTION_EVENT_AXIS_X,
@@ -293,9 +285,61 @@ NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMo
293285 newArgs.xCursorPosition = x;
294286 newArgs.yCursorPosition = y;
295287 }
288+
289+ // Note displayId may have changed if the cursor moved to a different display
290+ if (canUnfadeOnDisplay (newArgs.displayId )) {
291+ pc.unfade (PointerControllerInterface::Transition::IMMEDIATE);
292+ }
296293 return newArgs;
297294}
298295
296+ void PointerChoreographer::processPointerDeviceMotionEventLocked (NotifyMotionArgs& newArgs,
297+ PointerControllerInterface& pc) {
298+ const float deltaX = newArgs.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_X);
299+ const float deltaY = newArgs.pointerCoords [0 ].getAxisValue (AMOTION_EVENT_AXIS_RELATIVE_Y);
300+ vec2 unconsumedDelta = pc.move (deltaX, deltaY);
301+ if (IS_TOPOLOGY_AWARE && (std::abs (unconsumedDelta.x ) > 0 || std::abs (unconsumedDelta.y ) > 0 )) {
302+ handleUnconsumedDeltaLocked (pc, unconsumedDelta);
303+ // pointer may have moved to a different viewport
304+ newArgs.displayId = pc.getDisplayId ();
305+ }
306+ const auto [x, y] = pc.getPosition ();
307+ newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_X, x);
308+ newArgs.pointerCoords [0 ].setAxisValue (AMOTION_EVENT_AXIS_Y, y);
309+ newArgs.xCursorPosition = x;
310+ newArgs.yCursorPosition = y;
311+ }
312+
313+ void PointerChoreographer::handleUnconsumedDeltaLocked (PointerControllerInterface& pc,
314+ const vec2& unconsumedDelta) {
315+ const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId ();
316+ const auto & sourceViewport = *findViewportByIdLocked (sourceDisplayId);
317+ std::optional<AdjacentDisplay> destinationDisplay =
318+ findDestinationDisplayLocked (sourceViewport, unconsumedDelta);
319+ if (!destinationDisplay) {
320+ // no adjacent display
321+ return ;
322+ }
323+
324+ const DisplayViewport* destinationViewport =
325+ findViewportByIdLocked (destinationDisplay->displayId );
326+ if (destinationViewport == nullptr ) {
327+ // Topology is likely out of sync with viewport info, wait for them to be updated
328+ LOG (WARNING) << " Cannot find viewport for adjacent display "
329+ << destinationDisplay->displayId << " of source display " << sourceDisplayId;
330+ return ;
331+ }
332+
333+ mDefaultMouseDisplayId = destinationDisplay->displayId ;
334+ auto pcNode = mMousePointersByDisplay .extract (sourceDisplayId);
335+ pcNode.key () = destinationDisplay->displayId ;
336+ mMousePointersByDisplay .insert (std::move (pcNode));
337+
338+ // This will place cursor at the center of the target display for now
339+ // TODO (b/367660694) place the cursor at the appropriate position in destination display
340+ pc.setDisplayViewport (*destinationViewport);
341+ }
342+
299343void PointerChoreographer::processDrawingTabletEventLocked (const android::NotifyMotionArgs& args) {
300344 if (args.displayId == ui::LogicalDisplayId::INVALID) {
301345 return ;
@@ -441,7 +485,8 @@ void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args)
441485}
442486
443487void PointerChoreographer::onControllerAddedOrRemovedLocked () {
444- if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows ()) {
488+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows () &&
489+ !IS_TOPOLOGY_AWARE) {
445490 return ;
446491 }
447492 bool requireListener = !mTouchPointersByDevice .empty () || !mMousePointersByDisplay .empty () ||
@@ -674,6 +719,10 @@ PointerChoreographer::calculatePointerDisplayChangeToNotify() {
674719}
675720
676721void PointerChoreographer::setDefaultMouseDisplayId (ui::LogicalDisplayId displayId) {
722+ if (IS_TOPOLOGY_AWARE) {
723+ // default display will be set based on the topology
724+ return ;
725+ }
677726 PointerDisplayChange pointerDisplayChange;
678727
679728 { // acquire lock
@@ -887,6 +936,7 @@ void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfo
887936 mPrivacySensitiveDisplays = std::move (newPrivacySensitiveDisplays);
888937 mPointerChoreographer ->onPrivacySensitiveDisplaysChanged (mPrivacySensitiveDisplays );
889938 }
939+ mPointerChoreographer ->populateFakeDisplayTopology (windowInfosUpdate.displayInfos );
890940}
891941
892942void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos (
@@ -907,4 +957,93 @@ void PointerChoreographer::PointerChoreographerDisplayInfoListener::
907957 mPointerChoreographer = nullptr ;
908958}
909959
960+ void PointerChoreographer::populateFakeDisplayTopology (
961+ const std::vector<gui::DisplayInfo>& displayInfos) {
962+ if (!IS_TOPOLOGY_AWARE) {
963+ return ;
964+ }
965+ std::scoped_lock _lock (mLock );
966+
967+ if (displayInfos.size () == mTopology .size ()) {
968+ bool displaysChanged = false ;
969+ for (const auto & displayInfo : displayInfos) {
970+ if (mTopology .find (displayInfo.displayId ) == mTopology .end ()) {
971+ displaysChanged = true ;
972+ break ;
973+ }
974+ }
975+
976+ if (!displaysChanged) {
977+ return ;
978+ }
979+ }
980+
981+ // create a fake topology assuming following order
982+ // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
983+ // ┌─────────┬─────────┐
984+ // │ next │ next 2 │ ...
985+ // ├─────────┼─────────┘
986+ // │ default │
987+ // └─────────┘
988+ mTopology .clear ();
989+
990+ // treat default display as base, in real topology it should be the primary-display
991+ ui::LogicalDisplayId previousDisplay = ui::LogicalDisplayId::DEFAULT;
992+ for (const auto & displayInfo : displayInfos) {
993+ if (displayInfo.displayId == ui::LogicalDisplayId::DEFAULT) {
994+ continue ;
995+ }
996+ if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
997+ mTopology [previousDisplay].push_back ({displayInfo.displayId , DisplayPosition::TOP, 0 });
998+ mTopology [displayInfo.displayId ].push_back (
999+ {previousDisplay, DisplayPosition::BOTTOM, 0 });
1000+ } else {
1001+ mTopology [previousDisplay].push_back (
1002+ {displayInfo.displayId , DisplayPosition::RIGHT, 0 });
1003+ mTopology [displayInfo.displayId ].push_back ({previousDisplay, DisplayPosition::LEFT, 0 });
1004+ }
1005+ previousDisplay = displayInfo.displayId ;
1006+ }
1007+
1008+ // update default pointer display. In real topology it should be the primary-display
1009+ if (mTopology .find (mDefaultMouseDisplayId ) == mTopology .end ()) {
1010+ mDefaultMouseDisplayId = ui::LogicalDisplayId::DEFAULT;
1011+ }
1012+ }
1013+
1014+ std::optional<PointerChoreographer::AdjacentDisplay>
1015+ PointerChoreographer::findDestinationDisplayLocked (const DisplayViewport& sourceViewport,
1016+ const vec2& unconsumedDelta) const {
1017+ DisplayPosition sourceBoundary;
1018+ if (unconsumedDelta.x > 0 ) {
1019+ sourceBoundary = DisplayPosition::RIGHT;
1020+ } else if (unconsumedDelta.x < 0 ) {
1021+ sourceBoundary = DisplayPosition::LEFT;
1022+ } else if (unconsumedDelta.y > 0 ) {
1023+ sourceBoundary = DisplayPosition::BOTTOM;
1024+ } else {
1025+ sourceBoundary = DisplayPosition::TOP;
1026+ }
1027+
1028+ // Choreographer works in un-rotate coordinate space so we need to rotate boundary by viewport
1029+ // orientation to find the rotated boundary
1030+ constexpr int MOD = ftl::to_underlying (ui::Rotation::ftl_last) + 1 ;
1031+ sourceBoundary = static_cast <DisplayPosition>(
1032+ (ftl::to_underlying (sourceBoundary) + ftl::to_underlying (sourceViewport.orientation )) %
1033+ MOD);
1034+
1035+ if (mTopology .find (sourceViewport.displayId ) == mTopology .end ()) {
1036+ // Topology is likely out of sync with viewport info, wait for them to be updated
1037+ LOG (WARNING) << " Source display missing from topology " << sourceViewport.displayId ;
1038+ return std::nullopt ;
1039+ }
1040+
1041+ for (const auto & adjacentDisplay : mTopology .at (sourceViewport.displayId )) {
1042+ if (adjacentDisplay.position == sourceBoundary) {
1043+ return adjacentDisplay;
1044+ }
1045+ }
1046+ return std::nullopt ;
1047+ }
1048+
9101049} // namespace android
0 commit comments