blob: e529bddea54ad9168d4f72168170d69ca3c91af3 [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
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090034bool isHoverAction(int32_t action) {
35 return action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
36 action == AMOTION_EVENT_ACTION_HOVER_MOVE || action == AMOTION_EVENT_ACTION_HOVER_EXIT;
37}
38
39bool isStylusHoverEvent(const NotifyMotionArgs& args) {
40 return isStylusEvent(args.source, args.pointerProperties) && isHoverAction(args.action);
41}
Byoungho Jungda10dd32023-10-06 17:03:45 +090042} // namespace
43
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000044// --- PointerChoreographer ---
45
46PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
47 PointerChoreographerPolicyInterface& policy)
Prabir Pradhan16788792023-11-08 21:07:21 +000048 : mTouchControllerConstructor([this]() REQUIRES(mLock) {
49 return mPolicy.createPointerController(
50 PointerControllerInterface::ControllerType::TOUCH);
51 }),
52 mNextListener(listener),
Byoungho Jungda10dd32023-10-06 17:03:45 +090053 mPolicy(policy),
54 mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
Byoungho Jung6f5b16b2023-10-27 18:22:07 +090055 mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090056 mShowTouchesEnabled(false),
57 mStylusPointerIconEnabled(false) {}
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000058
59void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +090060 std::scoped_lock _l(mLock);
61
62 mInputDeviceInfos = args.inputDeviceInfos;
63 updatePointerControllersLocked();
Prabir Pradhanb56e92c2023-06-09 23:40:37 +000064 mNextListener.notify(args);
65}
66
67void PointerChoreographer::notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) {
68 mNextListener.notify(args);
69}
70
71void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
72 mNextListener.notify(args);
73}
74
75void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +090076 NotifyMotionArgs newArgs = processMotion(args);
77
78 mNextListener.notify(newArgs);
79}
80
81NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
82 std::scoped_lock _l(mLock);
83
84 if (isFromMouse(args)) {
85 return processMouseEventLocked(args);
Byoungho Jungd6fe27b2023-10-27 20:49:38 +090086 } else if (mStylusPointerIconEnabled && isStylusHoverEvent(args)) {
87 processStylusHoverEventLocked(args);
Byoungho Jungda10dd32023-10-06 17:03:45 +090088 } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +090089 processTouchscreenAndStylusEventLocked(args);
Byoungho Jungda10dd32023-10-06 17:03:45 +090090 }
91 return args;
92}
93
94NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) {
95 if (args.getPointerCount() != 1) {
Prabir Pradhan19767602023-11-03 16:53:31 +000096 LOG(FATAL) << "Only mouse events with a single pointer are currently supported: "
97 << args.dump();
Byoungho Jungda10dd32023-10-06 17:03:45 +090098 }
99
100 const int32_t displayId = getTargetMouseDisplayLocked(args.displayId);
Prabir Pradhan19767602023-11-03 16:53:31 +0000101
102 // Get the mouse pointer controller for the display, or create one if it doesn't exist.
103 auto [it, emplaced] =
104 mMousePointersByDisplay.try_emplace(displayId,
105 getMouseControllerConstructor(displayId));
106 if (emplaced) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900107 notifyPointerDisplayIdChangedLocked();
108 }
109
110 PointerControllerInterface& pc = *it->second;
111
112 const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
113 const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
114 pc.move(deltaX, deltaY);
115 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
116
117 const auto [x, y] = pc.getPosition();
118 NotifyMotionArgs newArgs(args);
119 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
120 newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
121 newArgs.xCursorPosition = x;
122 newArgs.yCursorPosition = y;
123 newArgs.displayId = displayId;
124 return newArgs;
125}
126
127/**
128 * When screen is touched, fade the mouse pointer on that display. We only call fade for
129 * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the
130 * mouse device keeps moving and unfades the cursor.
131 * For touch events, we do not need to populate the cursor position.
132 */
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900133void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
134 if (args.displayId == ADISPLAY_ID_NONE) {
135 return;
136 }
137
Byoungho Jungda10dd32023-10-06 17:03:45 +0900138 if (const auto it = mMousePointersByDisplay.find(args.displayId);
139 it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) {
140 it->second->fade(PointerControllerInterface::Transition::GRADUAL);
141 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900142
143 if (!mShowTouchesEnabled) {
144 return;
145 }
146
147 // Get the touch pointer controller for the device, or create one if it doesn't exist.
Prabir Pradhan16788792023-11-08 21:07:21 +0000148 auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900149
150 PointerControllerInterface& pc = *it->second;
151
152 const PointerCoords* coords = args.pointerCoords.data();
153 const int32_t maskedAction = MotionEvent::getActionMasked(args.action);
154 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
155 std::array<uint32_t, MAX_POINTER_ID + 1> idToIndex;
156 BitSet32 idBits;
157 if (maskedAction != AMOTION_EVENT_ACTION_UP && maskedAction != AMOTION_EVENT_ACTION_CANCEL) {
158 for (size_t i = 0; i < args.getPointerCount(); i++) {
159 if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP && actionIndex == i) {
160 continue;
161 }
162 uint32_t id = args.pointerProperties[i].id;
163 idToIndex[id] = i;
164 idBits.markBit(id);
165 }
166 }
167 // The PointerController already handles setting spots per-display, so
168 // we do not need to manually manage display changes for touch spots for now.
169 pc.setSpots(coords, idToIndex.cbegin(), idBits, args.displayId);
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000170}
171
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900172void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
173 if (args.displayId == ADISPLAY_ID_NONE) {
174 return;
175 }
176
177 if (args.getPointerCount() != 1) {
178 LOG(WARNING) << "Only stylus hover events with a single pointer are currently supported: "
179 << args.dump();
180 }
181
182 // Get the stylus pointer controller for the device, or create one if it doesn't exist.
183 auto [it, _] =
184 mStylusPointersByDevice.try_emplace(args.deviceId,
185 getStylusControllerConstructor(args.displayId));
186
187 PointerControllerInterface& pc = *it->second;
188
189 const float x = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
190 const float y = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
191 pc.setPosition(x, y);
192 if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) {
193 pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
194 } else {
195 pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
196 }
197}
198
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000199void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) {
200 mNextListener.notify(args);
201}
202
203void PointerChoreographer::notifySensor(const NotifySensorArgs& args) {
204 mNextListener.notify(args);
205}
206
207void PointerChoreographer::notifyVibratorState(const NotifyVibratorStateArgs& args) {
208 mNextListener.notify(args);
209}
210
211void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900212 processDeviceReset(args);
213
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000214 mNextListener.notify(args);
215}
216
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900217void PointerChoreographer::processDeviceReset(const NotifyDeviceResetArgs& args) {
218 std::scoped_lock _l(mLock);
Prabir Pradhan16788792023-11-08 21:07:21 +0000219 mTouchPointersByDevice.erase(args.deviceId);
220 mStylusPointersByDevice.erase(args.deviceId);
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900221}
222
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000223void PointerChoreographer::notifyPointerCaptureChanged(
224 const NotifyPointerCaptureChangedArgs& args) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900225 if (args.request.enable) {
226 std::scoped_lock _l(mLock);
227 for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
228 mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
229 }
230 }
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000231 mNextListener.notify(args);
232}
233
234void PointerChoreographer::dump(std::string& dump) {
Byoungho Jungda10dd32023-10-06 17:03:45 +0900235 std::scoped_lock _l(mLock);
236
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000237 dump += "PointerChoreographer:\n";
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900238 dump += StringPrintf("show touches: %s\n", mShowTouchesEnabled ? "true" : "false");
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900239 dump += StringPrintf("stylus pointer icon enabled: %s\n",
240 mStylusPointerIconEnabled ? "true" : "false");
Byoungho Jungda10dd32023-10-06 17:03:45 +0900241
242 dump += INDENT "MousePointerControllers:\n";
243 for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
244 std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
245 dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
246 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900247 dump += INDENT "TouchPointerControllers:\n";
248 for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
249 std::string pointerControllerDump = addLinePrefix(touchPointerController->dump(), INDENT);
250 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
251 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900252 dump += INDENT "StylusPointerControllers:\n";
253 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
254 std::string pointerControllerDump = addLinePrefix(stylusPointerController->dump(), INDENT);
255 dump += INDENT + std::to_string(deviceId) + " : " + pointerControllerDump;
256 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900257 dump += "\n";
258}
259
260const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
261 for (auto& viewport : mViewports) {
262 if (viewport.displayId == displayId) {
263 return &viewport;
264 }
265 }
266 return nullptr;
267}
268
269int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
270 return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
271}
272
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900273InputDeviceInfo* PointerChoreographer::findInputDeviceLocked(DeviceId deviceId) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000274 auto it = std::find_if(mInputDeviceInfos.begin(), mInputDeviceInfos.end(),
275 [deviceId](const auto& info) { return info.getId() == deviceId; });
276 return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900277}
278
Byoungho Jungda10dd32023-10-06 17:03:45 +0900279void PointerChoreographer::updatePointerControllersLocked() {
280 std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900281 std::set<DeviceId> touchDevicesToKeep;
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900282 std::set<DeviceId> stylusDevicesToKeep;
Byoungho Jungda10dd32023-10-06 17:03:45 +0900283
284 // Mark the displayIds or deviceIds of PointerControllers currently needed.
285 for (const auto& info : mInputDeviceInfos) {
286 const uint32_t sources = info.getSources();
287 if (isFromSource(sources, AINPUT_SOURCE_MOUSE) ||
288 isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) {
289 const int32_t resolvedDisplayId =
290 getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
291 mouseDisplaysToKeep.insert(resolvedDisplayId);
292 }
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900293 if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
294 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
295 touchDevicesToKeep.insert(info.getId());
296 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900297 if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
298 info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
299 stylusDevicesToKeep.insert(info.getId());
300 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900301 }
302
303 // Remove PointerControllers no longer needed.
Prabir Pradhan19767602023-11-03 16:53:31 +0000304 std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000305 return mouseDisplaysToKeep.find(pair.first) == mouseDisplaysToKeep.end();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900306 });
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900307 std::erase_if(mTouchPointersByDevice, [&touchDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000308 return touchDevicesToKeep.find(pair.first) == touchDevicesToKeep.end();
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900309 });
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900310 std::erase_if(mStylusPointersByDevice, [&stylusDevicesToKeep](const auto& pair) {
Prabir Pradhan16788792023-11-08 21:07:21 +0000311 return stylusDevicesToKeep.find(pair.first) == stylusDevicesToKeep.end();
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900312 });
Byoungho Jungda10dd32023-10-06 17:03:45 +0900313
314 // Notify the policy if there's a change on the pointer display ID.
315 notifyPointerDisplayIdChangedLocked();
316}
317
318void PointerChoreographer::notifyPointerDisplayIdChangedLocked() {
319 int32_t displayIdToNotify = ADISPLAY_ID_NONE;
320 FloatPoint cursorPosition = {0, 0};
321 if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
322 it != mMousePointersByDisplay.end()) {
Prabir Pradhan19767602023-11-03 16:53:31 +0000323 const auto& pointerController = it->second;
324 // Use the displayId from the pointerController, because it accurately reflects whether
325 // the viewport has been added for that display. Otherwise, we would have to check if
326 // the viewport exists separately.
327 displayIdToNotify = pointerController->getDisplayId();
328 cursorPosition = pointerController->getPosition();
Byoungho Jungda10dd32023-10-06 17:03:45 +0900329 }
330
331 if (mNotifiedPointerDisplayId == displayIdToNotify) {
332 return;
333 }
334 mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition);
335 mNotifiedPointerDisplayId = displayIdToNotify;
336}
337
338void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
339 std::scoped_lock _l(mLock);
340
341 mDefaultMouseDisplayId = displayId;
342 updatePointerControllersLocked();
343}
344
345void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
346 std::scoped_lock _l(mLock);
347 for (const auto& viewport : viewports) {
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900348 const int32_t displayId = viewport.displayId;
349 if (const auto it = mMousePointersByDisplay.find(displayId);
Byoungho Jungda10dd32023-10-06 17:03:45 +0900350 it != mMousePointersByDisplay.end()) {
351 it->second->setDisplayViewport(viewport);
352 }
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900353 for (const auto& [deviceId, stylusPointerController] : mStylusPointersByDevice) {
354 const InputDeviceInfo* info = findInputDeviceLocked(deviceId);
355 if (info && info->getAssociatedDisplayId() == displayId) {
356 stylusPointerController->setDisplayViewport(viewport);
357 }
358 }
Byoungho Jungda10dd32023-10-06 17:03:45 +0900359 }
360 mViewports = viewports;
361 notifyPointerDisplayIdChangedLocked();
362}
363
364std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
365 int32_t associatedDisplayId) {
366 std::scoped_lock _l(mLock);
367 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
368 if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
369 return *viewport;
370 }
371 return std::nullopt;
372}
373
374FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
375 std::scoped_lock _l(mLock);
376 const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
377 if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
378 it != mMousePointersByDisplay.end()) {
379 return it->second->getPosition();
380 }
381 return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000382}
383
Byoungho Jung6f5b16b2023-10-27 18:22:07 +0900384void PointerChoreographer::setShowTouchesEnabled(bool enabled) {
385 std::scoped_lock _l(mLock);
386 if (mShowTouchesEnabled == enabled) {
387 return;
388 }
389 mShowTouchesEnabled = enabled;
390 updatePointerControllersLocked();
391}
392
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900393void PointerChoreographer::setStylusPointerIconEnabled(bool enabled) {
394 std::scoped_lock _l(mLock);
395 if (mStylusPointerIconEnabled == enabled) {
396 return;
397 }
398 mStylusPointerIconEnabled = enabled;
399 updatePointerControllersLocked();
400}
401
Prabir Pradhan19767602023-11-03 16:53:31 +0000402PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
403 int32_t displayId) {
404 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
405 [this, displayId]() REQUIRES(mLock) {
406 auto pc = mPolicy.createPointerController(
407 PointerControllerInterface::ControllerType::MOUSE);
408 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
409 pc->setDisplayViewport(*viewport);
410 }
411 return pc;
412 };
413 return ConstructorDelegate(std::move(ctor));
414}
415
Byoungho Jungd6fe27b2023-10-27 20:49:38 +0900416PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
417 int32_t displayId) {
418 std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
419 [this, displayId]() REQUIRES(mLock) {
420 auto pc = mPolicy.createPointerController(
421 PointerControllerInterface::ControllerType::STYLUS);
422 if (const auto viewport = findViewportByIdLocked(displayId); viewport) {
423 pc->setDisplayViewport(*viewport);
424 }
425 return pc;
426 };
427 return ConstructorDelegate(std::move(ctor));
428}
429
Prabir Pradhanb56e92c2023-06-09 23:40:37 +0000430} // namespace android