blob: 014e7c424f63abe86598c43440b8acdbfaa80070 [file] [log] [blame]
Prabir Pradhanb56e92c2023-06-09 23:40:37 +00001/*
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
Byoungho Jungda10dd32023-10-06 17:03:45 +090019#include <android-base/logging.h>
Arpit Singh4b6ad2d2024-04-04 11:54:20 +000020#include <com_android_input_flags.h>
21#if defined(__ANDROID__)
22#include <gui/SurfaceComposerClient.h>
23#endif
Arpit Singhb65e2bd2024-06-03 09:48:16 +000024#include <input/Keyboard.h>
Byoungho Jungda10dd32023-10-06 17:03:45 +090025#include <input/PrintTools.h>
Arpit Singh4b6ad2d2024-04-04 11:54:20 +000026#include <unordered_set>
Byoungho Jungda10dd32023-10-06 17:03:45 +090027
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000028#include "PointerChoreographer.h"
29
Byoungho Jungda10dd32023-10-06 17:03:45 +090030#define INDENT " "
31
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000032namespace android {
33
Byoungho Jungda10dd32023-10-06 17:03:45 +090034namespace {
Prabir Pradhan5a51a222024-03-05 03:54:00 +000035
Byoungho Jungda10dd32023-10-06 17:03:45 +090036bool isFromMouse(const NotifyMotionArgs& args) {
37 return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
38 args.pointerProperties[0].toolType == ToolType::MOUSE;
39}
40
Byoungho Jungee6268f2023-10-30 17:27:26 +090041bool isFromTouchpad(const NotifyMotionArgs& args) {
42 return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
43 args.pointerProperties[0].toolType == ToolType::FINGER;
44}
45
Prabir Pradhan4c977a42024-03-15 16:47:37 +000046bool isFromDrawingTablet(const NotifyMotionArgs& args) {
47 return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
48 isStylusToolType(args.pointerProperties[0].toolType);
49}
50
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090051bool isHoverAction(int32_t action) {
52 return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
53 action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
54}
55
56bool isStylusHoverEvent(const NotifyMotionArgs& args) {
57 return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
58}
Prabir Pradhan5a51a222024-03-05 03:54:00 +000059
Prabir Pradhan4c977a42024-03-15 16:47:37 +000060bool isMouseOrTouchpad(uint32_t sources) {
61 // Check if this is a mouse or touchpad, but not a drawing tablet.
62 return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
63 (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
64 !isFromSource(sources, AINPUT_SOURCE_STYLUS));
65}
66
Linnan Li13bf76a2024-05-05 19:18:02 +080067inline void notifyPointerDisplayChange(
68 std::optional<std::tuple<ui::LogicalDisplayId, FloatPoint>> change,
69 PointerChoreographerPolicyInterface& policy) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +000070 if (!change) {
71 return;
72 }
73 const auto& [displayId, cursorPosition] = *change;
74 policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
75}
76
Prabir Pradhan4c977a42024-03-15 16:47:37 +000077void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
78 PointerControllerInterface& controller) {
79 if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
80 if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
81 LOG(FATAL) << "SpriteIcon should not be null";
82 }
83 controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
84 } else {
85 controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
86 }
87}
88
Arpit Singh420d0742024-04-04 11:54:20 +000089// filters and returns a set of privacy sensitive displays that are currently visible.
90std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
91 const std::vector<gui::WindowInfo>& windowInfos) {
92 std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
93 for (const auto& windowInfo : windowInfos) {
94 if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
95 windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
96 privacySensitiveDisplays.insert(windowInfo.displayId);
97 }
98 }
99 return privacySensitiveDisplays;
100}
101
Byoungho Jungda10dd32023-10-06 17:03:45 +0900102} // namespace
103
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000104// --- PointerChoreographer ---
105
Arpit Singha5ba9f12024-10-25 20:59:24 +0000106const bool PointerChoreographer::IS_TOPOLOGY_AWARE =
107 com::android::input::flags::connected_displays_cursor();
108
Arpit Singhbd49b282024-05-23 18:02:54 +0000109PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000110 PointerChoreographerPolicyInterface& policy)
Arpit Singhbd49b282024-05-23 18:02:54 +0000111 : PointerChoreographer(
112 inputListener, policy,
113 [](const sp<android::gui::WindowInfosListener>& listener) {
114 auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
115 std::vector<android::gui::DisplayInfo>{});
116#if defined(__ANDROID__)
117 SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
118 &initialInfo);
119#endif
120 return initialInfo.first;
121 },
122 [](const sp<android::gui::WindowInfosListener>& listener) {
123#if defined(__ANDROID__)
124 SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
125#endif
126 }) {
127}
128
129PointerChoreographer::PointerChoreographer(
130 android::InputListenerInterface& listener,
131 android::PointerChoreographerPolicyInterface& policy,
132 const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
133 const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000134 : mTouchControllerConstructor([this]() {
Prabir Pradhan16788792023-11-08 21:07:21 +0000135 return mPolicy.createPointerController(
136 PointerControllerInterface::ControllerType::TOUCH);
137 }),
138 mNextListener(listener),
Byoungho Jungda10dd32023-10-06 17:03:45 +0900139 mPolicy(policy),
Siarhei Vishniakoucfbee532024-05-10 13:41:35 -0700140 mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
141 mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900142 mShowTouchesEnabled(false),
Arpit Singhbd49b282024-05-23 18:02:54 +0000143 mStylusPointerIconEnabled(false),
Arpit Singhb65e2bd2024-06-03 09:48:16 +0000144 mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
Arpit Singhbd49b282024-05-23 18:02:54 +0000145 mRegisterListener(registerListener),
146 mUnregisterListener(unregisterListener) {}
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000147
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000148PointerChoreographer::~PointerChoreographer() {
149 std::scoped_lock _l(mLock);
150 if (mWindowInfoListener == nullptr) {
151 return;
152 }
153 mWindowInfoListener->onPointerChoreographerDestroyed();
Arpit Singhbd49b282024-05-23 18:02:54 +0000154 mUnregisterListener(mWindowInfoListener);
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000155}
156
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000157void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000158 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900159
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000160 { // acquire lock
161 std::scoped_lock _l(mLock);
162
163 mInputDeviceInfos = args.inputDeviceInfos;
164 pointerDisplayChange = updatePointerControllersLocked();
165 } // release lock
166
167 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000168 mNextListener.notify(args);
169}
170
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000171void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
Arpit Singhb65e2bd2024-06-03 09:48:16 +0000172 fadeMouseCursorOnKeyPress(args);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000173 mNextListener.notify(args);
174}
175
176void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900177 NotifyMotionArgs newArgs = processMotion(args);
178
179 mNextListener.notify(newArgs);
180}
181
Arpit Singhb65e2bd2024-06-03 09:48:16 +0000182void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
183 if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
184 return;
185 }
186 // Meta state for these keys is ignored for dismissing cursor while typing
187 constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
188 AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
189 if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
190 // Do not fade if any other meta state is active
191 return;
192 }
193 if (!mPolicy.isInputMethodConnectionActive()) {
194 return;
195 }
196
197 std::scoped_lock _l(mLock);
198 ui::LogicalDisplayId targetDisplay = args.displayId;
199 if (targetDisplay == ui::LogicalDisplayId::INVALID) {
200 targetDisplay = mCurrentFocusedDisplay;
201 }
202 auto it = mMousePointersByDisplay.find(targetDisplay);
203 if (it != mMousePointersByDisplay.end()) {
Arpit Singh849beb42024-06-06 07:14:17 +0000204 mPolicy.notifyMouseCursorFadedOnTyping();
Arpit Singhb65e2bd2024-06-03 09:48:16 +0000205 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
206 }
207}
208
Byoungho Jungda10dd32023-10-06 17:03:45 +0900209NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
Arpit Singha5ba9f12024-10-25 20:59:24 +0000210 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
Byoungho Jungda10dd32023-10-06 17:03:45 +0900228
Arpit Singha5ba9f12024-10-25 20:59:24 +0000229 if (pointerDisplayChange) {
230 // pointer display may have changed if mouse crossed display boundary
231 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900232 }
Arpit Singha5ba9f12024-10-25 20:59:24 +0000233 return newArgs;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900234}
235
236NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
237 if (args.getPointerCount() != 1) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000238 LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
239 << args.dump();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900240 }
241
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000242 mMouseDevices.emplace(args.deviceId);
Prabir Pradhan990d8712024-03-05 00:31:36 +0000243 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Nergi Rahardie0a4cfe2024-03-11 13:18:59 +0900244 NotifyMotionArgs newArgs(args);
245 newArgs.displayId = displayId;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900246
Nergi Rahardie0a4cfe2024-03-11 13:18:59 +0900247 if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
248 // This is an absolute mouse device that knows about the location of the cursor on the
249 // display, so set the cursor position to the specified location.
250 const auto [x, y] = pc.getPosition();
251 const float deltaX = args.xCursorPosition - x;
252 const float deltaY = args.yCursorPosition - y;
253 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
254 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
255 pc.setPosition(args.xCursorPosition, args.yCursorPosition);
256 } else {
257 // This is a relative mouse, so move the cursor by the specified amount.
Arpit Singha5ba9f12024-10-25 20:59:24 +0000258 processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
Nergi Rahardie0a4cfe2024-03-11 13:18:59 +0900259 }
Arpit Singha5ba9f12024-10-25 20:59:24 +0000260 // Note displayId may have changed if the cursor moved to a different display
261 if (canUnfadeOnDisplay(newArgs.displayId)) {
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000262 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
263 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900264 return newArgs;
265}
266
Byoungho Jungee6268f2023-10-30 17:27:26 +0900267NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000268 mMouseDevices.emplace(args.deviceId);
Prabir Pradhan990d8712024-03-05 00:31:36 +0000269 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900270
271 NotifyMotionArgs newArgs(args);
272 newArgs.displayId = displayId;
273 if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
274 // This is a movement of the mouse pointer.
Arpit Singha5ba9f12024-10-25 20:59:24 +0000275 processPointerDeviceMotionEventLocked(/*byref*/ newArgs, /*byref*/ pc);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900276 } else {
277 // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
Byoungho Jungee6268f2023-10-30 17:27:26 +0900278 const auto [x, y] = pc.getPosition();
279 for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
280 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
281 args.pointerCoords[i].getX() + x);
282 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
283 args.pointerCoords[i].getY() + y);
284 }
285 newArgs.xCursorPosition = x;
286 newArgs.yCursorPosition = y;
287 }
Arpit Singha5ba9f12024-10-25 20:59:24 +0000288
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 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900293 return newArgs;
294}
295
Arpit Singha5ba9f12024-10-25 20:59:24 +0000296void 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
313void 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
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000343void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
Siarhei Vishniakoucfbee532024-05-10 13:41:35 -0700344 if (args.displayId == ui::LogicalDisplayId::INVALID) {
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000345 return;
346 }
347
348 if (args.getPointerCount() != 1) {
349 LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
350 << args.dump();
351 }
352
353 // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
Arpit Singh420d0742024-04-04 11:54:20 +0000354 auto [it, controllerAdded] =
355 mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
356 getMouseControllerConstructor(
357 args.displayId));
358 if (controllerAdded) {
359 onControllerAddedOrRemovedLocked();
360 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000361
362 PointerControllerInterface& pc = *it->second;
363
364 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
365 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
366 pc.setPosition(x, y);
367 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
368 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
369 // immediately by a DOWN event.
370 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
371 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
372 } else if (canUnfadeOnDisplay(args.displayId)) {
373 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
374 }
375}
376
Byoungho Jungda10dd32023-10-06 17:03:45 +0900377/**
378 * When screen is touched, fade the mouse pointer on that display. We only call fade for
379 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
380 * mouse device keeps moving and unfades the cursor.
381 * For touch events, we do not need to populate the cursor position.
382 */
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900383void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800384 if (!args.displayId.isValid()) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900385 return;
386 }
387
Byoungho Jungda10dd32023-10-06 17:03:45 +0900388 if (const auto it = mMousePointersByDisplay.find(args.displayId);
389 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
390 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
391 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900392
393 if (!mShowTouchesEnabled) {
394 return;
395 }
396
397 // Get the touch pointer controller for the device, or create one if it doesn't exist.
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000398 auto [it, controllerAdded] =
399 mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
400 if (controllerAdded) {
Arpit Singh420d0742024-04-04 11:54:20 +0000401 onControllerAddedOrRemovedLocked();
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000402 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900403
404 PointerControllerInterface& pc = *it->second;
405
406 const PointerCoords* coords = args.pointerCoords.data();
407 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
408 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
409 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
410 BitSet32 idBits;
Linnan Li45b321e2024-07-17 19:33:21 +0000411 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL &&
412 maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900413 for (size_t i = 0; i < args.getPointerCount(); i++) {
414 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
415 continue;
416 }
417 uint32_t id = args.pointerProperties[i].id;
418 idToIndex[id] = i;
419 idBits.markBit(id);
420 }
421 }
422 // The PointerController already handles setting spots per-display, so
423 // we do not need to manually manage display changes for touch spots for now.
424 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000425}
426
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900427void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800428 if (!args.displayId.isValid()) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900429 return;
430 }
431
432 if (args.getPointerCount() != 1) {
433 LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
434 << args.dump();
435 }
436
437 // Get the stylus pointer controller for the device, or create one if it doesn't exist.
Arpit Singh420d0742024-04-04 11:54:20 +0000438 auto [it, controllerAdded] =
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900439 mStylusPointersByDevice.try_emplace(args.deviceId,
440 getStylusControllerConstructor(args.displayId));
Arpit Singh420d0742024-04-04 11:54:20 +0000441 if (controllerAdded) {
442 onControllerAddedOrRemovedLocked();
443 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900444
445 PointerControllerInterface& pc = *it->second;
446
447 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
448 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
449 pc.setPosition(x, y);
450 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000451 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
452 // immediately by a DOWN event.
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900453 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
Prabir Pradhan888993d2024-09-17 20:01:48 +0000454 pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER
455 : PointerIconStyle::TYPE_NOT_SPECIFIED);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000456 } else if (canUnfadeOnDisplay(args.displayId)) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900457 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
458 }
459}
460
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000461void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
462 mNextListener.notify(args);
463}
464
465void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
466 mNextListener.notify(args);
467}
468
469void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
470 mNextListener.notify(args);
471}
472
473void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900474 processDeviceReset(args);
475
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000476 mNextListener.notify(args);
477}
478
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900479void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
480 std::scoped_lock _l(mLock);
Prabir Pradhan16788792023-11-08 21:07:21 +0000481 mTouchPointersByDevice.erase(args.deviceId);
482 mStylusPointersByDevice.erase(args.deviceId);
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000483 mDrawingTabletPointersByDevice.erase(args.deviceId);
Arpit Singh420d0742024-04-04 11:54:20 +0000484 onControllerAddedOrRemovedLocked();
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000485}
486
Arpit Singh420d0742024-04-04 11:54:20 +0000487void PointerChoreographer::onControllerAddedOrRemovedLocked() {
Arpit Singha5ba9f12024-10-25 20:59:24 +0000488 if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows() &&
489 !IS_TOPOLOGY_AWARE) {
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000490 return;
491 }
Arpit Singh420d0742024-04-04 11:54:20 +0000492 bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
493 !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000494
495 if (requireListener && mWindowInfoListener == nullptr) {
496 mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
Arpit Singhbd49b282024-05-23 18:02:54 +0000497 mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
Arpit Singh420d0742024-04-04 11:54:20 +0000498 onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000499 } else if (!requireListener && mWindowInfoListener != nullptr) {
Arpit Singhbd49b282024-05-23 18:02:54 +0000500 mUnregisterListener(mWindowInfoListener);
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000501 mWindowInfoListener = nullptr;
Arpit Singh420d0742024-04-04 11:54:20 +0000502 } else if (requireListener && mWindowInfoListener != nullptr) {
503 // controller may have been added to an existing privacy sensitive display, we need to
504 // update all controllers again
505 onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
506 }
507}
508
509void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
510 const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
511 for (auto& [_, pc] : mTouchPointersByDevice) {
512 pc->clearSkipScreenshotFlags();
513 for (auto displayId : privacySensitiveDisplays) {
514 pc->setSkipScreenshotFlagForDisplay(displayId);
515 }
516 }
517
518 for (auto& [displayId, pc] : mMousePointersByDisplay) {
519 if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
520 pc->setSkipScreenshotFlagForDisplay(displayId);
521 } else {
522 pc->clearSkipScreenshotFlags();
523 }
524 }
525
526 for (auto* pointerControllerByDevice :
527 {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
528 for (auto& [_, pc] : *pointerControllerByDevice) {
529 auto displayId = pc->getDisplayId();
530 if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
531 pc->setSkipScreenshotFlagForDisplay(displayId);
532 } else {
533 pc->clearSkipScreenshotFlags();
534 }
535 }
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000536 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900537}
538
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000539void PointerChoreographer::notifyPointerCaptureChanged(
540 const NotifyPointerCaptureChangedArgs& args) {
Hiroki Sato25040232024-02-22 17:21:22 +0900541 if (args.request.isEnable()) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900542 std::scoped_lock _l(mLock);
543 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
544 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
545 }
546 }
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000547 mNextListener.notify(args);
548}
549
Arpit Singh420d0742024-04-04 11:54:20 +0000550void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
551 const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000552 std::scoped_lock _l(mLock);
Arpit Singh420d0742024-04-04 11:54:20 +0000553 onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000554}
555
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000556void PointerChoreographer::dump(std::string& dump) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900557 std::scoped_lock _l(mLock);
558
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000559 dump += "PointerChoreographer:\n";
Harry Cuttsebd418a2024-08-16 15:52:24 +0000560 dump += StringPrintf(INDENT "Show Touches Enabled: %s\n",
561 mShowTouchesEnabled ? "true" : "false");
562 dump += StringPrintf(INDENT "Stylus PointerIcon Enabled: %s\n",
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900563 mStylusPointerIconEnabled ? "true" : "false");
Byoungho Jungda10dd32023-10-06 17:03:45 +0900564
565 dump += INDENT "MousePointerControllers:\n";
566 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
567 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
Linnan Li13bf76a2024-05-05 19:18:02 +0800568 dump += INDENT + displayId.toString() + " : " + pointerControllerDump;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900569 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900570 dump += INDENT "TouchPointerControllers:\n";
571 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
572 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
573 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
574 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900575 dump += INDENT "StylusPointerControllers:\n";
576 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
577 std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
578 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
579 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000580 dump += INDENT "DrawingTabletControllers:\n";
581 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
582 std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
583 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
584 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900585 dump += "\n";
586}
587
Linnan Li13bf76a2024-05-05 19:18:02 +0800588const DisplayViewport* PointerChoreographer::findViewportByIdLocked(
589 ui::LogicalDisplayId displayId) const {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900590 for (auto& viewport : mViewports) {
591 if (viewport.displayId == displayId) {
592 return &viewport;
593 }
594 }
595 return nullptr;
596}
597
Linnan Li13bf76a2024-05-05 19:18:02 +0800598ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
599 ui::LogicalDisplayId associatedDisplayId) const {
600 return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900601}
602
Linnan Li13bf76a2024-05-05 19:18:02 +0800603std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
604PointerChoreographer::ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) {
605 const ui::LogicalDisplayId displayId = getTargetMouseDisplayLocked(associatedDisplayId);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900606
Prabir Pradhan990d8712024-03-05 00:31:36 +0000607 auto it = mMousePointersByDisplay.find(displayId);
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000608 if (it == mMousePointersByDisplay.end()) {
609 it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
610 .first;
Arpit Singh420d0742024-04-04 11:54:20 +0000611 onControllerAddedOrRemovedLocked();
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000612 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900613
614 return {displayId, *it->second};
615}
616
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900617InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000618 auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
619 [deviceId](const auto& info) { return info.getId() == deviceId; });
620 return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900621}
622
Linnan Li13bf76a2024-05-05 19:18:02 +0800623bool PointerChoreographer::canUnfadeOnDisplay(ui::LogicalDisplayId displayId) {
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000624 return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
625}
626
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000627PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
Linnan Li13bf76a2024-05-05 19:18:02 +0800628 std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900629 std::set<DeviceId> touchDevicesToKeep;
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900630 std::set<DeviceId> stylusDevicesToKeep;
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000631 std::set<DeviceId> drawingTabletDevicesToKeep;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900632
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000633 // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
634 // new PointerControllers if necessary.
Byoungho Jungda10dd32023-10-06 17:03:45 +0900635 for (const auto& info : mInputDeviceInfos) {
Linnan Li48f80da2024-04-22 18:38:16 +0000636 if (!info.isEnabled()) {
637 // If device is disabled, we should not keep it, and should not show pointer for
638 // disabled mouse device.
639 continue;
640 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900641 const uint32_t sources = info.getSources();
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000642 const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
643
644 if (isMouseOrTouchpad(sources) || isKnownMouse) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800645 const ui::LogicalDisplayId displayId =
646 getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000647 mouseDisplaysToKeep.insert(displayId);
648 // For mice, show the cursor immediately when the device is first connected or
649 // when it moves to a new display.
650 auto [mousePointerIt, isNewMousePointer] =
651 mMousePointersByDisplay.try_emplace(displayId,
652 getMouseControllerConstructor(displayId));
Arpit Singh420d0742024-04-04 11:54:20 +0000653 if (isNewMousePointer) {
654 onControllerAddedOrRemovedLocked();
655 }
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000656
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000657 mMouseDevices.emplace(info.getId());
658 if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000659 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
660 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900661 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900662 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
Linnan Li13bf76a2024-05-05 19:18:02 +0800663 info.getAssociatedDisplayId().isValid()) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900664 touchDevicesToKeep.insert(info.getId());
665 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900666 if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
Linnan Li13bf76a2024-05-05 19:18:02 +0800667 info.getAssociatedDisplayId().isValid()) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900668 stylusDevicesToKeep.insert(info.getId());
669 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000670 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
Linnan Li13bf76a2024-05-05 19:18:02 +0800671 info.getAssociatedDisplayId().isValid()) {
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000672 drawingTabletDevicesToKeep.insert(info.getId());
673 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900674 }
675
676 // Remove PointerControllers no longer needed.
Prabir Pradhan19767602023-11-03 16:53:31 +0000677 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000678 return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900679 });
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900680 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000681 return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900682 });
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900683 std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000684 return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900685 });
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000686 std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
687 return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
688 });
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000689 std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
690 return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
691 [id](const auto& info) { return info.getId() == id; }) ==
692 mInputDeviceInfos.end();
693 });
Byoungho Jungda10dd32023-10-06 17:03:45 +0900694
Arpit Singh420d0742024-04-04 11:54:20 +0000695 onControllerAddedOrRemovedLocked();
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000696
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000697 // Check if we need to notify the policy if there's a change on the pointer display ID.
698 return calculatePointerDisplayChangeToNotify();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900699}
700
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000701PointerChoreographer::PointerDisplayChange
702PointerChoreographer::calculatePointerDisplayChangeToNotify() {
Siarhei Vishniakoucfbee532024-05-10 13:41:35 -0700703 ui::LogicalDisplayId displayIdToNotify = ui::LogicalDisplayId::INVALID;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900704 FloatPoint cursorPosition = {0, 0};
705 if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
706 it != mMousePointersByDisplay.end()) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000707 const auto& pointerController = it->second;
708 // Use the displayId from the pointerController, because it accurately reflects whether
709 // the viewport has been added for that display. Otherwise, we would have to check if
710 // the viewport exists separately.
711 displayIdToNotify = pointerController->getDisplayId();
712 cursorPosition = pointerController->getPosition();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900713 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900714 if (mNotifiedPointerDisplayId == displayIdToNotify) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000715 return {};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900716 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900717 mNotifiedPointerDisplayId = displayIdToNotify;
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000718 return {{displayIdToNotify, cursorPosition}};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900719}
720
Linnan Li13bf76a2024-05-05 19:18:02 +0800721void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
Arpit Singha5ba9f12024-10-25 20:59:24 +0000722 if (IS_TOPOLOGY_AWARE) {
723 // default display will be set based on the topology
724 return;
725 }
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000726 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900727
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000728 { // acquire lock
729 std::scoped_lock _l(mLock);
730
731 mDefaultMouseDisplayId = displayId;
732 pointerDisplayChange = updatePointerControllersLocked();
733 } // release lock
734
735 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900736}
737
738void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000739 PointerDisplayChange pointerDisplayChange;
740
741 { // acquire lock
742 std::scoped_lock _l(mLock);
743 for (const auto& viewport : viewports) {
Linnan Li13bf76a2024-05-05 19:18:02 +0800744 const ui::LogicalDisplayId displayId = viewport.displayId;
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000745 if (const auto it = mMousePointersByDisplay.find(displayId);
746 it != mMousePointersByDisplay.end()) {
747 it->second->setDisplayViewport(viewport);
748 }
749 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
750 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
751 if (info && info->getAssociatedDisplayId() == displayId) {
752 stylusPointerController->setDisplayViewport(viewport);
753 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900754 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000755 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
756 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
757 if (info && info->getAssociatedDisplayId() == displayId) {
758 drawingTabletController->setDisplayViewport(viewport);
759 }
760 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900761 }
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000762 mViewports = viewports;
763 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
764 } // release lock
765
766 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900767}
768
769std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
Linnan Li13bf76a2024-05-05 19:18:02 +0800770 ui::LogicalDisplayId associatedDisplayId) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900771 std::scoped_lock _l(mLock);
Linnan Li13bf76a2024-05-05 19:18:02 +0800772 const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900773 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
774 return *viewport;
775 }
776 return std::nullopt;
777}
778
Linnan Li13bf76a2024-05-05 19:18:02 +0800779FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900780 std::scoped_lock _l(mLock);
Linnan Li13bf76a2024-05-05 19:18:02 +0800781 const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900782 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
783 it != mMousePointersByDisplay.end()) {
784 return it->second->getPosition();
785 }
786 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000787}
788
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900789void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000790 PointerDisplayChange pointerDisplayChange;
791
792 { // acquire lock
793 std::scoped_lock _l(mLock);
794 if (mShowTouchesEnabled == enabled) {
795 return;
796 }
797 mShowTouchesEnabled = enabled;
798 pointerDisplayChange = updatePointerControllersLocked();
799 } // release lock
800
801 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900802}
803
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900804void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000805 PointerDisplayChange pointerDisplayChange;
806
807 { // acquire lock
808 std::scoped_lock _l(mLock);
809 if (mStylusPointerIconEnabled == enabled) {
810 return;
811 }
812 mStylusPointerIconEnabled = enabled;
813 pointerDisplayChange = updatePointerControllersLocked();
814 } // release lock
815
816 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900817}
818
Byoungho Jung99326452023-11-03 20:19:17 +0900819bool PointerChoreographer::setPointerIcon(
Linnan Li13bf76a2024-05-05 19:18:02 +0800820 std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
821 ui::LogicalDisplayId displayId, DeviceId deviceId) {
Byoungho Jung99326452023-11-03 20:19:17 +0900822 std::scoped_lock _l(mLock);
823 if (deviceId < 0) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000824 LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900825 return false;
826 }
827 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
828 if (!info) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000829 LOG(WARNING) << "No input device info found for id " << deviceId
830 << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900831 return false;
832 }
833 const uint32_t sources = info->getSources();
Byoungho Jung99326452023-11-03 20:19:17 +0900834
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000835 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
836 auto it = mDrawingTabletPointersByDevice.find(deviceId);
837 if (it != mDrawingTabletPointersByDevice.end()) {
838 setIconForController(icon, *it->second);
839 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900840 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000841 }
842 if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
843 auto it = mStylusPointersByDevice.find(deviceId);
844 if (it != mStylusPointersByDevice.end()) {
Prabir Pradhan888993d2024-09-17 20:01:48 +0000845 if (mShowTouchesEnabled) {
846 // If an app doesn't override the icon for the hovering stylus, show the hover icon.
847 auto* style = std::get_if<PointerIconStyle>(&icon);
848 if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) {
849 *style = PointerIconStyle::TYPE_SPOT_HOVER;
850 }
851 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000852 setIconForController(icon, *it->second);
853 return true;
854 }
855 }
856 if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
857 auto it = mMousePointersByDisplay.find(displayId);
858 if (it != mMousePointersByDisplay.end()) {
859 setIconForController(icon, *it->second);
860 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900861 } else {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000862 LOG(WARNING) << "No mouse pointer controller found for display " << displayId
863 << ", device " << deviceId << ".";
Byoungho Jung99326452023-11-03 20:19:17 +0900864 return false;
865 }
Byoungho Jung99326452023-11-03 20:19:17 +0900866 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000867 LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
868 << ".";
869 return false;
Byoungho Jung99326452023-11-03 20:19:17 +0900870}
871
Linnan Li13bf76a2024-05-05 19:18:02 +0800872void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000873 std::scoped_lock lock(mLock);
874 if (visible) {
875 mDisplaysWithPointersHidden.erase(displayId);
876 // We do not unfade the icons here, because we don't know when the last event happened.
877 return;
878 }
879
880 mDisplaysWithPointersHidden.emplace(displayId);
881
882 // Hide any icons that are currently visible on the display.
883 if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
884 const auto& [_, controller] = *it;
885 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
886 }
887 for (const auto& [_, controller] : mStylusPointersByDevice) {
888 if (controller->getDisplayId() == displayId) {
889 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
890 }
891 }
892}
893
Arpit Singhb65e2bd2024-06-03 09:48:16 +0000894void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
895 std::scoped_lock lock(mLock);
896 mCurrentFocusedDisplay = displayId;
897}
898
Prabir Pradhan19767602023-11-03 16:53:31 +0000899PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
Linnan Li13bf76a2024-05-05 19:18:02 +0800900 ui::LogicalDisplayId displayId) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000901 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
902 [this, displayId]() REQUIRES(mLock) {
903 auto pc = mPolicy.createPointerController(
904 PointerControllerInterface::ControllerType::MOUSE);
905 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
906 pc->setDisplayViewport(*viewport);
907 }
908 return pc;
909 };
910 return ConstructorDelegate(std::move(ctor));
911}
912
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900913PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
Linnan Li13bf76a2024-05-05 19:18:02 +0800914 ui::LogicalDisplayId displayId) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900915 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
916 [this, displayId]() REQUIRES(mLock) {
917 auto pc = mPolicy.createPointerController(
918 PointerControllerInterface::ControllerType::STYLUS);
919 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
920 pc->setDisplayViewport(*viewport);
921 }
922 return pc;
923 };
924 return ConstructorDelegate(std::move(ctor));
925}
926
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000927void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
928 const gui::WindowInfosUpdate& windowInfosUpdate) {
929 std::scoped_lock _l(mListenerLock);
Arpit Singh420d0742024-04-04 11:54:20 +0000930 if (mPointerChoreographer == nullptr) {
931 return;
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000932 }
Arpit Singh420d0742024-04-04 11:54:20 +0000933 auto newPrivacySensitiveDisplays =
934 getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
935 if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
936 mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
937 mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
938 }
Arpit Singha5ba9f12024-10-25 20:59:24 +0000939 mPointerChoreographer->populateFakeDisplayTopology(windowInfosUpdate.displayInfos);
Arpit Singh420d0742024-04-04 11:54:20 +0000940}
941
942void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
943 const std::vector<gui::WindowInfo>& windowInfos) {
944 std::scoped_lock _l(mListenerLock);
945 mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
946}
947
948std::unordered_set<ui::LogicalDisplayId /*displayId*/>
949PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
950 std::scoped_lock _l(mListenerLock);
951 return mPrivacySensitiveDisplays;
Arpit Singh4b6ad2d2024-04-04 11:54:20 +0000952}
953
954void PointerChoreographer::PointerChoreographerDisplayInfoListener::
955 onPointerChoreographerDestroyed() {
956 std::scoped_lock _l(mListenerLock);
957 mPointerChoreographer = nullptr;
958}
959
Arpit Singha5ba9f12024-10-25 20:59:24 +0000960void 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
1014std::optional<PointerChoreographer::AdjacentDisplay>
1015PointerChoreographer::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
Prabir Pradhanb56e92c2023-06-09 23:40:37 +00001049} // namespace android