@@ -313,30 +313,89 @@ void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArg
313313
314314void PointerChoreographer::handleUnconsumedDeltaLocked (PointerControllerInterface& pc,
315315 const FloatPoint& unconsumedDelta) {
316+ // Display topology is in rotated coordinate space and Pointer controller returns and expects
317+ // values in the un-rotated coordinate space. So we need to transform delta and cursor position
318+ // back to the rotated coordinate space to lookup adjacent display in the display topology.
319+ const auto & sourceDisplayTransform = pc.getDisplayTransform ();
320+ const vec2 rotatedUnconsumedDelta =
321+ transformWithoutTranslation (sourceDisplayTransform,
322+ {unconsumedDelta.x , unconsumedDelta.y });
323+ const FloatPoint cursorPosition = pc.getPosition ();
324+ const vec2 rotatedCursorPosition =
325+ sourceDisplayTransform.transform (cursorPosition.x , cursorPosition.y );
326+
327+ // To find out the boundary that cursor is crossing we are checking delta in x and y direction
328+ // respectively. This prioritizes x direction over y.
329+ // In practise, majority of cases we only have non-zero values in either x or y coordinates,
330+ // except sometimes near the corners.
331+ // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
332+ // the destination display for the same reason.
333+ DisplayPosition sourceBoundary;
334+ float cursorOffset = 0 .0f ;
335+ if (rotatedUnconsumedDelta.x > 0 ) {
336+ sourceBoundary = DisplayPosition::RIGHT;
337+ cursorOffset = rotatedCursorPosition.y ;
338+ } else if (rotatedUnconsumedDelta.x < 0 ) {
339+ sourceBoundary = DisplayPosition::LEFT;
340+ cursorOffset = rotatedCursorPosition.y ;
341+ } else if (rotatedUnconsumedDelta.y > 0 ) {
342+ sourceBoundary = DisplayPosition::BOTTOM;
343+ cursorOffset = rotatedCursorPosition.x ;
344+ } else {
345+ sourceBoundary = DisplayPosition::TOP;
346+ cursorOffset = rotatedCursorPosition.x ;
347+ }
348+
316349 const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId ();
317- const auto & sourceViewport = *findViewportByIdLocked (sourceDisplayId);
318- std::optional<const DisplayViewport*> destination =
319- findDestinationDisplayLocked (sourceViewport, unconsumedDelta);
320- if (!destination) {
321- // no adjacent display
350+ std::optional<std::pair<const DisplayViewport*, float /* offset*/ >> destination =
351+ findDestinationDisplayLocked (sourceDisplayId, sourceBoundary, cursorOffset);
352+ if (!destination.has_value ()) {
353+ // No matching adjacent display
322354 return ;
323355 }
324356
325- const DisplayViewport* destinationViewport = *destination;
326-
327- if (mMousePointersByDisplay .find (destinationViewport-> displayId ) !=
357+ const DisplayViewport& destinationViewport = *destination-> first ;
358+ const float destinationOffset = destination-> second ;
359+ if (mMousePointersByDisplay .find (destinationViewport. displayId ) !=
328360 mMousePointersByDisplay .end ()) {
329361 LOG (FATAL) << " A cursor already exists on destination display"
330- << destinationViewport-> displayId ;
362+ << destinationViewport. displayId ;
331363 }
332- mDefaultMouseDisplayId = destinationViewport-> displayId ;
364+ mDefaultMouseDisplayId = destinationViewport. displayId ;
333365 auto pcNode = mMousePointersByDisplay .extract (sourceDisplayId);
334- pcNode.key () = destinationViewport-> displayId ;
366+ pcNode.key () = destinationViewport. displayId ;
335367 mMousePointersByDisplay .insert (std::move (pcNode));
336368
337- // This will place cursor at the center of the target display for now
338- // TODO (b/367660694) place the cursor at the appropriate position in destination display
339- pc.setDisplayViewport (*destinationViewport);
369+ // Before updating the viewport and moving the cursor to appropriate location in the destination
370+ // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the
371+ // center of the display in any intermediate frames.
372+ pc.fade (PointerControllerInterface::Transition::IMMEDIATE);
373+ pc.setDisplayViewport (destinationViewport);
374+ vec2 destinationPosition =
375+ calculateDestinationPosition (destinationViewport, cursorOffset - destinationOffset,
376+ sourceBoundary);
377+
378+ // Transform position back to un-rotated coordinate space before sending it to controller
379+ destinationPosition = pc.getDisplayTransform ().inverse ().transform (destinationPosition.x ,
380+ destinationPosition.y );
381+ pc.setPosition (destinationPosition.x , destinationPosition.y );
382+ pc.unfade (PointerControllerInterface::Transition::IMMEDIATE);
383+ }
384+
385+ vec2 PointerChoreographer::calculateDestinationPosition (const DisplayViewport& destinationViewport,
386+ float pointerOffset,
387+ DisplayPosition sourceBoundary) {
388+ // destination is opposite of the source boundary
389+ switch (sourceBoundary) {
390+ case DisplayPosition::RIGHT:
391+ return {0 , pointerOffset}; // left edge
392+ case DisplayPosition::TOP:
393+ return {pointerOffset, destinationViewport.logicalBottom }; // bottom edge
394+ case DisplayPosition::LEFT:
395+ return {destinationViewport.logicalRight , pointerOffset}; // right edge
396+ case DisplayPosition::BOTTOM:
397+ return {pointerOffset, 0 }; // top edge
398+ }
340399}
341400
342401void PointerChoreographer::processDrawingTabletEventLocked (const android::NotifyMotionArgs& args) {
@@ -951,10 +1010,11 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked(
9511010
9521011 // create a fake topology assuming following order
9531012 // default-display (top-edge) -> next-display (right-edge) -> next-display (right-edge) ...
954- // ┌─────────┬─────────┐
955- // │ next │ next 2 │ ...
956- // ├─────────┼─────────┘
957- // │ default │
1013+ // This also adds a 100px offset on corresponding edge for better manual testing
1014+ // ┌────────┐
1015+ // │ next ├─────────┐
1016+ // ┌─└───────┐┤ next 2 │ ...
1017+ // │ default │└─────────┘
9581018 // └─────────┘
9591019 mTopology .clear ();
9601020
@@ -965,13 +1025,15 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked(
9651025 continue ;
9661026 }
9671027 if (previousDisplay == ui::LogicalDisplayId::DEFAULT) {
968- mTopology [previousDisplay].push_back ({displayInfo.displayId , DisplayPosition::TOP, 0 });
1028+ mTopology [previousDisplay].push_back (
1029+ {displayInfo.displayId , DisplayPosition::TOP, 100 });
9691030 mTopology [displayInfo.displayId ].push_back (
970- {previousDisplay, DisplayPosition::BOTTOM, 0 });
1031+ {previousDisplay, DisplayPosition::BOTTOM, - 100 });
9711032 } else {
9721033 mTopology [previousDisplay].push_back (
973- {displayInfo.displayId , DisplayPosition::RIGHT, 0 });
974- mTopology [displayInfo.displayId ].push_back ({previousDisplay, DisplayPosition::LEFT, 0 });
1034+ {displayInfo.displayId , DisplayPosition::RIGHT, 100 });
1035+ mTopology [displayInfo.displayId ].push_back (
1036+ {previousDisplay, DisplayPosition::LEFT, -100 });
9751037 }
9761038 previousDisplay = displayInfo.displayId ;
9771039 }
@@ -982,34 +1044,17 @@ void PointerChoreographer::populateFakeDisplayTopologyLocked(
9821044 }
9831045}
9841046
985- std::optional<const DisplayViewport*> PointerChoreographer::findDestinationDisplayLocked (
986- const DisplayViewport& sourceViewport, const FloatPoint& unconsumedDelta) const {
987- DisplayPosition sourceBoundary;
988- if (unconsumedDelta.x > 0 ) {
989- sourceBoundary = DisplayPosition::RIGHT;
990- } else if (unconsumedDelta.x < 0 ) {
991- sourceBoundary = DisplayPosition::LEFT;
992- } else if (unconsumedDelta.y > 0 ) {
993- sourceBoundary = DisplayPosition::BOTTOM;
994- } else {
995- sourceBoundary = DisplayPosition::TOP;
996- }
997-
998- // Choreographer works in un-rotate coordinate space so we need to rotate boundary by viewport
999- // orientation to find the rotated boundary
1000- constexpr int MOD = ftl::to_underlying (ui::Rotation::ftl_last) + 1 ;
1001- sourceBoundary = static_cast <DisplayPosition>(
1002- (ftl::to_underlying (sourceBoundary) + ftl::to_underlying (sourceViewport.orientation )) %
1003- MOD);
1004-
1005- const auto & destination = mTopology .find (sourceViewport.displayId );
1006- if (destination == mTopology .end ()) {
1047+ std::optional<std::pair<const DisplayViewport*, float /* offset*/ >>
1048+ PointerChoreographer::findDestinationDisplayLocked (const ui::LogicalDisplayId sourceDisplayId,
1049+ const DisplayPosition sourceBoundary,
1050+ float cursorOffset) const {
1051+ const auto & sourceNode = mTopology .find (sourceDisplayId);
1052+ if (sourceNode == mTopology .end ()) {
10071053 // Topology is likely out of sync with viewport info, wait for it to be updated
1008- LOG (WARNING) << " Source display missing from topology " << sourceViewport. displayId ;
1054+ LOG (WARNING) << " Source display missing from topology " << sourceDisplayId ;
10091055 return std::nullopt ;
10101056 }
1011-
1012- for (const auto & adjacentDisplay : destination->second ) {
1057+ for (const AdjacentDisplay& adjacentDisplay : sourceNode->second ) {
10131058 if (adjacentDisplay.position != sourceBoundary) {
10141059 continue ;
10151060 }
@@ -1018,11 +1063,18 @@ std::optional<const DisplayViewport*> PointerChoreographer::findDestinationDispl
10181063 if (destinationViewport == nullptr ) {
10191064 // Topology is likely out of sync with viewport info, wait for them to be updated
10201065 LOG (WARNING) << " Cannot find viewport for adjacent display "
1021- << adjacentDisplay.displayId << " of source display "
1022- << sourceViewport.displayId ;
1023- break ;
1066+ << adjacentDisplay.displayId << " of source display " << sourceDisplayId;
1067+ continue ;
1068+ }
1069+ // target position must be within target display boundary
1070+ const int32_t edgeSize =
1071+ sourceBoundary == DisplayPosition::TOP || sourceBoundary == DisplayPosition::BOTTOM
1072+ ? (destinationViewport->logicalRight - destinationViewport->logicalLeft )
1073+ : (destinationViewport->logicalBottom - destinationViewport->logicalTop );
1074+ if (cursorOffset >= adjacentDisplay.offsetPx &&
1075+ cursorOffset <= adjacentDisplay.offsetPx + edgeSize) {
1076+ return std::make_pair (destinationViewport, adjacentDisplay.offsetPx );
10241077 }
1025- return destinationViewport;
10261078 }
10271079 return std::nullopt ;
10281080}
0 commit comments