blob: a3d6d4adec7bcfae0c51c84418b84004e26db399 [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>
20#include <input/PrintTools.h>
21
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000022#include "PointerChoreographer.h"
23
Byoungho Jungda10dd32023-10-06 17:03:45 +090024#define INDENT " "
25
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000026namespace android {
27
Byoungho Jungda10dd32023-10-06 17:03:45 +090028namespace {
Prabir Pradhana955ada2024-03-05 03:54:00 +000029
Byoungho Jungda10dd32023-10-06 17:03:45 +090030bool isFromMouse(const NotifyMotionArgs& args) {
31 return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
32 args.pointerProperties[0].toolType == ToolType::MOUSE;
33}
34
Byoungho Jungee6268f2023-10-30 17:27:26 +090035bool isFromTouchpad(const NotifyMotionArgs& args) {
36 return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
37 args.pointerProperties[0].toolType == ToolType::FINGER;
38}
39
Prabir Pradhan7fe89272024-03-15 16:47:37 +000040bool isFromDrawingTablet(const NotifyMotionArgs& args) {
41 return isFromSource(args.source, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS) &&
42 isStylusToolType(args.pointerProperties[0].toolType);
43}
44
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090045bool isHoverAction(int32_t action) {
46 return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
47 action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
48}
49
50bool isStylusHoverEvent(const NotifyMotionArgs& args) {
51 return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
52}
Prabir Pradhana955ada2024-03-05 03:54:00 +000053
Prabir Pradhan7fe89272024-03-15 16:47:37 +000054bool isMouseOrTouchpad(uint32_t sources) {
55 // Check if this is a mouse or touchpad, but not a drawing tablet.
56 return isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE) ||
57 (isFromSource(sources, AINPUT_SOURCE_MOUSE) &&
58 !isFromSource(sources, AINPUT_SOURCE_STYLUS));
59}
60
Prabir Pradhana955ada2024-03-05 03:54:00 +000061inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change,
62 PointerChoreographerPolicyInterface& policy) {
63 if (!change) {
64 return;
65 }
66 const auto& [displayId, cursorPosition] = *change;
67 policy.notifyPointerDisplayIdChanged(displayId, cursorPosition);
68}
69
Prabir Pradhan7fe89272024-03-15 16:47:37 +000070void setIconForController(const std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle>& icon,
71 PointerControllerInterface& controller) {
72 if (std::holds_alternative<std::unique_ptr<SpriteIcon>>(icon)) {
73 if (std::get<std::unique_ptr<SpriteIcon>>(icon) == nullptr) {
74 LOG(FATAL) << "SpriteIcon should not be null";
75 }
76 controller.setCustomPointerIcon(*std::get<std::unique_ptr<SpriteIcon>>(icon));
77 } else {
78 controller.updatePointerIcon(std::get<PointerIconStyle>(icon));
79 }
80}
81
Byoungho Jungda10dd32023-10-06 17:03:45 +090082} // namespace
83
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000084// --- PointerChoreographer ---
85
86PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
87 PointerChoreographerPolicyInterface& policy)
Prabir Pradhana955ada2024-03-05 03:54:00 +000088 : mTouchControllerConstructor([this]() {
Prabir Pradhan16788792023-11-08 21:07:21 +000089 return mPolicy.createPointerController(
90 PointerControllerInterface::ControllerType::TOUCH);
91 }),
92 mNextListener(listener),
Byoungho Jungda10dd32023-10-06 17:03:45 +090093 mPolicy(policy),
94 mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
Byoungho Jung6f5b16b2023-10-27 18:22:07 +090095 mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090096 mShowTouchesEnabled(false),
97 mStylusPointerIconEnabled(false) {}
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000098
99void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000100 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900101
Prabir Pradhana955ada2024-03-05 03:54:00 +0000102 { // acquire lock
103 std::scoped_lock _l(mLock);
104
105 mInputDeviceInfos = args.inputDeviceInfos;
106 pointerDisplayChange = updatePointerControllersLocked();
107 } // release lock
108
109 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000110 mNextListener.notify(args);
111}
112
113void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
114 mNextListener.notify(args);
115}
116
117void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
118 mNextListener.notify(args);
119}
120
121void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900122 NotifyMotionArgs newArgs = processMotion(args);
123
124 mNextListener.notify(newArgs);
125}
126
127NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
128 std::scoped_lock _l(mLock);
129
130 if (isFromMouse(args)) {
131 return processMouseEventLocked(args);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900132 } else if (isFromTouchpad(args)) {
133 return processTouchpadEventLocked(args);
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000134 } else if (isFromDrawingTablet(args)) {
135 processDrawingTabletEventLocked(args);
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900136 } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
137 processStylusHoverEventLocked(args);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900138 } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900139 processTouchscreenAndStylusEventLocked(args);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900140 }
141 return args;
142}
143
144NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
145 if (args.getPointerCount() != 1) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000146 LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
147 << args.dump();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900148 }
149
Prabir Pradhan30ed2c02024-03-05 00:31:36 +0000150 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900151
152 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
153 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
154 pc.move(deltaX, deltaY);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000155 if (canUnfadeOnDisplay(displayId)) {
156 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
157 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900158
159 const auto [x, y] = pc.getPosition();
160 NotifyMotionArgs newArgs(args);
161 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
162 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
163 newArgs.xCursorPosition = x;
164 newArgs.yCursorPosition = y;
165 newArgs.displayId = displayId;
166 return newArgs;
167}
168
Byoungho Jungee6268f2023-10-30 17:27:26 +0900169NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
Prabir Pradhan30ed2c02024-03-05 00:31:36 +0000170 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900171
172 NotifyMotionArgs newArgs(args);
173 newArgs.displayId = displayId;
174 if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
175 // This is a movement of the mouse pointer.
176 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
177 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
178 pc.move(deltaX, deltaY);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000179 if (canUnfadeOnDisplay(displayId)) {
180 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
181 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900182
183 const auto [x, y] = pc.getPosition();
184 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
185 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
186 newArgs.xCursorPosition = x;
187 newArgs.yCursorPosition = y;
188 } else {
189 // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000190 if (canUnfadeOnDisplay(displayId)) {
191 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
192 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900193
194 const auto [x, y] = pc.getPosition();
195 for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
196 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
197 args.pointerCoords[i].getX() + x);
198 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
199 args.pointerCoords[i].getY() + y);
200 }
201 newArgs.xCursorPosition = x;
202 newArgs.yCursorPosition = y;
203 }
204 return newArgs;
205}
206
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000207void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
208 if (args.displayId == ADISPLAY_ID_NONE) {
209 return;
210 }
211
212 if (args.getPointerCount() != 1) {
213 LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
214 << args.dump();
215 }
216
217 // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
218 auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
219 getMouseControllerConstructor(
220 args.displayId));
221
222 PointerControllerInterface& pc = *it->second;
223
224 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
225 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
226 pc.setPosition(x, y);
227 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
228 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
229 // immediately by a DOWN event.
230 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
231 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
232 } else if (canUnfadeOnDisplay(args.displayId)) {
233 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
234 }
235}
236
Byoungho Jungda10dd32023-10-06 17:03:45 +0900237/**
238 * When screen is touched, fade the mouse pointer on that display. We only call fade for
239 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
240 * mouse device keeps moving and unfades the cursor.
241 * For touch events, we do not need to populate the cursor position.
242 */
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900243void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
244 if (args.displayId == ADISPLAY_ID_NONE) {
245 return;
246 }
247
Byoungho Jungda10dd32023-10-06 17:03:45 +0900248 if (const auto it = mMousePointersByDisplay.find(args.displayId);
249 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
250 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
251 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900252
253 if (!mShowTouchesEnabled) {
254 return;
255 }
256
257 // Get the touch pointer controller for the device, or create one if it doesn't exist.
Prabir Pradhan16788792023-11-08 21:07:21 +0000258 auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900259
260 PointerControllerInterface& pc = *it->second;
261
262 const PointerCoords* coords = args.pointerCoords.data();
263 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
264 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
265 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
266 BitSet32 idBits;
Linnan Lic17f0422024-09-30 17:23:51 +0000267 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL &&
268 maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900269 for (size_t i = 0; i < args.getPointerCount(); i++) {
270 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
271 continue;
272 }
273 uint32_t id = args.pointerProperties[i].id;
274 idToIndex[id] = i;
275 idBits.markBit(id);
276 }
277 }
278 // The PointerController already handles setting spots per-display, so
279 // we do not need to manually manage display changes for touch spots for now.
280 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000281}
282
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900283void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
284 if (args.displayId == ADISPLAY_ID_NONE) {
285 return;
286 }
287
288 if (args.getPointerCount() != 1) {
289 LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
290 << args.dump();
291 }
292
293 // Get the stylus pointer controller for the device, or create one if it doesn't exist.
294 auto [it, _] =
295 mStylusPointersByDevice.try_emplace(args.deviceId,
296 getStylusControllerConstructor(args.displayId));
297
298 PointerControllerInterface& pc = *it->second;
299
300 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
301 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
302 pc.setPosition(x, y);
303 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000304 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
305 // immediately by a DOWN event.
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900306 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
Prabir Pradhan4b36db92024-01-03 20:56:57 +0000307 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000308 } else if (canUnfadeOnDisplay(args.displayId)) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900309 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
310 }
311}
312
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000313void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
314 mNextListener.notify(args);
315}
316
317void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
318 mNextListener.notify(args);
319}
320
321void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
322 mNextListener.notify(args);
323}
324
325void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900326 processDeviceReset(args);
327
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000328 mNextListener.notify(args);
329}
330
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900331void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
332 std::scoped_lock _l(mLock);
Prabir Pradhan16788792023-11-08 21:07:21 +0000333 mTouchPointersByDevice.erase(args.deviceId);
334 mStylusPointersByDevice.erase(args.deviceId);
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000335 mDrawingTabletPointersByDevice.erase(args.deviceId);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900336}
337
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000338void PointerChoreographer::notifyPointerCaptureChanged(
339 const NotifyPointerCaptureChangedArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900340 if (args.request.enable) {
341 std::scoped_lock _l(mLock);
342 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
343 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
344 }
345 }
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000346 mNextListener.notify(args);
347}
348
349void PointerChoreographer::dump(std::string& dump) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900350 std::scoped_lock _l(mLock);
351
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000352 dump += "PointerChoreographer:\n";
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900353 dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900354 dump += StringPrintf("stylus pointer icon enabled: %s\n",
355 mStylusPointerIconEnabled ? "true" : "false");
Byoungho Jungda10dd32023-10-06 17:03:45 +0900356
357 dump += INDENT "MousePointerControllers:\n";
358 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
359 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
360 dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
361 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900362 dump += INDENT "TouchPointerControllers:\n";
363 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
364 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
365 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
366 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900367 dump += INDENT "StylusPointerControllers:\n";
368 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
369 std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
370 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
371 }
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000372 dump += INDENT "DrawingTabletControllers:\n";
373 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
374 std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
375 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
376 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900377 dump += "\n";
378}
379
380const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
381 for (auto& viewport : mViewports) {
382 if (viewport.displayId == displayId) {
383 return &viewport;
384 }
385 }
386 return nullptr;
387}
388
389int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
390 return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
391}
392
Prabir Pradhan30ed2c02024-03-05 00:31:36 +0000393std::pair<int32_t, PointerControllerInterface&> PointerChoreographer::ensureMouseControllerLocked(
394 int32_t associatedDisplayId) {
Byoungho Jungee6268f2023-10-30 17:27:26 +0900395 const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
396
Prabir Pradhan30ed2c02024-03-05 00:31:36 +0000397 auto it = mMousePointersByDisplay.find(displayId);
398 LOG_ALWAYS_FATAL_IF(it == mMousePointersByDisplay.end(),
399 "There is no mouse controller created for display %d", displayId);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900400
401 return {displayId, *it->second};
402}
403
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900404InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000405 auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
406 [deviceId](const auto& info) { return info.getId() == deviceId; });
407 return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900408}
409
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000410bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) {
411 return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
412}
413
Prabir Pradhana955ada2024-03-05 03:54:00 +0000414PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900415 std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900416 std::set<DeviceId> touchDevicesToKeep;
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900417 std::set<DeviceId> stylusDevicesToKeep;
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000418 std::set<DeviceId> drawingTabletDevicesToKeep;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900419
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000420 // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
421 // new PointerControllers if necessary.
Byoungho Jungda10dd32023-10-06 17:03:45 +0900422 for (const auto& info : mInputDeviceInfos) {
423 const uint32_t sources = info.getSources();
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000424 if (isMouseOrTouchpad(sources)) {
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000425 const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
426 mouseDisplaysToKeep.insert(displayId);
427 // For mice, show the cursor immediately when the device is first connected or
428 // when it moves to a new display.
429 auto [mousePointerIt, isNewMousePointer] =
430 mMousePointersByDisplay.try_emplace(displayId,
431 getMouseControllerConstructor(displayId));
432 auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId());
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000433 if ((isNewMouseDevice || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000434 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
435 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900436 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900437 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
438 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
439 touchDevicesToKeep.insert(info.getId());
440 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900441 if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
442 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
443 stylusDevicesToKeep.insert(info.getId());
444 }
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000445 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
446 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
447 drawingTabletDevicesToKeep.insert(info.getId());
448 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900449 }
450
451 // Remove PointerControllers no longer needed.
Prabir Pradhan19767602023-11-03 16:53:31 +0000452 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000453 return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900454 });
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900455 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000456 return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900457 });
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900458 std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000459 return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900460 });
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000461 std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
462 return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
463 });
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000464 std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
465 return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
466 [id](const auto& info) { return info.getId() == id; }) ==
467 mInputDeviceInfos.end();
468 });
Byoungho Jungda10dd32023-10-06 17:03:45 +0900469
Prabir Pradhana955ada2024-03-05 03:54:00 +0000470 // Check if we need to notify the policy if there's a change on the pointer display ID.
471 return calculatePointerDisplayChangeToNotify();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900472}
473
Prabir Pradhana955ada2024-03-05 03:54:00 +0000474PointerChoreographer::PointerDisplayChange
475PointerChoreographer::calculatePointerDisplayChangeToNotify() {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900476 int32_t displayIdToNotify = ADISPLAY_ID_NONE;
477 FloatPoint cursorPosition = {0, 0};
478 if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
479 it != mMousePointersByDisplay.end()) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000480 const auto& pointerController = it->second;
481 // Use the displayId from the pointerController, because it accurately reflects whether
482 // the viewport has been added for that display. Otherwise, we would have to check if
483 // the viewport exists separately.
484 displayIdToNotify = pointerController->getDisplayId();
485 cursorPosition = pointerController->getPosition();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900486 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900487 if (mNotifiedPointerDisplayId == displayIdToNotify) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000488 return {};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900489 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900490 mNotifiedPointerDisplayId = displayIdToNotify;
Prabir Pradhana955ada2024-03-05 03:54:00 +0000491 return {{displayIdToNotify, cursorPosition}};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900492}
493
494void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000495 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900496
Prabir Pradhana955ada2024-03-05 03:54:00 +0000497 { // acquire lock
498 std::scoped_lock _l(mLock);
499
500 mDefaultMouseDisplayId = displayId;
501 pointerDisplayChange = updatePointerControllersLocked();
502 } // release lock
503
504 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900505}
506
507void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000508 PointerDisplayChange pointerDisplayChange;
509
510 { // acquire lock
511 std::scoped_lock _l(mLock);
512 for (const auto& viewport : viewports) {
513 const int32_t displayId = viewport.displayId;
514 if (const auto it = mMousePointersByDisplay.find(displayId);
515 it != mMousePointersByDisplay.end()) {
516 it->second->setDisplayViewport(viewport);
517 }
518 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
519 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
520 if (info && info->getAssociatedDisplayId() == displayId) {
521 stylusPointerController->setDisplayViewport(viewport);
522 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900523 }
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000524 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
525 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
526 if (info && info->getAssociatedDisplayId() == displayId) {
527 drawingTabletController->setDisplayViewport(viewport);
528 }
529 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900530 }
Prabir Pradhana955ada2024-03-05 03:54:00 +0000531 mViewports = viewports;
532 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
533 } // release lock
534
535 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900536}
537
538std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
539 int32_t associatedDisplayId) {
540 std::scoped_lock _l(mLock);
541 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
542 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
543 return *viewport;
544 }
545 return std::nullopt;
546}
547
548FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
549 std::scoped_lock _l(mLock);
550 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
551 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
552 it != mMousePointersByDisplay.end()) {
553 return it->second->getPosition();
554 }
555 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000556}
557
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900558void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000559 PointerDisplayChange pointerDisplayChange;
560
561 { // acquire lock
562 std::scoped_lock _l(mLock);
563 if (mShowTouchesEnabled == enabled) {
564 return;
565 }
566 mShowTouchesEnabled = enabled;
567 pointerDisplayChange = updatePointerControllersLocked();
568 } // release lock
569
570 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900571}
572
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900573void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
Prabir Pradhana955ada2024-03-05 03:54:00 +0000574 PointerDisplayChange pointerDisplayChange;
575
576 { // acquire lock
577 std::scoped_lock _l(mLock);
578 if (mStylusPointerIconEnabled == enabled) {
579 return;
580 }
581 mStylusPointerIconEnabled = enabled;
582 pointerDisplayChange = updatePointerControllersLocked();
583 } // release lock
584
585 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900586}
587
Byoungho Jung99326452023-11-03 20:19:17 +0900588bool PointerChoreographer::setPointerIcon(
589 std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId,
590 DeviceId deviceId) {
591 std::scoped_lock _l(mLock);
592 if (deviceId < 0) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000593 LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900594 return false;
595 }
596 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
597 if (!info) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000598 LOG(WARNING) << "No input device info found for id " << deviceId
599 << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900600 return false;
601 }
602 const uint32_t sources = info->getSources();
Byoungho Jung99326452023-11-03 20:19:17 +0900603
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000604 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
605 auto it = mDrawingTabletPointersByDevice.find(deviceId);
606 if (it != mDrawingTabletPointersByDevice.end()) {
607 setIconForController(icon, *it->second);
608 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900609 }
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000610 }
611 if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
612 auto it = mStylusPointersByDevice.find(deviceId);
613 if (it != mStylusPointersByDevice.end()) {
614 setIconForController(icon, *it->second);
615 return true;
616 }
617 }
618 if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
619 auto it = mMousePointersByDisplay.find(displayId);
620 if (it != mMousePointersByDisplay.end()) {
621 setIconForController(icon, *it->second);
622 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900623 } else {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000624 LOG(WARNING) << "No mouse pointer controller found for display " << displayId
625 << ", device " << deviceId << ".";
Byoungho Jung99326452023-11-03 20:19:17 +0900626 return false;
627 }
Byoungho Jung99326452023-11-03 20:19:17 +0900628 }
Prabir Pradhan7fe89272024-03-15 16:47:37 +0000629 LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
630 << ".";
631 return false;
Byoungho Jung99326452023-11-03 20:19:17 +0900632}
633
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000634void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
635 std::scoped_lock lock(mLock);
636 if (visible) {
637 mDisplaysWithPointersHidden.erase(displayId);
638 // We do not unfade the icons here, because we don't know when the last event happened.
639 return;
640 }
641
642 mDisplaysWithPointersHidden.emplace(displayId);
643
644 // Hide any icons that are currently visible on the display.
645 if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
646 const auto& [_, controller] = *it;
647 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
648 }
649 for (const auto& [_, controller] : mStylusPointersByDevice) {
650 if (controller->getDisplayId() == displayId) {
651 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
652 }
653 }
654}
655
Prabir Pradhan19767602023-11-03 16:53:31 +0000656PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
657 int32_t displayId) {
658 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
659 [this, displayId]() REQUIRES(mLock) {
660 auto pc = mPolicy.createPointerController(
661 PointerControllerInterface::ControllerType::MOUSE);
662 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
663 pc->setDisplayViewport(*viewport);
664 }
665 return pc;
666 };
667 return ConstructorDelegate(std::move(ctor));
668}
669
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900670PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
671 int32_t displayId) {
672 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
673 [this, displayId]() REQUIRES(mLock) {
674 auto pc = mPolicy.createPointerController(
675 PointerControllerInterface::ControllerType::STYLUS);
676 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
677 pc->setDisplayViewport(*viewport);
678 }
679 return pc;
680 };
681 return ConstructorDelegate(std::move(ctor));
682}
683
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000684} // namespace android