blob: 4dc273792cf1bb4babb6a9dcceb5faf22d874f67 [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 Pradhan5a51a222024-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 Pradhan4c977a42024-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 Pradhan5a51a222024-03-05 03:54:00 +000053
Prabir Pradhan4c977a42024-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 Pradhan5a51a222024-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 Pradhan4c977a42024-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 Pradhan5a51a222024-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 Pradhan5a51a222024-03-05 03:54:00 +0000100 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900101
Prabir Pradhan5a51a222024-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 Pradhan4c977a42024-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 Pradhan5a31d3c2024-03-29 20:23:22 +0000150 mMouseDevices.emplace(args.deviceId);
Prabir Pradhan990d8712024-03-05 00:31:36 +0000151 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Nergi Rahardie0a4cfe2024-03-11 13:18:59 +0900152 NotifyMotionArgs newArgs(args);
153 newArgs.displayId = displayId;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900154
Nergi Rahardie0a4cfe2024-03-11 13:18:59 +0900155 if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
156 // This is an absolute mouse device that knows about the location of the cursor on the
157 // display, so set the cursor position to the specified location.
158 const auto [x, y] = pc.getPosition();
159 const float deltaX = args.xCursorPosition - x;
160 const float deltaY = args.yCursorPosition - y;
161 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
162 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
163 pc.setPosition(args.xCursorPosition, args.yCursorPosition);
164 } else {
165 // This is a relative mouse, so move the cursor by the specified amount.
166 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
167 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
168 pc.move(deltaX, deltaY);
169 const auto [x, y] = pc.getPosition();
170 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
171 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
172 newArgs.xCursorPosition = x;
173 newArgs.yCursorPosition = y;
174 }
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000175 if (canUnfadeOnDisplay(displayId)) {
176 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
177 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900178 return newArgs;
179}
180
Byoungho Jungee6268f2023-10-30 17:27:26 +0900181NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000182 mMouseDevices.emplace(args.deviceId);
Prabir Pradhan990d8712024-03-05 00:31:36 +0000183 auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
Byoungho Jungee6268f2023-10-30 17:27:26 +0900184
185 NotifyMotionArgs newArgs(args);
186 newArgs.displayId = displayId;
187 if (args.getPointerCount() == 1 && args.classification == MotionClassification::NONE) {
188 // This is a movement of the mouse pointer.
189 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
190 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
191 pc.move(deltaX, deltaY);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000192 if (canUnfadeOnDisplay(displayId)) {
193 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
194 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900195
196 const auto [x, y] = pc.getPosition();
197 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
198 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
199 newArgs.xCursorPosition = x;
200 newArgs.yCursorPosition = y;
201 } else {
202 // This is a trackpad gesture with fake finger(s) that should not move the mouse pointer.
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000203 if (canUnfadeOnDisplay(displayId)) {
204 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
205 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900206
207 const auto [x, y] = pc.getPosition();
208 for (uint32_t i = 0; i < newArgs.getPointerCount(); i++) {
209 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
210 args.pointerCoords[i].getX() + x);
211 newArgs.pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
212 args.pointerCoords[i].getY() + y);
213 }
214 newArgs.xCursorPosition = x;
215 newArgs.yCursorPosition = y;
216 }
217 return newArgs;
218}
219
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000220void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
221 if (args.displayId == ADISPLAY_ID_NONE) {
222 return;
223 }
224
225 if (args.getPointerCount() != 1) {
226 LOG(WARNING) << "Only drawing tablet events with a single pointer are currently supported: "
227 << args.dump();
228 }
229
230 // Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
231 auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
232 getMouseControllerConstructor(
233 args.displayId));
234
235 PointerControllerInterface& pc = *it->second;
236
237 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
238 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
239 pc.setPosition(x, y);
240 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
241 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
242 // immediately by a DOWN event.
243 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
244 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
245 } else if (canUnfadeOnDisplay(args.displayId)) {
246 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
247 }
248}
249
Byoungho Jungda10dd32023-10-06 17:03:45 +0900250/**
251 * When screen is touched, fade the mouse pointer on that display. We only call fade for
252 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
253 * mouse device keeps moving and unfades the cursor.
254 * For touch events, we do not need to populate the cursor position.
255 */
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900256void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
257 if (args.displayId == ADISPLAY_ID_NONE) {
258 return;
259 }
260
Byoungho Jungda10dd32023-10-06 17:03:45 +0900261 if (const auto it = mMousePointersByDisplay.find(args.displayId);
262 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
263 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
264 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900265
266 if (!mShowTouchesEnabled) {
267 return;
268 }
269
270 // Get the touch pointer controller for the device, or create one if it doesn't exist.
Prabir Pradhan16788792023-11-08 21:07:21 +0000271 auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900272
273 PointerControllerInterface& pc = *it->second;
274
275 const PointerCoords* coords = args.pointerCoords.data();
276 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
277 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
278 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
279 BitSet32 idBits;
280 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
281 for (size_t i = 0; i < args.getPointerCount(); i++) {
282 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
283 continue;
284 }
285 uint32_t id = args.pointerProperties[i].id;
286 idToIndex[id] = i;
287 idBits.markBit(id);
288 }
289 }
290 // The PointerController already handles setting spots per-display, so
291 // we do not need to manually manage display changes for touch spots for now.
292 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000293}
294
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900295void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
296 if (args.displayId == ADISPLAY_ID_NONE) {
297 return;
298 }
299
300 if (args.getPointerCount() != 1) {
301 LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
302 << args.dump();
303 }
304
305 // Get the stylus pointer controller for the device, or create one if it doesn't exist.
306 auto [it, _] =
307 mStylusPointersByDevice.try_emplace(args.deviceId,
308 getStylusControllerConstructor(args.displayId));
309
310 PointerControllerInterface& pc = *it->second;
311
312 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
313 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
314 pc.setPosition(x, y);
315 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000316 // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
317 // immediately by a DOWN event.
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900318 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
Prabir Pradhan4b36db92024-01-03 20:56:57 +0000319 pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000320 } else if (canUnfadeOnDisplay(args.displayId)) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900321 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
322 }
323}
324
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000325void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
326 mNextListener.notify(args);
327}
328
329void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
330 mNextListener.notify(args);
331}
332
333void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
334 mNextListener.notify(args);
335}
336
337void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900338 processDeviceReset(args);
339
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000340 mNextListener.notify(args);
341}
342
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900343void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
344 std::scoped_lock _l(mLock);
Prabir Pradhan16788792023-11-08 21:07:21 +0000345 mTouchPointersByDevice.erase(args.deviceId);
346 mStylusPointersByDevice.erase(args.deviceId);
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000347 mDrawingTabletPointersByDevice.erase(args.deviceId);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900348}
349
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000350void PointerChoreographer::notifyPointerCaptureChanged(
351 const NotifyPointerCaptureChangedArgs& args) {
Hiroki Sato25040232024-02-22 17:21:22 +0900352 if (args.request.isEnable()) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900353 std::scoped_lock _l(mLock);
354 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
355 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
356 }
357 }
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000358 mNextListener.notify(args);
359}
360
361void PointerChoreographer::dump(std::string& dump) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900362 std::scoped_lock _l(mLock);
363
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000364 dump += "PointerChoreographer:\n";
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900365 dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900366 dump += StringPrintf("stylus pointer icon enabled: %s\n",
367 mStylusPointerIconEnabled ? "true" : "false");
Byoungho Jungda10dd32023-10-06 17:03:45 +0900368
369 dump += INDENT "MousePointerControllers:\n";
370 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
371 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
372 dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
373 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900374 dump += INDENT "TouchPointerControllers:\n";
375 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
376 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
377 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
378 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900379 dump += INDENT "StylusPointerControllers:\n";
380 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
381 std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
382 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
383 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000384 dump += INDENT "DrawingTabletControllers:\n";
385 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
386 std::string pointerControllerDump = addLinePrefix(drawingTabletController->dump(), INDENT);
387 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
388 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900389 dump += "\n";
390}
391
392const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
393 for (auto& viewport : mViewports) {
394 if (viewport.displayId == displayId) {
395 return &viewport;
396 }
397 }
398 return nullptr;
399}
400
401int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
402 return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
403}
404
Prabir Pradhan990d8712024-03-05 00:31:36 +0000405std::pair<int32_t, PointerControllerInterface&> PointerChoreographer::ensureMouseControllerLocked(
406 int32_t associatedDisplayId) {
Byoungho Jungee6268f2023-10-30 17:27:26 +0900407 const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
408
Prabir Pradhan990d8712024-03-05 00:31:36 +0000409 auto it = mMousePointersByDisplay.find(displayId);
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000410 if (it == mMousePointersByDisplay.end()) {
411 it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
412 .first;
413 }
Byoungho Jungee6268f2023-10-30 17:27:26 +0900414
415 return {displayId, *it->second};
416}
417
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900418InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000419 auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
420 [deviceId](const auto& info) { return info.getId() == deviceId; });
421 return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900422}
423
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000424bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) {
425 return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
426}
427
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000428PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900429 std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900430 std::set<DeviceId> touchDevicesToKeep;
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900431 std::set<DeviceId> stylusDevicesToKeep;
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000432 std::set<DeviceId> drawingTabletDevicesToKeep;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900433
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000434 // Mark the displayIds or deviceIds of PointerControllers currently needed, and create
435 // new PointerControllers if necessary.
Byoungho Jungda10dd32023-10-06 17:03:45 +0900436 for (const auto& info : mInputDeviceInfos) {
Linnan Li48f80da2024-04-22 18:38:16 +0000437 if (!info.isEnabled()) {
438 // If device is disabled, we should not keep it, and should not show pointer for
439 // disabled mouse device.
440 continue;
441 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900442 const uint32_t sources = info.getSources();
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000443 const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
444
445 if (isMouseOrTouchpad(sources) || isKnownMouse) {
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000446 const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
447 mouseDisplaysToKeep.insert(displayId);
448 // For mice, show the cursor immediately when the device is first connected or
449 // when it moves to a new display.
450 auto [mousePointerIt, isNewMousePointer] =
451 mMousePointersByDisplay.try_emplace(displayId,
452 getMouseControllerConstructor(displayId));
Prabir Pradhan5a31d3c2024-03-29 20:23:22 +0000453 mMouseDevices.emplace(info.getId());
454 if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000455 mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
456 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900457 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900458 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
459 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
460 touchDevicesToKeep.insert(info.getId());
461 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900462 if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
463 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
464 stylusDevicesToKeep.insert(info.getId());
465 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000466 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
467 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
468 drawingTabletDevicesToKeep.insert(info.getId());
469 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900470 }
471
472 // Remove PointerControllers no longer needed.
Prabir Pradhan19767602023-11-03 16:53:31 +0000473 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000474 return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900475 });
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900476 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000477 return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900478 });
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900479 std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000480 return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900481 });
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000482 std::erase_if(mDrawingTabletPointersByDevice, [&drawingTabletDevicesToKeep](const auto& pair) {
483 return drawingTabletDevicesToKeep.find(pair.first) == drawingTabletDevicesToKeep.end();
484 });
Prabir Pradhan6506f6f2023-12-11 20:48:39 +0000485 std::erase_if(mMouseDevices, [&](DeviceId id) REQUIRES(mLock) {
486 return std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
487 [id](const auto& info) { return info.getId() == id; }) ==
488 mInputDeviceInfos.end();
489 });
Byoungho Jungda10dd32023-10-06 17:03:45 +0900490
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000491 // Check if we need to notify the policy if there's a change on the pointer display ID.
492 return calculatePointerDisplayChangeToNotify();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900493}
494
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000495PointerChoreographer::PointerDisplayChange
496PointerChoreographer::calculatePointerDisplayChangeToNotify() {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900497 int32_t displayIdToNotify = ADISPLAY_ID_NONE;
498 FloatPoint cursorPosition = {0, 0};
499 if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
500 it != mMousePointersByDisplay.end()) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000501 const auto& pointerController = it->second;
502 // Use the displayId from the pointerController, because it accurately reflects whether
503 // the viewport has been added for that display. Otherwise, we would have to check if
504 // the viewport exists separately.
505 displayIdToNotify = pointerController->getDisplayId();
506 cursorPosition = pointerController->getPosition();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900507 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900508 if (mNotifiedPointerDisplayId == displayIdToNotify) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000509 return {};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900510 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900511 mNotifiedPointerDisplayId = displayIdToNotify;
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000512 return {{displayIdToNotify, cursorPosition}};
Byoungho Jungda10dd32023-10-06 17:03:45 +0900513}
514
515void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000516 PointerDisplayChange pointerDisplayChange;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900517
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000518 { // acquire lock
519 std::scoped_lock _l(mLock);
520
521 mDefaultMouseDisplayId = displayId;
522 pointerDisplayChange = updatePointerControllersLocked();
523 } // release lock
524
525 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900526}
527
528void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000529 PointerDisplayChange pointerDisplayChange;
530
531 { // acquire lock
532 std::scoped_lock _l(mLock);
533 for (const auto& viewport : viewports) {
534 const int32_t displayId = viewport.displayId;
535 if (const auto it = mMousePointersByDisplay.find(displayId);
536 it != mMousePointersByDisplay.end()) {
537 it->second->setDisplayViewport(viewport);
538 }
539 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
540 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
541 if (info && info->getAssociatedDisplayId() == displayId) {
542 stylusPointerController->setDisplayViewport(viewport);
543 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900544 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000545 for (const auto& [deviceId, drawingTabletController] : mDrawingTabletPointersByDevice) {
546 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
547 if (info && info->getAssociatedDisplayId() == displayId) {
548 drawingTabletController->setDisplayViewport(viewport);
549 }
550 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900551 }
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000552 mViewports = viewports;
553 pointerDisplayChange = calculatePointerDisplayChangeToNotify();
554 } // release lock
555
556 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900557}
558
559std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
560 int32_t associatedDisplayId) {
561 std::scoped_lock _l(mLock);
562 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
563 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
564 return *viewport;
565 }
566 return std::nullopt;
567}
568
569FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
570 std::scoped_lock _l(mLock);
571 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
572 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
573 it != mMousePointersByDisplay.end()) {
574 return it->second->getPosition();
575 }
576 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000577}
578
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900579void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000580 PointerDisplayChange pointerDisplayChange;
581
582 { // acquire lock
583 std::scoped_lock _l(mLock);
584 if (mShowTouchesEnabled == enabled) {
585 return;
586 }
587 mShowTouchesEnabled = enabled;
588 pointerDisplayChange = updatePointerControllersLocked();
589 } // release lock
590
591 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900592}
593
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900594void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
Prabir Pradhan5a51a222024-03-05 03:54:00 +0000595 PointerDisplayChange pointerDisplayChange;
596
597 { // acquire lock
598 std::scoped_lock _l(mLock);
599 if (mStylusPointerIconEnabled == enabled) {
600 return;
601 }
602 mStylusPointerIconEnabled = enabled;
603 pointerDisplayChange = updatePointerControllersLocked();
604 } // release lock
605
606 notifyPointerDisplayChange(pointerDisplayChange, mPolicy);
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900607}
608
Byoungho Jung99326452023-11-03 20:19:17 +0900609bool PointerChoreographer::setPointerIcon(
610 std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId,
611 DeviceId deviceId) {
612 std::scoped_lock _l(mLock);
613 if (deviceId < 0) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000614 LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900615 return false;
616 }
617 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
618 if (!info) {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000619 LOG(WARNING) << "No input device info found for id " << deviceId
620 << ". Cannot set pointer icon.";
Byoungho Jung99326452023-11-03 20:19:17 +0900621 return false;
622 }
623 const uint32_t sources = info->getSources();
Byoungho Jung99326452023-11-03 20:19:17 +0900624
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000625 if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE)) {
626 auto it = mDrawingTabletPointersByDevice.find(deviceId);
627 if (it != mDrawingTabletPointersByDevice.end()) {
628 setIconForController(icon, *it->second);
629 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900630 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000631 }
632 if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
633 auto it = mStylusPointersByDevice.find(deviceId);
634 if (it != mStylusPointersByDevice.end()) {
635 setIconForController(icon, *it->second);
636 return true;
637 }
638 }
639 if (isFromSource(sources, AINPUT_SOURCE_MOUSE)) {
640 auto it = mMousePointersByDisplay.find(displayId);
641 if (it != mMousePointersByDisplay.end()) {
642 setIconForController(icon, *it->second);
643 return true;
Byoungho Jung99326452023-11-03 20:19:17 +0900644 } else {
Prabir Pradhan521f4fc2023-12-04 19:09:59 +0000645 LOG(WARNING) << "No mouse pointer controller found for display " << displayId
646 << ", device " << deviceId << ".";
Byoungho Jung99326452023-11-03 20:19:17 +0900647 return false;
648 }
Byoungho Jung99326452023-11-03 20:19:17 +0900649 }
Prabir Pradhan4c977a42024-03-15 16:47:37 +0000650 LOG(WARNING) << "Cannot set pointer icon for display " << displayId << ", device " << deviceId
651 << ".";
652 return false;
Byoungho Jung99326452023-11-03 20:19:17 +0900653}
654
Prabir Pradhan502ddbd2024-01-19 02:22:38 +0000655void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
656 std::scoped_lock lock(mLock);
657 if (visible) {
658 mDisplaysWithPointersHidden.erase(displayId);
659 // We do not unfade the icons here, because we don't know when the last event happened.
660 return;
661 }
662
663 mDisplaysWithPointersHidden.emplace(displayId);
664
665 // Hide any icons that are currently visible on the display.
666 if (auto it = mMousePointersByDisplay.find(displayId); it != mMousePointersByDisplay.end()) {
667 const auto& [_, controller] = *it;
668 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
669 }
670 for (const auto& [_, controller] : mStylusPointersByDevice) {
671 if (controller->getDisplayId() == displayId) {
672 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
673 }
674 }
675}
676
Prabir Pradhan19767602023-11-03 16:53:31 +0000677PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
678 int32_t displayId) {
679 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
680 [this, displayId]() REQUIRES(mLock) {
681 auto pc = mPolicy.createPointerController(
682 PointerControllerInterface::ControllerType::MOUSE);
683 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
684 pc->setDisplayViewport(*viewport);
685 }
686 return pc;
687 };
688 return ConstructorDelegate(std::move(ctor));
689}
690
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900691PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
692 int32_t displayId) {
693 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
694 [this, displayId]() REQUIRES(mLock) {
695 auto pc = mPolicy.createPointerController(
696 PointerControllerInterface::ControllerType::STYLUS);
697 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
698 pc->setDisplayViewport(*viewport);
699 }
700 return pc;
701 };
702 return ConstructorDelegate(std::move(ctor));
703}
704
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000705} // namespace android