1 /*
2 * Copyright (C) 2020 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 "MouseCursorController"
18 //#define LOG_NDEBUG 0
19
20 // Log debug messages about pointer updates
21 #define DEBUG_MOUSE_CURSOR_UPDATES 0
22
23 #include "MouseCursorController.h"
24
25 #include <input/Input.h>
26 #include <log/log.h>
27
28 #define INDENT " "
29 #define INDENT2 " "
30
31 namespace android {
32
33 namespace {
34
35 // Time to spend fading out the pointer completely.
36 const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
37
38 } // namespace
39
40 // --- MouseCursorController ---
41
MouseCursorController(PointerControllerContext & context)42 MouseCursorController::MouseCursorController(PointerControllerContext& context)
43 : mContext(context) {
44 std::scoped_lock lock(mLock);
45
46 mLocked.stylusHoverMode = false;
47
48 mLocked.animationFrameIndex = 0;
49 mLocked.lastFrameUpdatedTime = 0;
50
51 mLocked.pointerFadeDirection = 0;
52 mLocked.pointerPosition = {0, 0};
53 mLocked.pointerAlpha = 0.0f; // pointer is initially faded
54 mLocked.pointerSprite = mContext.getSpriteController().createSprite();
55 mLocked.updatePointerIcon = false;
56 mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
57 mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
58
59 mLocked.resourcesLoaded = false;
60 }
61
~MouseCursorController()62 MouseCursorController::~MouseCursorController() {
63 std::scoped_lock lock(mLock);
64
65 mLocked.pointerSprite.clear();
66 }
67
move(vec2 delta)68 vec2 MouseCursorController::move(vec2 delta) {
69 #if DEBUG_MOUSE_CURSOR_UPDATES
70 ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
71 #endif
72 if (delta.x == 0.0f && delta.y == 0.0f) {
73 return {0, 0};
74 }
75
76 // When transition occurs, the MouseCursorController object may or may not be deleted, depending
77 // if there's another display on the other side of the transition. At this point we still need
78 // to move the cursor to the boundary.
79 std::scoped_lock lock(mLock);
80 const vec2 targetPosition = mLocked.pointerPosition + delta;
81 setPositionLocked(targetPosition);
82 // The amount of the delta that was not consumed as a result of the cursor
83 // hitting the edge of the display.
84 return targetPosition - mLocked.pointerPosition;
85 }
86
setPosition(vec2 position)87 void MouseCursorController::setPosition(vec2 position) {
88 #if DEBUG_MOUSE_CURSOR_UPDATES
89 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
90 #endif
91 std::scoped_lock lock(mLock);
92 setPositionLocked(position);
93 }
94
getBoundsLocked()95 FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) {
96 // The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside
97 // the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily
98 // close to the outside edge.
99 return FloatRect{
100 static_cast<float>(mLocked.viewport.logicalLeft),
101 static_cast<float>(mLocked.viewport.logicalTop),
102 static_cast<float>(mLocked.viewport.logicalRight - 1),
103 static_cast<float>(mLocked.viewport.logicalBottom - 1),
104 };
105 }
106
setPositionLocked(vec2 position)107 void MouseCursorController::setPositionLocked(vec2 position) REQUIRES(mLock) {
108 const auto& v = mLocked.viewport;
109 if (!v.isValid()) return;
110
111 const FloatRect bounds = getBoundsLocked();
112 mLocked.pointerPosition.x = std::max(bounds.left, std::min(bounds.right, position.x));
113 mLocked.pointerPosition.y = std::max(bounds.top, std::min(bounds.bottom, position.y));
114
115 updatePointerLocked();
116 }
117
getPosition() const118 vec2 MouseCursorController::getPosition() const {
119 std::scoped_lock lock(mLock);
120
121 return mLocked.pointerPosition;
122 }
123
getDisplayId() const124 ui::LogicalDisplayId MouseCursorController::getDisplayId() const {
125 std::scoped_lock lock(mLock);
126 return mLocked.viewport.displayId;
127 }
128
fade(PointerControllerInterface::Transition transition)129 void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
130 std::scoped_lock lock(mLock);
131
132 // Remove the inactivity timeout, since we are fading now.
133 mContext.removeInactivityTimeout();
134
135 // Start fading.
136 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
137 mLocked.pointerFadeDirection = 0;
138 mLocked.pointerAlpha = 0.0f;
139 updatePointerLocked();
140 } else {
141 mLocked.pointerFadeDirection = -1;
142 startAnimationLocked();
143 }
144 }
145
unfade(PointerControllerInterface::Transition transition)146 void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
147 std::scoped_lock lock(mLock);
148
149 // Always reset the inactivity timer.
150 mContext.resetInactivityTimeout();
151
152 // Start unfading.
153 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
154 mLocked.pointerFadeDirection = 0;
155 mLocked.pointerAlpha = 1.0f;
156 updatePointerLocked();
157 } else {
158 mLocked.pointerFadeDirection = 1;
159 startAnimationLocked();
160 }
161 }
162
setStylusHoverMode(bool stylusHoverMode)163 void MouseCursorController::setStylusHoverMode(bool stylusHoverMode) {
164 std::scoped_lock lock(mLock);
165
166 if (mLocked.stylusHoverMode != stylusHoverMode) {
167 mLocked.stylusHoverMode = stylusHoverMode;
168 mLocked.updatePointerIcon = true;
169 }
170 }
171
setSkipScreenshot(bool skip)172 void MouseCursorController::setSkipScreenshot(bool skip) {
173 std::scoped_lock lock(mLock);
174 if (mLocked.skipScreenshot == skip) {
175 return;
176 }
177 mLocked.skipScreenshot = skip;
178 updatePointerLocked();
179 }
180
reloadPointerResources(bool getAdditionalMouseResources)181 void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
182 std::scoped_lock lock(mLock);
183
184 loadResourcesLocked(getAdditionalMouseResources);
185 updatePointerLocked();
186 }
187
188 /**
189 * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
190 * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
191 */
getNonRotatedSize(const DisplayViewport & viewport,int32_t & width,int32_t & height)192 static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
193 width = viewport.deviceWidth;
194 height = viewport.deviceHeight;
195
196 if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
197 std::swap(width, height);
198 }
199 }
200
setDisplayViewport(const DisplayViewport & viewport,bool getAdditionalMouseResources)201 void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
202 bool getAdditionalMouseResources) {
203 std::scoped_lock lock(mLock);
204
205 if (viewport == mLocked.viewport) {
206 return;
207 }
208
209 const DisplayViewport oldViewport = mLocked.viewport;
210 mLocked.viewport = viewport;
211
212 int32_t oldDisplayWidth, oldDisplayHeight;
213 getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
214 int32_t newDisplayWidth, newDisplayHeight;
215 getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
216
217 // Reset cursor position to center if size or display changed.
218 if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
219 oldDisplayHeight != newDisplayHeight) {
220 if (viewport.isValid()) {
221 // Use integer coordinates as the starting point for the cursor location.
222 // We usually expect display sizes to be even numbers, so the flooring is precautionary.
223 mLocked.pointerPosition.x =
224 std::floor((viewport.logicalLeft + viewport.logicalRight) / 2);
225 mLocked.pointerPosition.y =
226 std::floor((viewport.logicalTop + viewport.logicalBottom) / 2);
227 // Reload icon resources for density may be changed.
228 loadResourcesLocked(getAdditionalMouseResources);
229 } else {
230 mLocked.pointerPosition = {0, 0};
231 }
232 } else if (oldViewport.orientation != viewport.orientation) {
233 // Apply offsets to convert from the pixel top-left corner position to the pixel center.
234 // This creates an invariant frame of reference that we can easily rotate when
235 // taking into account that the pointer may be located at fractional pixel offsets.
236 float x = mLocked.pointerPosition.x + 0.5f;
237 float y = mLocked.pointerPosition.y + 0.5f;
238 float temp;
239
240 // Undo the previous rotation.
241 switch (oldViewport.orientation) {
242 case ui::ROTATION_90:
243 temp = x;
244 x = oldViewport.deviceHeight - y;
245 y = temp;
246 break;
247 case ui::ROTATION_180:
248 x = oldViewport.deviceWidth - x;
249 y = oldViewport.deviceHeight - y;
250 break;
251 case ui::ROTATION_270:
252 temp = x;
253 x = y;
254 y = oldViewport.deviceWidth - temp;
255 break;
256 case ui::ROTATION_0:
257 break;
258 }
259
260 // Perform the new rotation.
261 switch (viewport.orientation) {
262 case ui::ROTATION_90:
263 temp = x;
264 x = y;
265 y = viewport.deviceHeight - temp;
266 break;
267 case ui::ROTATION_180:
268 x = viewport.deviceWidth - x;
269 y = viewport.deviceHeight - y;
270 break;
271 case ui::ROTATION_270:
272 temp = x;
273 x = viewport.deviceWidth - y;
274 y = temp;
275 break;
276 case ui::ROTATION_0:
277 break;
278 }
279
280 // Apply offsets to convert from the pixel center to the pixel top-left corner position
281 // and save the results.
282 mLocked.pointerPosition.x = x - 0.5f;
283 mLocked.pointerPosition.y = y - 0.5f;
284 }
285
286 updatePointerLocked();
287 }
288
updatePointerIcon(PointerIconStyle iconId)289 void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
290 std::scoped_lock lock(mLock);
291
292 if (mLocked.requestedPointerType != iconId) {
293 mLocked.requestedPointerType = iconId;
294 mLocked.updatePointerIcon = true;
295 updatePointerLocked();
296 }
297 }
298
setCustomPointerIcon(const SpriteIcon & icon)299 void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
300 std::scoped_lock lock(mLock);
301
302 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
303 mLocked.additionalMouseResources[iconId] = icon;
304 mLocked.requestedPointerType = iconId;
305 mLocked.updatePointerIcon = true;
306 updatePointerLocked();
307 }
308
doFadingAnimationLocked(nsecs_t timestamp)309 bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
310 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
311 bool keepAnimating = false;
312
313 // Animate pointer fade.
314 if (mLocked.pointerFadeDirection < 0) {
315 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
316 if (mLocked.pointerAlpha <= 0.0f) {
317 mLocked.pointerAlpha = 0.0f;
318 mLocked.pointerFadeDirection = 0;
319 } else {
320 keepAnimating = true;
321 }
322 updatePointerLocked();
323 } else if (mLocked.pointerFadeDirection > 0) {
324 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
325 if (mLocked.pointerAlpha >= 1.0f) {
326 mLocked.pointerAlpha = 1.0f;
327 mLocked.pointerFadeDirection = 0;
328 } else {
329 keepAnimating = true;
330 }
331 updatePointerLocked();
332 }
333 return keepAnimating;
334 }
335
doBitmapAnimationLocked(nsecs_t timestamp)336 bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
337 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
338 mLocked.animationResources.find(mLocked.resolvedPointerType);
339 if (iter == mLocked.animationResources.end()) {
340 return false;
341 }
342
343 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
344 auto& spriteController = mContext.getSpriteController();
345 spriteController.openTransaction();
346
347 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
348 mLocked.animationFrameIndex += incr;
349 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
350 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
351 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
352 }
353 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
354
355 spriteController.closeTransaction();
356 }
357 // Keep animating.
358 return true;
359 }
360
updatePointerLocked()361 void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
362 if (!mLocked.viewport.isValid()) {
363 return;
364 }
365 auto& spriteController = mContext.getSpriteController();
366 spriteController.openTransaction();
367
368 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
369 mLocked.pointerSprite->setPosition(mLocked.pointerPosition.x, mLocked.pointerPosition.y);
370 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
371 mLocked.pointerSprite->setSkipScreenshot(mLocked.skipScreenshot);
372
373 if (mLocked.pointerAlpha > 0) {
374 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
375 mLocked.pointerSprite->setVisible(true);
376 } else {
377 mLocked.pointerSprite->setVisible(false);
378 }
379
380 if (mLocked.updatePointerIcon) {
381 mLocked.resolvedPointerType = mLocked.requestedPointerType;
382 const PointerIconStyle defaultPointerIconId =
383 mContext.getPolicy()->getDefaultPointerIconId();
384 if (mLocked.resolvedPointerType == PointerIconStyle::TYPE_NOT_SPECIFIED) {
385 mLocked.resolvedPointerType = mLocked.stylusHoverMode
386 ? mContext.getPolicy()->getDefaultStylusIconId()
387 : defaultPointerIconId;
388 }
389
390 if (mLocked.resolvedPointerType == defaultPointerIconId) {
391 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
392 } else {
393 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
394 mLocked.additionalMouseResources.find(mLocked.resolvedPointerType);
395 if (iter != mLocked.additionalMouseResources.end()) {
396 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
397 mLocked.animationResources.find(mLocked.resolvedPointerType);
398 if (anim_iter != mLocked.animationResources.end()) {
399 mLocked.animationFrameIndex = 0;
400 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
401 startAnimationLocked();
402 }
403 mLocked.pointerSprite->setIcon(iter->second);
404 } else {
405 ALOGW("Can't find the resource for icon id %d", mLocked.resolvedPointerType);
406 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
407 }
408 }
409 mLocked.updatePointerIcon = false;
410 }
411
412 spriteController.closeTransaction();
413 }
414
loadResourcesLocked(bool getAdditionalMouseResources)415 void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
416 if (!mLocked.viewport.isValid()) {
417 return;
418 }
419
420 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
421
422 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
423 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
424 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
425
426 mLocked.additionalMouseResources.clear();
427 mLocked.animationResources.clear();
428 if (getAdditionalMouseResources) {
429 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
430 &mLocked.animationResources,
431 mLocked.viewport.displayId);
432 }
433
434 mLocked.updatePointerIcon = true;
435 }
436
isViewportValid()437 bool MouseCursorController::isViewportValid() {
438 std::scoped_lock lock(mLock);
439 return mLocked.viewport.isValid();
440 }
441
getAdditionalMouseResources()442 void MouseCursorController::getAdditionalMouseResources() {
443 std::scoped_lock lock(mLock);
444
445 if (mLocked.additionalMouseResources.empty()) {
446 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
447 &mLocked.animationResources,
448 mLocked.viewport.displayId);
449 }
450 mLocked.updatePointerIcon = true;
451 updatePointerLocked();
452 }
453
resourcesLoaded()454 bool MouseCursorController::resourcesLoaded() {
455 std::scoped_lock lock(mLock);
456 return mLocked.resourcesLoaded;
457 }
458
dump() const459 std::string MouseCursorController::dump() const {
460 std::string dump = INDENT "MouseCursorController:\n";
461 std::scoped_lock lock(mLock);
462 dump += StringPrintf(INDENT2 "viewport: %s\n", mLocked.viewport.toString().c_str());
463 dump += StringPrintf(INDENT2 "stylusHoverMode: %s\n",
464 mLocked.stylusHoverMode ? "true" : "false");
465 dump += StringPrintf(INDENT2 "pointerFadeDirection: %d\n", mLocked.pointerFadeDirection);
466 dump += StringPrintf(INDENT2 "updatePointerIcon: %s\n",
467 mLocked.updatePointerIcon ? "true" : "false");
468 dump += StringPrintf(INDENT2 "resourcesLoaded: %s\n",
469 mLocked.resourcesLoaded ? "true" : "false");
470 dump += StringPrintf(INDENT2 "requestedPointerType: %d\n", mLocked.requestedPointerType);
471 dump += StringPrintf(INDENT2 "resolvedPointerType: %d\n", mLocked.resolvedPointerType);
472 dump += StringPrintf(INDENT2 "skipScreenshot: %s\n", mLocked.skipScreenshot ? "true" : "false");
473 dump += StringPrintf(INDENT2 "animating: %s\n", mLocked.animating ? "true" : "false");
474 return dump;
475 }
476
doAnimations(nsecs_t timestamp)477 bool MouseCursorController::doAnimations(nsecs_t timestamp) {
478 std::scoped_lock lock(mLock);
479 bool keepFading = doFadingAnimationLocked(timestamp);
480 bool keepBitmap = doBitmapAnimationLocked(timestamp);
481 bool keepAnimating = keepFading || keepBitmap;
482 if (!keepAnimating) {
483 /*
484 * We know that this callback will be removed before another
485 * is added. mLock in PointerAnimator will not be released
486 * until after this is removed, and adding another callback
487 * requires that lock. Thus it's safe to set mLocked.animating
488 * here.
489 */
490 mLocked.animating = false;
491 }
492 return keepAnimating;
493 }
494
startAnimationLocked()495 void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
496 using namespace std::placeholders;
497
498 if (mLocked.animating) {
499 return;
500 }
501 mLocked.animating = true;
502
503 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
504 /*
505 * Using ui::LogicalDisplayId::INVALID for displayId here to avoid removing the callback
506 * if a TouchSpotController with the same display is removed.
507 */
508 mContext.addAnimationCallback(ui::LogicalDisplayId::INVALID, func);
509 }
510
511 } // namespace android
512