blob: 13fbf1d974b8d60bc9c3f23947b7877bc9896f92 [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 {
29bool isFromMouse(const NotifyMotionArgs& args) {
30 return isFromSource(args.source, AINPUT_SOURCE_MOUSE) &&
31 args.pointerProperties[0].toolType == ToolType::MOUSE;
32}
33
34} // namespace
35
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000036// --- PointerChoreographer ---
37
38PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
39 PointerChoreographerPolicyInterface& policy)
Byoungho Jungda10dd32023-10-06 17:03:45 +090040 : mNextListener(listener),
41 mPolicy(policy),
42 mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
Byoungho Jung6f5b16b2023-10-27 18:22:07 +090043 mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
44 mShowTouchesEnabled(false) {}
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000045
46void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +090047 std::scoped_lock _l(mLock);
48
49 mInputDeviceInfos = args.inputDeviceInfos;
50 updatePointerControllersLocked();
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000051 mNextListener.notify(args);
52}
53
54void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
55 mNextListener.notify(args);
56}
57
58void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
59 mNextListener.notify(args);
60}
61
62void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +090063 NotifyMotionArgs newArgs = processMotion(args);
64
65 mNextListener.notify(newArgs);
66}
67
68NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
69 std::scoped_lock _l(mLock);
70
71 if (isFromMouse(args)) {
72 return processMouseEventLocked(args);
73 } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +090074 processTouchscreenAndStylusEventLocked(args);
Byoungho Jungda10dd32023-10-06 17:03:45 +090075 }
76 return args;
77}
78
79NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
80 if (args.getPointerCount() != 1) {
Prabir Pradhan19767602023-11-03 16:53:31 +000081 LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
82 << args.dump();
Byoungho Jungda10dd32023-10-06 17:03:45 +090083 }
84
85 const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
Prabir Pradhan19767602023-11-03 16:53:31 +000086
87 // Get the mouse pointer controller for the display, or create one if it doesn't exist.
88 auto [it, emplaced] =
89 mMousePointersByDisplay.try_emplace(displayId,
90 getMouseControllerConstructor(displayId));
91 if (emplaced) {
Byoungho Jungda10dd32023-10-06 17:03:45 +090092 notifyPointerDisplayIdChangedLocked();
93 }
94
95 PointerControllerInterface& pc = *it->second;
96
97 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
98 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
99 pc.move(deltaX, deltaY);
100 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
101
102 const auto [x, y] = pc.getPosition();
103 NotifyMotionArgs newArgs(args);
104 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
105 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
106 newArgs.xCursorPosition = x;
107 newArgs.yCursorPosition = y;
108 newArgs.displayId = displayId;
109 return newArgs;
110}
111
112/**
113 * When screen is touched, fade the mouse pointer on that display. We only call fade for
114 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
115 * mouse device keeps moving and unfades the cursor.
116 * For touch events, we do not need to populate the cursor position.
117 */
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900118void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
119 if (args.displayId == ADISPLAY_ID_NONE) {
120 return;
121 }
122
Byoungho Jungda10dd32023-10-06 17:03:45 +0900123 if (const auto it = mMousePointersByDisplay.find(args.displayId);
124 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
125 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
126 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900127
128 if (!mShowTouchesEnabled) {
129 return;
130 }
131
132 // Get the touch pointer controller for the device, or create one if it doesn't exist.
133 auto [it, _] =
134 mTouchPointersByDevice.try_emplace(args.deviceId, getTouchControllerConstructor());
135
136 PointerControllerInterface& pc = *it->second;
137
138 const PointerCoords* coords = args.pointerCoords.data();
139 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
140 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
141 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
142 BitSet32 idBits;
143 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
144 for (size_t i = 0; i < args.getPointerCount(); i++) {
145 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
146 continue;
147 }
148 uint32_t id = args.pointerProperties[i].id;
149 idToIndex[id] = i;
150 idBits.markBit(id);
151 }
152 }
153 // The PointerController already handles setting spots per-display, so
154 // we do not need to manually manage display changes for touch spots for now.
155 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000156}
157
158void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
159 mNextListener.notify(args);
160}
161
162void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
163 mNextListener.notify(args);
164}
165
166void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
167 mNextListener.notify(args);
168}
169
170void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900171 processDeviceReset(args);
172
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000173 mNextListener.notify(args);
174}
175
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900176void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
177 std::scoped_lock _l(mLock);
178
179 const InputDeviceInfo* info = findInputDeviceLocked(args.deviceId);
180 if (info == nullptr) {
181 return;
182 }
183
184 if (isFromSource(info->getSources(), AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
185 info->getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
186 if (const auto it = mTouchPointersByDevice.find(args.deviceId);
187 it != mTouchPointersByDevice.end()) {
188 it->second->clearSpots();
189 }
190 }
191}
192
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000193void PointerChoreographer::notifyPointerCaptureChanged(
194 const NotifyPointerCaptureChangedArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900195 if (args.request.enable) {
196 std::scoped_lock _l(mLock);
197 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
198 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
199 }
200 }
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000201 mNextListener.notify(args);
202}
203
204void PointerChoreographer::dump(std::string& dump) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900205 std::scoped_lock _l(mLock);
206
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000207 dump += "PointerChoreographer:\n";
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900208 dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
Byoungho Jungda10dd32023-10-06 17:03:45 +0900209
210 dump += INDENT "MousePointerControllers:\n";
211 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
212 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
213 dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
214 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900215 dump += INDENT "TouchPointerControllers:\n";
216 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
217 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
218 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
219 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900220 dump += "\n";
221}
222
223const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
224 for (auto& viewport : mViewports) {
225 if (viewport.displayId == displayId) {
226 return &viewport;
227 }
228 }
229 return nullptr;
230}
231
232int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
233 return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
234}
235
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900236InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
237 for (auto& info : mInputDeviceInfos) {
238 if (info.getId() == deviceId) {
239 return &info;
240 }
241 }
242 return nullptr;
243}
244
Byoungho Jungda10dd32023-10-06 17:03:45 +0900245void PointerChoreographer::updatePointerControllersLocked() {
246 std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900247 std::set<DeviceId> touchDevicesToKeep;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900248
249 // Mark the displayIds or deviceIds of PointerControllers currently needed.
250 for (const auto& info : mInputDeviceInfos) {
251 const uint32_t sources = info.getSources();
252 if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
253 isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
254 const int32_t resolvedDisplayId =
255 getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
256 mouseDisplaysToKeep.insert(resolvedDisplayId);
257 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900258 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
259 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
260 touchDevicesToKeep.insert(info.getId());
261 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900262 }
263
264 // Remove PointerControllers no longer needed.
265 // This has the side-effect of fading pointers or clearing spots before removal.
Prabir Pradhan19767602023-11-03 16:53:31 +0000266 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
267 auto& [displayId, controller] = pair;
268 if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) {
269 controller->fade(PointerControllerInterface::Transition::IMMEDIATE);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900270 return true;
271 }
272 return false;
273 });
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900274 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
275 auto& [deviceId, controller] = pair;
276 if (touchDevicesToKeep.find(deviceId) == touchDevicesToKeep.end()) {
277 controller->clearSpots();
278 return true;
279 }
280 return false;
281 });
Byoungho Jungda10dd32023-10-06 17:03:45 +0900282
283 // Notify the policy if there's a change on the pointer display ID.
284 notifyPointerDisplayIdChangedLocked();
285}
286
287void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
288 int32_t displayIdToNotify = ADISPLAY_ID_NONE;
289 FloatPoint cursorPosition = {0, 0};
290 if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
291 it != mMousePointersByDisplay.end()) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000292 const auto& pointerController = it->second;
293 // Use the displayId from the pointerController, because it accurately reflects whether
294 // the viewport has been added for that display. Otherwise, we would have to check if
295 // the viewport exists separately.
296 displayIdToNotify = pointerController->getDisplayId();
297 cursorPosition = pointerController->getPosition();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900298 }
299
300 if (mNotifiedPointerDisplayId == displayIdToNotify) {
301 return;
302 }
303 mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
304 mNotifiedPointerDisplayId = displayIdToNotify;
305}
306
307void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
308 std::scoped_lock _l(mLock);
309
310 mDefaultMouseDisplayId = displayId;
311 updatePointerControllersLocked();
312}
313
314void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
315 std::scoped_lock _l(mLock);
316 for (const auto& viewport : viewports) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000317 if (const auto it = mMousePointersByDisplay.find(viewport.displayId);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900318 it != mMousePointersByDisplay.end()) {
319 it->second->setDisplayViewport(viewport);
320 }
321 }
322 mViewports = viewports;
323 notifyPointerDisplayIdChangedLocked();
324}
325
326std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
327 int32_t associatedDisplayId) {
328 std::scoped_lock _l(mLock);
329 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
330 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
331 return *viewport;
332 }
333 return std::nullopt;
334}
335
336FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
337 std::scoped_lock _l(mLock);
338 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
339 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
340 it != mMousePointersByDisplay.end()) {
341 return it->second->getPosition();
342 }
343 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000344}
345
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900346void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
347 std::scoped_lock _l(mLock);
348 if (mShowTouchesEnabled == enabled) {
349 return;
350 }
351 mShowTouchesEnabled = enabled;
352 updatePointerControllersLocked();
353}
354
Prabir Pradhan19767602023-11-03 16:53:31 +0000355PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
356 int32_t displayId) {
357 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
358 [this, displayId]() REQUIRES(mLock) {
359 auto pc = mPolicy.createPointerController(
360 PointerControllerInterface::ControllerType::MOUSE);
361 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
362 pc->setDisplayViewport(*viewport);
363 }
364 return pc;
365 };
366 return ConstructorDelegate(std::move(ctor));
367}
368
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900369PointerChoreographer::ControllerConstructor PointerChoreographer::getTouchControllerConstructor() {
370 std::function<std::shared_ptr<PointerControllerInterface>()> ctor = [this]() REQUIRES(mLock) {
371 return mPolicy.createPointerController(PointerControllerInterface::ControllerType::TOUCH);
372 };
373 return ConstructorDelegate(std::move(ctor));
374}
375
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000376} // namespace android