1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "PointerChoreographer"
18
19 #include <android-base/logging.h>
20 #include <android/configuration.h>
21 #include <com_android_input_flags.h>
22 #include <algorithm>
23 #if defined(__ANDROID__)
24 #include <gui/SurfaceComposerClient.h>
25 #endif
26 #include <input/InputFlags.h>
27 #include <input/Keyboard.h>
28 #include <input/PrintTools.h>
29 #include <unordered_set>
30
31 #include "PointerChoreographer.h"
32
33 #define INDENT " "
34 #define INDENT2 " "
35
36 namespace android {
37
38 namespace {
39
notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId,vec2>> change,PointerChoreographerPolicyInterface & policy)40 inline void notifyPointerDisplayChange(std::optional<std::tuple<ui::LogicalDisplayId, vec2>> change,
41 PointerChoreographerPolicyInterface& policy) {
42 if (!change) {
43 return;
44 }
45 const auto& [displayId, cursorPosition] = *change;
46 policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
47 }
48
setIconForController(const std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> & icon,PointerControllerInterface & controller)49 void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
50 PointerControllerInterface& controller) {
51 if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
52 if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
53 LOG(FATAL) << "SpriteIcon should not be null";
54 }
55 controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
56 } else {
57 controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
58 }
59 }
60
61 // filters and returns a set of privacy sensitive displays that are currently visible.
getPrivacySensitiveDisplaysFromWindowInfos(const std::vector<gui::WindowInfo> & windowInfos)62 std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
63 const std::vector<gui::WindowInfo>& windowInfos) {
64 std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
65 for (const auto& windowInfo : windowInfos) {
66 if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
67 windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
68 privacySensitiveDisplays.insert(windowInfo.displayId);
69 }
70 }
71 return privacySensitiveDisplays;
72 }
73
calculatePositionOnDestinationViewport(const DisplayViewport & destinationViewport,float pointerOffset,DisplayTopologyPosition sourceBoundary)74 vec2 calculatePositionOnDestinationViewport(const DisplayViewport& destinationViewport,
75 float pointerOffset,
76 DisplayTopologyPosition sourceBoundary) {
77 // destination is opposite of the source boundary
78 switch (sourceBoundary) {
79 case DisplayTopologyPosition::RIGHT:
80 return {0, pointerOffset}; // left edge
81 case DisplayTopologyPosition::TOP:
82 return {pointerOffset, destinationViewport.logicalBottom}; // bottom edge
83 case DisplayTopologyPosition::LEFT:
84 return {destinationViewport.logicalRight, pointerOffset}; // right edge
85 case DisplayTopologyPosition::BOTTOM:
86 return {pointerOffset, 0}; // top edge
87 }
88 }
89
90 // The standardised medium display density for which 1 px = 1 dp
91 constexpr int32_t DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM;
92
pxToDp(int px,int dpi)93 inline float pxToDp(int px, int dpi) {
94 return static_cast<float>(px * DENSITY_MEDIUM) / static_cast<float>(dpi);
95 }
96
dpToPx(float dp,int dpi)97 inline int dpToPx(float dp, int dpi) {
98 return static_cast<int>((dp * dpi) / DENSITY_MEDIUM);
99 }
100
101 } // namespace
102
103 // --- PointerChoreographer ---
104
PointerChoreographer(InputListenerInterface & inputListener,PointerChoreographerPolicyInterface & policy)105 PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
106 PointerChoreographerPolicyInterface& policy)
107 : PointerChoreographer(
108 inputListener, policy,
109 [](const sp<android::gui::WindowInfosListener>& listener) {
110 auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
111 std::vector<android::gui::DisplayInfo>{});
112 #if defined(__ANDROID__)
113 SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
114 &initialInfo);
115 #endif
116 return initialInfo.first;
117 },
__anon2bcd3f0f0302(const sp<android::gui::WindowInfosListener>& listener) 118 [](const sp<android::gui::WindowInfosListener>& listener) {
119 #if defined(__ANDROID__)
120 SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
121 #endif
122 }) {
123 }
124
PointerChoreographer(android::InputListenerInterface & listener,android::PointerChoreographerPolicyInterface & policy,const android::PointerChoreographer::WindowListenerRegisterConsumer & registerListener,const android::PointerChoreographer::WindowListenerUnregisterConsumer & unregisterListener)125 PointerChoreographer::PointerChoreographer(
126 android::InputListenerInterface& listener,
127 android::PointerChoreographerPolicyInterface& policy,
128 const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
129 const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
130 : mTouchControllerConstructor([this]() {
131 return mPolicy.createPointerController(
132 PointerControllerInterface::ControllerType::TOUCH);
133 }),
134 mNextListener(listener),
135 mPolicy(policy),
136 mCurrentMouseDisplayId(ui::LogicalDisplayId::INVALID),
137 mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
138 mShowTouchesEnabled(false),
139 mStylusPointerIconEnabled(false),
140 mPointerMotionFilterEnabled(false),
141 mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
142 mIsWindowInfoListenerRegistered(false),
143 mWindowInfoListener(sp<PointerChoreographerDisplayInfoListener>::make(this)),
144 mRegisterListener(registerListener),
145 mUnregisterListener(unregisterListener) {}
146
~PointerChoreographer()147 PointerChoreographer::~PointerChoreographer() {
148 if (mIsWindowInfoListenerRegistered) {
149 mUnregisterListener(mWindowInfoListener);
150 mIsWindowInfoListenerRegistered = false;
151 }
152 mWindowInfoListener->onPointerChoreographerDestroyed();
153 }
154
notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs & args)155 void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
156 PointerDisplayChange pointerDisplayChange;
157
158 { // acquire lock
159 std::scoped_lock _l(getLock());
160
161 mInputDeviceInfos = args.inputDeviceInfos;
162 pointerDisplayChange = updatePointerControllersLocked();
163 } // release lock
164
165 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
166 mNextListener.notify(args);
167 }
168
notifyKey(const NotifyKeyArgs & args)169 void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
170 fadeMouseCursorOnKeyPress(args);
171 mNextListener.notify(args);
172 }
173
notifyMotion(const NotifyMotionArgs & args)174 void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
175 NotifyMotionArgs newArgs = processMotion(args);
176
177 mNextListener.notify(newArgs);
178 }
179
fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs & args)180 void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
181 if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
182 return;
183 }
184 // Meta state for these keys is ignored for dismissing cursor while typing
185 constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
186 AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
187 if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
188 // Do not fade if any other meta state is active
189 return;
190 }
191 if (!mPolicy.isInputMethodConnectionActive()) {
192 return;
193 }
194
195 std::scoped_lock _l(getLock());
196 ui::LogicalDisplayId targetDisplay = args.displayId;
197 if (targetDisplay == ui::LogicalDisplayId::INVALID) {
198 targetDisplay = mCurrentFocusedDisplay;
199 }
200 auto it = mMousePointersByDisplay.find(targetDisplay);
201 if (it != mMousePointersByDisplay.end()) {
202 mPolicy.notifyMouseCursorFadedOnTyping();
203 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
204 }
205 }
206
processMotion(const NotifyMotionArgs & args)207 NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
208 NotifyMotionArgs newArgs(args);
209 PointerDisplayChange pointerDisplayChange;
210 { // acquire lock
211 std::scoped_lock _l(getLock());
212 if (isFromMouse(args.source, args.pointerProperties[0].toolType)) {
213 newArgs = processMouseEventLocked(args);
214 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
215 } else if (isFromTouchpad(args.source, args.pointerProperties[0].toolType)) {
216 newArgs = processTouchpadEventLocked(args);
217 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
218 } else if (isFromDrawingTablet(args.source, args.pointerProperties[0].toolType)) {
219 processDrawingTabletEventLocked(args);
220 } else if (mStylusPointerIconEnabled &&
221 isStylusHoverEvent(args.source, args.pointerProperties, args.action)) {
222 processStylusHoverEventLocked(args);
223 } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
224 processTouchscreenAndStylusEventLocked(args);
225 }
226 } // release lock
227
228 if (pointerDisplayChange) {
229 // pointer display may have changed if mouse crossed display boundary
230 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
231 }
232 return newArgs;
233 }
234
processMouseEventLocked(const NotifyMotionArgs & args)235 NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
236 if (args.getPointerCount() != 1) {
237 LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
238 << args.dump();
239 }
240
241 mMouseDevices.emplace(args.deviceId);
242 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
243 NotifyMotionArgs newArgs(args);
244 newArgs.displayId = displayId;
245
246 if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
247 // This is an absolute mouse device that knows about the location of the cursor on the
248 // display, so set the cursor position to the specified location.
249 const auto position = pc.getPosition();
250 const float deltaX = args.xCursorPosition - position.x;
251 const float deltaY = args.yCursorPosition - position.y;
252 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
253 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
254 pc.setPosition(args.xCursorPosition, args.yCursorPosition);
255 } else {
256 // This is a relative mouse, so move the cursor by the specified amount.
257 processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
258 }
259 // Note displayId may have changed if the cursor moved to a different display
260 if (canUnfadeOnDisplay(newArgs.displayId)) {
261 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
262 }
263 return newArgs;
264 }
265
processTouchpadEventLocked(const NotifyMotionArgs & args)266 NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
267 mMouseDevices.emplace(args.deviceId);
268 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
269
270 NotifyMotionArgs newArgs(args);
271 newArgs.displayId = displayId;
272 if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
273 // This is a movement of the mouse pointer.
274 processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
275 } else {
276 // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
277 const auto position = pc.getPosition();
278 for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
279 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
280 args.pointerCoords[i].getX() + position.x);
281 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
282 args.pointerCoords[i].getY() + position.y);
283 }
284 newArgs.xCursorPosition = position.x;
285 newArgs.yCursorPosition = position.y;
286 }
287
288 // Note displayId may have changed if the cursor moved to a different display
289 if (canUnfadeOnDisplay(newArgs.displayId)) {
290 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
291 }
292 return newArgs;
293 }
294
processPointerDeviceMotionEventLocked(NotifyMotionArgs & newArgs,PointerControllerInterface & pc)295 void PointerChoreographer::processPointerDeviceMotionEventLocked(NotifyMotionArgs& newArgs,
296 PointerControllerInterface& pc) {
297 const float deltaX = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
298 const float deltaY = newArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
299 vec2 filteredDelta =
300 filterPointerMotionForAccessibilityLocked(pc.getPosition(), vec2{deltaX, deltaY},
301 newArgs.displayId);
302 vec2 unconsumedDelta = pc.move(filteredDelta.x, filteredDelta.y);
303 if (InputFlags::connectedDisplaysCursorEnabled() &&
304 (std::abs(unconsumedDelta.x) > 0 || std::abs(unconsumedDelta.y) > 0)) {
305 handleUnconsumedDeltaLocked(pc, unconsumedDelta);
306 // pointer may have moved to a different viewport
307 newArgs.displayId = pc.getDisplayId();
308 }
309
310 const auto position = pc.getPosition();
311 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
312 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
313 newArgs.xCursorPosition = position.x;
314 newArgs.yCursorPosition = position.y;
315 }
316
handleUnconsumedDeltaLocked(PointerControllerInterface & pc,const vec2 & unconsumedDelta)317 void PointerChoreographer::handleUnconsumedDeltaLocked(PointerControllerInterface& pc,
318 const vec2& unconsumedDelta) {
319 // Display topology is in rotated coordinate space and Pointer controller returns and expects
320 // values in the un-rotated coordinate space. So we need to transform delta and cursor position
321 // back to the rotated coordinate space to lookup adjacent display in the display topology.
322 const auto& sourceDisplayTransform = pc.getDisplayTransform();
323 const vec2 rotatedUnconsumedDelta =
324 transformWithoutTranslation(sourceDisplayTransform, unconsumedDelta);
325 const vec2 cursorPosition = pc.getPosition();
326 const vec2 rotatedCursorPosition = sourceDisplayTransform.transform(cursorPosition);
327
328 // To find out the boundary that cursor is crossing we are checking delta in x and y direction
329 // respectively. This prioritizes x direction over y.
330 // In practise, majority of cases we only have non-zero values in either x or y coordinates,
331 // except sometimes near the corners.
332 // In these cases this behaviour is not noticeable. We also do not apply unconsumed delta on
333 // the destination display for the same reason.
334 DisplayTopologyPosition sourceBoundary;
335 float cursorOffset = 0.0f;
336 if (rotatedUnconsumedDelta.x > 0) {
337 sourceBoundary = DisplayTopologyPosition::RIGHT;
338 cursorOffset = rotatedCursorPosition.y;
339 } else if (rotatedUnconsumedDelta.x < 0) {
340 sourceBoundary = DisplayTopologyPosition::LEFT;
341 cursorOffset = rotatedCursorPosition.y;
342 } else if (rotatedUnconsumedDelta.y > 0) {
343 sourceBoundary = DisplayTopologyPosition::BOTTOM;
344 cursorOffset = rotatedCursorPosition.x;
345 } else {
346 sourceBoundary = DisplayTopologyPosition::TOP;
347 cursorOffset = rotatedCursorPosition.x;
348 }
349
350 const ui::LogicalDisplayId sourceDisplayId = pc.getDisplayId();
351 std::optional<std::pair<const DisplayViewport*, float /*offset*/>> destination =
352 findDestinationDisplayLocked(sourceDisplayId, sourceBoundary, cursorOffset);
353 if (!destination.has_value()) {
354 // No matching adjacent display
355 return;
356 }
357
358 const DisplayViewport& destinationViewport = *destination->first;
359 const float destinationOffset = destination->second;
360 if (mMousePointersByDisplay.find(destinationViewport.displayId) !=
361 mMousePointersByDisplay.end()) {
362 LOG(FATAL) << "A cursor already exists on destination display"
363 << destinationViewport.displayId;
364 }
365 mCurrentMouseDisplayId = destinationViewport.displayId;
366 auto pcNode = mMousePointersByDisplay.extract(sourceDisplayId);
367 pcNode.key() = destinationViewport.displayId;
368 mMousePointersByDisplay.insert(std::move(pcNode));
369
370 // Before updating the viewport and moving the cursor to appropriate location in the destination
371 // viewport, we need to temporarily hide the cursor. This will prevent it from appearing at the
372 // center of the display in any intermediate frames.
373 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
374 pc.setDisplayViewport(destinationViewport);
375 vec2 destinationPosition =
376 calculatePositionOnDestinationViewport(destinationViewport, destinationOffset,
377 sourceBoundary);
378
379 // Transform position back to un-rotated coordinate space before sending it to controller
380 destinationPosition = pc.getDisplayTransform().inverse().transform(destinationPosition.x,
381 destinationPosition.y);
382 pc.setPosition(destinationPosition.x, destinationPosition.y);
383 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
384 }
385
processDrawingTabletEventLocked(const android::NotifyMotionArgs & args)386 void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
387 if (args.displayId == ui::LogicalDisplayId::INVALID) {
388 return;
389 }
390
391 if (args.getPointerCount() != 1) {
392 LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
393 << args.dump();
394 }
395
396 // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
397 auto [it, controllerAdded] =
398 mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
399 getMouseControllerConstructor(
400 args.displayId));
401 if (controllerAdded) {
402 onControllerAddedOrRemovedLocked();
403 }
404
405 PointerControllerInterface& pc = *it->second;
406
407 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
408 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
409 pc.setPosition(x, y);
410 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
411 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
412 // immediately by a DOWN event.
413 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
414 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
415 } else if (canUnfadeOnDisplay(args.displayId)) {
416 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
417 }
418 }
419
420 /**
421 * When screen is touched, fade the mouse pointer on that display. We only call fade for
422 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
423 * mouse device keeps moving and unfades the cursor.
424 * For touch events, we do not need to populate the cursor position.
425 */
processTouchscreenAndStylusEventLocked(const NotifyMotionArgs & args)426 void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
427 if (!args.displayId.isValid()) {
428 return;
429 }
430
431 if (const auto it = mMousePointersByDisplay.find(args.displayId);
432 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
433 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
434 }
435
436 if (!mShowTouchesEnabled) {
437 return;
438 }
439
440 // Get the touch pointer controller for the device, or create one if it doesn't exist.
441 auto [it, controllerAdded] =
442 mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
443 if (controllerAdded) {
444 onControllerAddedOrRemovedLocked();
445 }
446
447 PointerControllerInterface& pc = *it->second;
448
449 const PointerCoords* coords = args.pointerCoords.data();
450 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
451 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
452 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
453 BitSet32 idBits;
454 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL &&
455 maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
456 for (size_t i = 0; i < args.getPointerCount(); i++) {
457 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
458 continue;
459 }
460 uint32_t id = args.pointerProperties[i].id;
461 idToIndex[id] = i;
462 idBits.markBit(id);
463 }
464 }
465 // The PointerController already handles setting spots per-display, so
466 // we do not need to manually manage display changes for touch spots for now.
467 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
468 }
469
processStylusHoverEventLocked(const NotifyMotionArgs & args)470 void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
471 if (!args.displayId.isValid()) {
472 return;
473 }
474
475 if (args.getPointerCount() != 1) {
476 LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
477 << args.dump();
478 }
479
480 // Fade the mouse pointer on the display if there is one when the stylus starts hovering.
481 if (args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) {
482 if (const auto it = mMousePointersByDisplay.find(args.displayId);
483 it != mMousePointersByDisplay.end()) {
484 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
485 }
486 }
487
488 // Get the stylus pointer controller for the device, or create one if it doesn't exist.
489 auto [it, controllerAdded] =
490 mStylusPointersByDevice.try_emplace(args.deviceId,
491 getStylusControllerConstructor(args.displayId));
492 if (controllerAdded) {
493 onControllerAddedOrRemovedLocked();
494 }
495
496 PointerControllerInterface& pc = *it->second;
497
498 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
499 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
500 pc.setPosition(x, y);
501 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
502 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
503 // immediately by a DOWN event.
504 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
505 pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER
506 : PointerIconStyle::TYPE_NOT_SPECIFIED);
507 } else if (canUnfadeOnDisplay(args.displayId)) {
508 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
509 }
510 }
511
notifySwitch(const NotifySwitchArgs & args)512 void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
513 mNextListener.notify(args);
514 }
515
notifySensor(const NotifySensorArgs & args)516 void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
517 mNextListener.notify(args);
518 }
519
notifyVibratorState(const NotifyVibratorStateArgs & args)520 void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
521 mNextListener.notify(args);
522 }
523
notifyDeviceReset(const NotifyDeviceResetArgs & args)524 void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
525 processDeviceReset(args);
526
527 mNextListener.notify(args);
528 }
529
processDeviceReset(const NotifyDeviceResetArgs & args)530 void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
531 std::scoped_lock _l(getLock());
532 mTouchPointersByDevice.erase(args.deviceId);
533 mStylusPointersByDevice.erase(args.deviceId);
534 mDrawingTabletPointersByDevice.erase(args.deviceId);
535 onControllerAddedOrRemovedLocked();
536 }
537
onControllerAddedOrRemovedLocked()538 void PointerChoreographer::onControllerAddedOrRemovedLocked() {
539 bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
540 !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
541
542 // PointerChoreographer uses Listener's lock which is already held by caller
543 base::ScopedLockAssertion assumeLocked(mWindowInfoListener->mLock);
544
545 if (requireListener && !mIsWindowInfoListenerRegistered) {
546 mIsWindowInfoListenerRegistered = true;
547 mWindowInfoListener->setInitialDisplayInfosLocked(mRegisterListener(mWindowInfoListener));
548 onPrivacySensitiveDisplaysChangedLocked(
549 mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
550 } else if (!requireListener && mIsWindowInfoListenerRegistered) {
551 mIsWindowInfoListenerRegistered = false;
552 mUnregisterListener(mWindowInfoListener);
553 } else if (requireListener) {
554 // controller may have been added to an existing privacy sensitive display, we need to
555 // update all controllers again
556 onPrivacySensitiveDisplaysChangedLocked(
557 mWindowInfoListener->getPrivacySensitiveDisplaysLocked());
558 }
559 }
560
onPrivacySensitiveDisplaysChangedLocked(const std::unordered_set<ui::LogicalDisplayId> & privacySensitiveDisplays)561 void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
562 const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
563 for (auto& [_, pc] : mTouchPointersByDevice) {
564 pc->clearSkipScreenshotFlags();
565 for (auto displayId : privacySensitiveDisplays) {
566 pc->setSkipScreenshotFlagForDisplay(displayId);
567 }
568 }
569
570 for (auto& [displayId, pc] : mMousePointersByDisplay) {
571 if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
572 pc->setSkipScreenshotFlagForDisplay(displayId);
573 } else {
574 pc->clearSkipScreenshotFlags();
575 }
576 }
577
578 for (auto* pointerControllerByDevice :
579 {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
580 for (auto& [_, pc] : *pointerControllerByDevice) {
581 auto displayId = pc->getDisplayId();
582 if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
583 pc->setSkipScreenshotFlagForDisplay(displayId);
584 } else {
585 pc->clearSkipScreenshotFlags();
586 }
587 }
588 }
589 }
590
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs & args)591 void PointerChoreographer::notifyPointerCaptureChanged(
592 const NotifyPointerCaptureChangedArgs& args) {
593 if (args.request.isEnable()) {
594 std::scoped_lock _l(getLock());
595 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
596 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
597 }
598 }
599 mNextListener.notify(args);
600 }
601
setDisplayTopology(const DisplayTopologyGraph & displayTopologyGraph)602 void PointerChoreographer::setDisplayTopology(const DisplayTopologyGraph& displayTopologyGraph) {
603 PointerDisplayChange pointerDisplayChange;
604 { // acquire lock
605 std::scoped_lock _l(getLock());
606 mTopology = displayTopologyGraph;
607
608 // make primary display default mouse display, if it was not set or
609 // the existing display was removed
610 if (mCurrentMouseDisplayId == ui::LogicalDisplayId::INVALID ||
611 mTopology.graph.find(mCurrentMouseDisplayId) == mTopology.graph.end()) {
612 mCurrentMouseDisplayId = mTopology.primaryDisplayId;
613 pointerDisplayChange = updatePointerControllersLocked();
614 }
615 } // release lock
616
617 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
618 }
619
dump(std::string & dump)620 void PointerChoreographer::dump(std::string& dump) {
621 std::scoped_lock _l(getLock());
622
623 dump += "PointerChoreographer:\n";
624 dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
625 mShowTouchesEnabled ? "true" : "false");
626 dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
627 mStylusPointerIconEnabled ? "true" : "false");
628 dump += StringPrintf(INDENT "Accessibility Pointer Motion Filter Enabled: %s\n",
629 mPointerMotionFilterEnabled ? "true" : "false");
630
631 dump += INDENT "MousePointerControllers:\n";
632 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
633 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
634 dump += INDENT + displayId.toString() + " : " + pointerControllerDump;
635 }
636 dump += INDENT "TouchPointerControllers:\n";
637 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
638 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
639 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
640 }
641 dump += INDENT "StylusPointerControllers:\n";
642 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
643 std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
644 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
645 }
646 dump += INDENT "DrawingTabletControllers:\n";
647 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
648 std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
649 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
650 }
651 dump += INDENT "DisplayTopologyGraph:\n";
652 dump += addLinePrefix(mTopology.dump(), INDENT2);
653 dump += "\n";
654 }
655
findViewportByIdLocked(ui::LogicalDisplayId displayId) const656 const DisplayViewport* PointerChoreographer::findViewportByIdLocked(
657 ui::LogicalDisplayId displayId) const {
658 for (auto& viewport : mViewports) {
659 if (viewport.displayId == displayId) {
660 return &viewport;
661 }
662 }
663 return nullptr;
664 }
665
getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const666 ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
667 ui::LogicalDisplayId associatedDisplayId) const {
668 if (!InputFlags::connectedDisplaysCursorAndAssociatedDisplayCursorBugfixEnabled()) {
669 if (associatedDisplayId.isValid()) {
670 return associatedDisplayId;
671 }
672 return mCurrentMouseDisplayId.isValid() ? mCurrentMouseDisplayId
673 : ui::LogicalDisplayId::DEFAULT;
674 }
675 // Associated display is not included in the topology, return this associated display.
676 if (associatedDisplayId.isValid() &&
677 mTopology.graph.find(associatedDisplayId) == mTopology.graph.end()) {
678 return associatedDisplayId;
679 }
680 if (mCurrentMouseDisplayId.isValid()) {
681 return mCurrentMouseDisplayId;
682 }
683 if (mTopology.primaryDisplayId.isValid()) {
684 return mTopology.primaryDisplayId;
685 }
686 return ui::LogicalDisplayId::DEFAULT;
687 }
688
689 std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId)690 PointerChoreographer::ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) {
691 const ui::LogicalDisplayId displayId = getTargetMouseDisplayLocked(associatedDisplayId);
692
693 auto it = mMousePointersByDisplay.find(displayId);
694 if (it == mMousePointersByDisplay.end()) {
695 it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
696 .first;
697 onControllerAddedOrRemovedLocked();
698 }
699
700 return {displayId, *it->second};
701 }
702
findInputDeviceLocked(DeviceId deviceId)703 InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
704 auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
705 [deviceId](const auto& info) { return info.getId() == deviceId; });
706 return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
707 }
708
canUnfadeOnDisplay(ui::LogicalDisplayId displayId)709 bool PointerChoreographer::canUnfadeOnDisplay(ui::LogicalDisplayId displayId) {
710 return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
711 }
712
getLock() const713 std::mutex& PointerChoreographer::getLock() const {
714 return mWindowInfoListener->mLock;
715 }
716
updatePointerControllersLocked()717 PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
718 std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
719 std::set<DeviceId> touchDevicesToKeep;
720 std::set<DeviceId> stylusDevicesToKeep;
721 std::set<DeviceId> drawingTabletDevicesToKeep;
722
723 // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
724 // new PointerControllers if necessary.
725 for (const auto& info : mInputDeviceInfos) {
726 if (!info.isEnabled()) {
727 // If device is disabled, we should not keep it, and should not show pointer for
728 // disabled mouse device.
729 continue;
730 }
731 const uint32_t sources = info.getSources();
732 const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
733
734 if (isMouseOrTouchpad(sources) || isKnownMouse) {
735 const ui::LogicalDisplayId displayId =
736 getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
737 mouseDisplaysToKeep.insert(displayId);
738 // For mice, show the cursor immediately when the device is first connected or
739 // when it moves to a new display.
740 auto [mousePointerIt, isNewMousePointer] =
741 mMousePointersByDisplay.try_emplace(displayId,
742 getMouseControllerConstructor(displayId));
743 if (isNewMousePointer) {
744 onControllerAddedOrRemovedLocked();
745 }
746
747 mMouseDevices.emplace(info.getId());
748 if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
749 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
750 }
751 }
752 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
753 info.getAssociatedDisplayId().isValid()) {
754 touchDevicesToKeep.insert(info.getId());
755 }
756 if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
757 info.getAssociatedDisplayId().isValid()) {
758 stylusDevicesToKeep.insert(info.getId());
759 }
760 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
761 info.getAssociatedDisplayId().isValid()) {
762 drawingTabletDevicesToKeep.insert(info.getId());
763 }
764 }
765
766 // Remove PointerControllers no longer needed.
767 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
768 return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
769 });
770 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
771 return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
772 });
773 std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
774 return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
775 });
776 std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
777 return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
778 });
779 std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(getLock()) {
780 return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
781 [id](const auto& info) { return info.getId() == id; }) ==
782 mInputDeviceInfos.end();
783 });
784
785 onControllerAddedOrRemovedLocked();
786
787 // Check if we need to notify the policy if there's a change on the pointer display ID.
788 return calculatePointerDisplayChangeToNotify();
789 }
790
791 PointerChoreographer::PointerDisplayChange
calculatePointerDisplayChangeToNotify()792 PointerChoreographer::calculatePointerDisplayChangeToNotify() {
793 ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
794 vec2 cursorPosition = {0, 0};
795 if (const auto it =
796 mMousePointersByDisplay.find(getTargetMouseDisplayLocked(mCurrentMouseDisplayId));
797 it != mMousePointersByDisplay.end()) {
798 const auto& pointerController = it->second;
799 // Use the displayId from the pointerController, because it accurately reflects whether
800 // the viewport has been added for that display. Otherwise, we would have to check if
801 // the viewport exists separately.
802 displayIdToNotify = pointerController->getDisplayId();
803 cursorPosition = pointerController->getPosition();
804 }
805 if (mNotifiedPointerDisplayId == displayIdToNotify) {
806 return {};
807 }
808 mNotifiedPointerDisplayId = displayIdToNotify;
809 return {{displayIdToNotify, cursorPosition}};
810 }
811
setDefaultMouseDisplayId(ui::LogicalDisplayId displayId)812 void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
813 if (InputFlags::connectedDisplaysCursorEnabled()) {
814 // In connected displays scenario, default mouse display will only be updated from topology.
815 return;
816 }
817 PointerDisplayChange pointerDisplayChange;
818
819 { // acquire lock
820 std::scoped_lock _l(getLock());
821
822 mCurrentMouseDisplayId = displayId;
823 pointerDisplayChange = updatePointerControllersLocked();
824 } // release lock
825
826 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
827 }
828
setDisplayViewports(const std::vector<DisplayViewport> & viewports)829 void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
830 PointerDisplayChange pointerDisplayChange;
831
832 { // acquire lock
833 std::scoped_lock _l(getLock());
834 for (const auto& viewport : viewports) {
835 const ui::LogicalDisplayId displayId = viewport.displayId;
836 if (const auto it = mMousePointersByDisplay.find(displayId);
837 it != mMousePointersByDisplay.end()) {
838 it->second->setDisplayViewport(viewport);
839 }
840 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
841 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
842 if (info && info->getAssociatedDisplayId() == displayId) {
843 stylusPointerController->setDisplayViewport(viewport);
844 }
845 }
846 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
847 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
848 if (info && info->getAssociatedDisplayId() == displayId) {
849 drawingTabletController->setDisplayViewport(viewport);
850 }
851 }
852 }
853 mViewports = viewports;
854 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
855 } // release lock
856
857 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
858 }
859
getViewportForPointerDevice(ui::LogicalDisplayId associatedDisplayId)860 std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
861 ui::LogicalDisplayId associatedDisplayId) {
862 std::scoped_lock _l(getLock());
863 const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
864 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
865 return *viewport;
866 }
867 return std::nullopt;
868 }
869
getMouseCursorPosition(ui::LogicalDisplayId displayId)870 vec2 PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
871 std::scoped_lock _l(getLock());
872 const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
873 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
874 it != mMousePointersByDisplay.end()) {
875 return it->second->getPosition();
876 }
877 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
878 }
879
setShowTouchesEnabled(bool enabled)880 void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
881 PointerDisplayChange pointerDisplayChange;
882
883 { // acquire lock
884 std::scoped_lock _l(getLock());
885 if (mShowTouchesEnabled == enabled) {
886 return;
887 }
888 mShowTouchesEnabled = enabled;
889 pointerDisplayChange = updatePointerControllersLocked();
890 } // release lock
891
892 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
893 }
894
setStylusPointerIconEnabled(bool enabled)895 void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
896 PointerDisplayChange pointerDisplayChange;
897
898 { // acquire lock
899 std::scoped_lock _l(getLock());
900 if (mStylusPointerIconEnabled == enabled) {
901 return;
902 }
903 mStylusPointerIconEnabled = enabled;
904 pointerDisplayChange = updatePointerControllersLocked();
905 } // release lock
906
907 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
908 }
909
setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>,PointerIconStyle> icon,ui::LogicalDisplayId displayId,DeviceId deviceId)910 bool PointerChoreographer::setPointerIcon(
911 std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
912 ui::LogicalDisplayId displayId, DeviceId deviceId) {
913 std::scoped_lock _l(getLock());
914 if (deviceId < 0) {
915 LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
916 return false;
917 }
918 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
919 if (!info) {
920 LOG(WARNING) << "No input device info found for id " << deviceId
921 << ". Cannot set pointer icon.";
922 return false;
923 }
924 const uint32_t sources = info->getSources();
925
926 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
927 auto it = mDrawingTabletPointersByDevice.find(deviceId);
928 if (it != mDrawingTabletPointersByDevice.end()) {
929 setIconForController(icon, *it->second);
930 return true;
931 }
932 }
933 if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
934 auto it = mStylusPointersByDevice.find(deviceId);
935 if (it != mStylusPointersByDevice.end()) {
936 if (mShowTouchesEnabled) {
937 // If an app doesn't override the icon for the hovering stylus, show the hover icon.
938 auto* style = std::get_if<PointerIconStyle>(&icon);
939 if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) {
940 *style = PointerIconStyle::TYPE_SPOT_HOVER;
941 }
942 }
943 setIconForController(icon, *it->second);
944 return true;
945 }
946 }
947 if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
948 auto it = mMousePointersByDisplay.find(displayId);
949 if (it != mMousePointersByDisplay.end()) {
950 setIconForController(icon, *it->second);
951 return true;
952 } else {
953 LOG(WARNING) << "No mouse pointer controller found for display " << displayId
954 << ", device " << deviceId << ".";
955 return false;
956 }
957 }
958 LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
959 << ".";
960 return false;
961 }
962
setPointerIconVisibility(ui::LogicalDisplayId displayId,bool visible)963 void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
964 std::scoped_lock lock(getLock());
965 if (visible) {
966 mDisplaysWithPointersHidden.erase(displayId);
967 // We do not unfade the icons here, because we don't know when the last event happened.
968 return;
969 }
970
971 mDisplaysWithPointersHidden.emplace(displayId);
972
973 // Hide any icons that are currently visible on the display.
974 if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
975 const auto& [_, controller] = *it;
976 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
977 }
978 for (const auto& [_, controller] : mStylusPointersByDevice) {
979 if (controller->getDisplayId() == displayId) {
980 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
981 }
982 }
983 }
984
setFocusedDisplay(ui::LogicalDisplayId displayId)985 void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
986 std::scoped_lock lock(getLock());
987 mCurrentFocusedDisplay = displayId;
988 }
989
setAccessibilityPointerMotionFilterEnabled(bool enabled)990 void PointerChoreographer::setAccessibilityPointerMotionFilterEnabled(bool enabled) {
991 std::scoped_lock _l(getLock());
992 mPointerMotionFilterEnabled = enabled;
993 }
994
getMouseControllerConstructor(ui::LogicalDisplayId displayId)995 PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
996 ui::LogicalDisplayId displayId) {
997 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
998 [this, displayId]() REQUIRES(getLock()) {
999 auto pc = mPolicy.createPointerController(
1000 PointerControllerInterface::ControllerType::MOUSE);
1001 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
1002 pc->setDisplayViewport(*viewport);
1003 }
1004 return pc;
1005 };
1006 return ConstructorDelegate(std::move(ctor));
1007 }
1008
getStylusControllerConstructor(ui::LogicalDisplayId displayId)1009 PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
1010 ui::LogicalDisplayId displayId) {
1011 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
1012 [this, displayId]() REQUIRES(getLock()) {
1013 auto pc = mPolicy.createPointerController(
1014 PointerControllerInterface::ControllerType::STYLUS);
1015 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
1016 pc->setDisplayViewport(*viewport);
1017 }
1018 return pc;
1019 };
1020 return ConstructorDelegate(std::move(ctor));
1021 }
1022
1023 std::optional<std::pair<const DisplayViewport*, float /*offsetPx*/>>
findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,const DisplayTopologyPosition sourceBoundary,int32_t sourceCursorOffsetPx) const1024 PointerChoreographer::findDestinationDisplayLocked(const ui::LogicalDisplayId sourceDisplayId,
1025 const DisplayTopologyPosition sourceBoundary,
1026 int32_t sourceCursorOffsetPx) const {
1027 const auto& sourceNode = mTopology.graph.find(sourceDisplayId);
1028 if (sourceNode == mTopology.graph.end()) {
1029 // Topology is likely out of sync with viewport info, wait for it to be updated
1030 LOG(WARNING) << "Source display missing from topology " << sourceDisplayId;
1031 return std::nullopt;
1032 }
1033 for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : sourceNode->second) {
1034 if (adjacentDisplay.position != sourceBoundary) {
1035 continue;
1036 }
1037 const DisplayViewport* adjacentViewport = findViewportByIdLocked(adjacentDisplay.displayId);
1038 if (adjacentViewport == nullptr) {
1039 // Topology is likely out of sync with viewport info, wait for them to be updated
1040 LOG(WARNING) << "Cannot find viewport for adjacent display "
1041 << adjacentDisplay.displayId << "of source display " << sourceDisplayId;
1042 continue;
1043 }
1044 // As displays can have different densities we need to do all calculations in
1045 // density-independent-pixels a.k.a. dp values.
1046 const int sourceDensity = mTopology.displaysDensity.at(sourceDisplayId);
1047 const int adjacentDisplayDensity = mTopology.displaysDensity.at(adjacentDisplay.displayId);
1048 const float sourceCursorOffsetDp = pxToDp(sourceCursorOffsetPx, sourceDensity);
1049 const int32_t edgeSizePx = sourceBoundary == DisplayTopologyPosition::TOP ||
1050 sourceBoundary == DisplayTopologyPosition::BOTTOM
1051 ? (adjacentViewport->logicalRight - adjacentViewport->logicalLeft)
1052 : (adjacentViewport->logicalBottom - adjacentViewport->logicalTop);
1053 const float adjacentEdgeSizeDp = pxToDp(edgeSizePx, adjacentDisplayDensity);
1054 // Target position must be within target display boundary.
1055 // Cursor should also be able to cross displays when only display corners are touching and
1056 // there may be zero overlapping pixels. To accommodate this we have margin of one pixel
1057 // around the end of the overlapping edge.
1058 if (sourceCursorOffsetDp >= adjacentDisplay.offsetDp &&
1059 sourceCursorOffsetDp <= adjacentDisplay.offsetDp + adjacentEdgeSizeDp) {
1060 const int destinationOffsetPx =
1061 dpToPx(sourceCursorOffsetDp - adjacentDisplay.offsetDp, adjacentDisplayDensity);
1062 return std::make_pair(adjacentViewport, destinationOffsetPx);
1063 }
1064 }
1065 return std::nullopt;
1066 }
1067
filterPointerMotionForAccessibilityLocked(const vec2 & current,const vec2 & delta,const ui::LogicalDisplayId & displayId)1068 vec2 PointerChoreographer::filterPointerMotionForAccessibilityLocked(
1069 const vec2& current, const vec2& delta, const ui::LogicalDisplayId& displayId) {
1070 if (!mPointerMotionFilterEnabled) {
1071 return delta;
1072 }
1073 std::optional<vec2> filterResult =
1074 mPolicy.filterPointerMotionForAccessibility(current, delta, displayId);
1075 if (!filterResult.has_value()) {
1076 // Disable filter when there's any error.
1077 mPointerMotionFilterEnabled = false;
1078 return delta;
1079 }
1080 return *filterResult;
1081 }
1082
1083 // --- PointerChoreographer::PointerChoreographerDisplayInfoListener ---
1084
onWindowInfosChanged(const gui::WindowInfosUpdate & windowInfosUpdate)1085 void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
1086 const gui::WindowInfosUpdate& windowInfosUpdate) {
1087 std::scoped_lock _l(mLock);
1088 if (mPointerChoreographer == nullptr) {
1089 return;
1090 }
1091 auto newPrivacySensitiveDisplays =
1092 getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
1093
1094 // PointerChoreographer uses Listener's lock.
1095 base::ScopedLockAssertion assumeLocked(mPointerChoreographer->getLock());
1096 if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
1097 mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
1098 mPointerChoreographer->onPrivacySensitiveDisplaysChangedLocked(mPrivacySensitiveDisplays);
1099 }
1100 }
1101
setInitialDisplayInfosLocked(const std::vector<gui::WindowInfo> & windowInfos)1102 void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfosLocked(
1103 const std::vector<gui::WindowInfo>& windowInfos) {
1104 mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
1105 }
1106
1107 std::unordered_set<ui::LogicalDisplayId /*displayId*/>
getPrivacySensitiveDisplaysLocked()1108 PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplaysLocked() {
1109 return mPrivacySensitiveDisplays;
1110 }
1111
1112 void PointerChoreographer::PointerChoreographerDisplayInfoListener::
onPointerChoreographerDestroyed()1113 onPointerChoreographerDestroyed() {
1114 std::scoped_lock _l(mLock);
1115 mPointerChoreographer = nullptr;
1116 }
1117
1118 } // namespace android
1119