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