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 int32_t 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 
reloadPointerResources(bool getAdditionalMouseResources)168 void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
169     std::scoped_lock lock(mLock);
170 
171     loadResourcesLocked(getAdditionalMouseResources);
172     updatePointerLocked();
173 }
174 
175 /**
176  * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
177  * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
178  */
getNonRotatedSize(const DisplayViewport & viewport,int32_t & width,int32_t & height)179 static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
180     width = viewport.deviceWidth;
181     height = viewport.deviceHeight;
182 
183     if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
184         std::swap(width, height);
185     }
186 }
187 
setDisplayViewport(const DisplayViewport & viewport,bool getAdditionalMouseResources)188 void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
189                                                bool getAdditionalMouseResources) {
190     std::scoped_lock lock(mLock);
191 
192     if (viewport == mLocked.viewport) {
193         return;
194     }
195 
196     const DisplayViewport oldViewport = mLocked.viewport;
197     mLocked.viewport = viewport;
198 
199     int32_t oldDisplayWidth, oldDisplayHeight;
200     getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
201     int32_t newDisplayWidth, newDisplayHeight;
202     getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
203 
204     // Reset cursor position to center if size or display changed.
205     if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
206         oldDisplayHeight != newDisplayHeight) {
207         if (const auto bounds = getBoundsLocked(); bounds) {
208             mLocked.pointerX = (bounds->left + bounds->right) * 0.5f;
209             mLocked.pointerY = (bounds->top + bounds->bottom) * 0.5f;
210             // Reload icon resources for density may be changed.
211             loadResourcesLocked(getAdditionalMouseResources);
212         } else {
213             mLocked.pointerX = 0;
214             mLocked.pointerY = 0;
215         }
216     } else if (oldViewport.orientation != viewport.orientation) {
217         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
218         // This creates an invariant frame of reference that we can easily rotate when
219         // taking into account that the pointer may be located at fractional pixel offsets.
220         float x = mLocked.pointerX + 0.5f;
221         float y = mLocked.pointerY + 0.5f;
222         float temp;
223 
224         // Undo the previous rotation.
225         switch (oldViewport.orientation) {
226             case ui::ROTATION_90:
227                 temp = x;
228                 x = oldViewport.deviceHeight - y;
229                 y = temp;
230                 break;
231             case ui::ROTATION_180:
232                 x = oldViewport.deviceWidth - x;
233                 y = oldViewport.deviceHeight - y;
234                 break;
235             case ui::ROTATION_270:
236                 temp = x;
237                 x = y;
238                 y = oldViewport.deviceWidth - temp;
239                 break;
240             case ui::ROTATION_0:
241                 break;
242         }
243 
244         // Perform the new rotation.
245         switch (viewport.orientation) {
246             case ui::ROTATION_90:
247                 temp = x;
248                 x = y;
249                 y = viewport.deviceHeight - temp;
250                 break;
251             case ui::ROTATION_180:
252                 x = viewport.deviceWidth - x;
253                 y = viewport.deviceHeight - y;
254                 break;
255             case ui::ROTATION_270:
256                 temp = x;
257                 x = viewport.deviceWidth - y;
258                 y = temp;
259                 break;
260             case ui::ROTATION_0:
261                 break;
262         }
263 
264         // Apply offsets to convert from the pixel center to the pixel top-left corner position
265         // and save the results.
266         mLocked.pointerX = x - 0.5f;
267         mLocked.pointerY = y - 0.5f;
268     }
269 
270     updatePointerLocked();
271 }
272 
updatePointerIcon(PointerIconStyle iconId)273 void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
274     std::scoped_lock lock(mLock);
275 
276     if (mLocked.requestedPointerType != iconId) {
277         mLocked.requestedPointerType = iconId;
278         mLocked.updatePointerIcon = true;
279         updatePointerLocked();
280     }
281 }
282 
setCustomPointerIcon(const SpriteIcon & icon)283 void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
284     std::scoped_lock lock(mLock);
285 
286     const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
287     mLocked.additionalMouseResources[iconId] = icon;
288     mLocked.requestedPointerType = iconId;
289     mLocked.updatePointerIcon = true;
290     updatePointerLocked();
291 }
292 
doFadingAnimationLocked(nsecs_t timestamp)293 bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
294     nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
295     bool keepAnimating = false;
296 
297     // Animate pointer fade.
298     if (mLocked.pointerFadeDirection < 0) {
299         mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
300         if (mLocked.pointerAlpha <= 0.0f) {
301             mLocked.pointerAlpha = 0.0f;
302             mLocked.pointerFadeDirection = 0;
303         } else {
304             keepAnimating = true;
305         }
306         updatePointerLocked();
307     } else if (mLocked.pointerFadeDirection > 0) {
308         mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
309         if (mLocked.pointerAlpha >= 1.0f) {
310             mLocked.pointerAlpha = 1.0f;
311             mLocked.pointerFadeDirection = 0;
312         } else {
313             keepAnimating = true;
314         }
315         updatePointerLocked();
316     }
317     return keepAnimating;
318 }
319 
doBitmapAnimationLocked(nsecs_t timestamp)320 bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
321     std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
322             mLocked.animationResources.find(mLocked.resolvedPointerType);
323     if (iter == mLocked.animationResources.end()) {
324         return false;
325     }
326 
327     if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
328         sp<SpriteController> spriteController = mContext.getSpriteController();
329         spriteController->openTransaction();
330 
331         int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
332         mLocked.animationFrameIndex += incr;
333         mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
334         while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
335             mLocked.animationFrameIndex -= iter->second.animationFrames.size();
336         }
337         mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
338 
339         spriteController->closeTransaction();
340     }
341     // Keep animating.
342     return true;
343 }
344 
updatePointerLocked()345 void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
346     if (!mLocked.viewport.isValid()) {
347         return;
348     }
349     sp<SpriteController> spriteController = mContext.getSpriteController();
350     spriteController->openTransaction();
351 
352     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
353     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
354     mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
355 
356     if (mLocked.pointerAlpha > 0) {
357         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
358         mLocked.pointerSprite->setVisible(true);
359     } else {
360         mLocked.pointerSprite->setVisible(false);
361     }
362 
363     if (mLocked.updatePointerIcon) {
364         mLocked.resolvedPointerType = mLocked.requestedPointerType;
365         const PointerIconStyle defaultPointerIconId =
366                 mContext.getPolicy()->getDefaultPointerIconId();
367         if (mLocked.resolvedPointerType == PointerIconStyle::TYPE_NOT_SPECIFIED) {
368             mLocked.resolvedPointerType = mLocked.stylusHoverMode
369                     ? mContext.getPolicy()->getDefaultStylusIconId()
370                     : defaultPointerIconId;
371         }
372 
373         if (mLocked.resolvedPointerType == defaultPointerIconId) {
374             mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
375         } else {
376             std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
377                     mLocked.additionalMouseResources.find(mLocked.resolvedPointerType);
378             if (iter != mLocked.additionalMouseResources.end()) {
379                 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
380                         mLocked.animationResources.find(mLocked.resolvedPointerType);
381                 if (anim_iter != mLocked.animationResources.end()) {
382                     mLocked.animationFrameIndex = 0;
383                     mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
384                     startAnimationLocked();
385                 }
386                 mLocked.pointerSprite->setIcon(iter->second);
387             } else {
388                 ALOGW("Can't find the resource for icon id %d", mLocked.resolvedPointerType);
389                 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
390             }
391         }
392         mLocked.updatePointerIcon = false;
393     }
394 
395     spriteController->closeTransaction();
396 }
397 
loadResourcesLocked(bool getAdditionalMouseResources)398 void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
399     if (!mLocked.viewport.isValid()) {
400         return;
401     }
402 
403     if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
404 
405     sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
406     policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
407     policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
408 
409     mLocked.additionalMouseResources.clear();
410     mLocked.animationResources.clear();
411     if (getAdditionalMouseResources) {
412         policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
413                                              &mLocked.animationResources,
414                                              mLocked.viewport.displayId);
415     }
416 
417     mLocked.updatePointerIcon = true;
418 }
419 
isViewportValid()420 bool MouseCursorController::isViewportValid() {
421     std::scoped_lock lock(mLock);
422     return mLocked.viewport.isValid();
423 }
424 
getAdditionalMouseResources()425 void MouseCursorController::getAdditionalMouseResources() {
426     std::scoped_lock lock(mLock);
427 
428     if (mLocked.additionalMouseResources.empty()) {
429         mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
430                                                            &mLocked.animationResources,
431                                                            mLocked.viewport.displayId);
432     }
433     mLocked.updatePointerIcon = true;
434     updatePointerLocked();
435 }
436 
resourcesLoaded()437 bool MouseCursorController::resourcesLoaded() {
438     std::scoped_lock lock(mLock);
439     return mLocked.resourcesLoaded;
440 }
441 
doAnimations(nsecs_t timestamp)442 bool MouseCursorController::doAnimations(nsecs_t timestamp) {
443     std::scoped_lock lock(mLock);
444     bool keepFading = doFadingAnimationLocked(timestamp);
445     bool keepBitmap = doBitmapAnimationLocked(timestamp);
446     bool keepAnimating = keepFading || keepBitmap;
447     if (!keepAnimating) {
448         /*
449          * We know that this callback will be removed before another
450          * is added. mLock in PointerAnimator will not be released
451          * until after this is removed, and adding another callback
452          * requires that lock. Thus it's safe to set mLocked.animating
453          * here.
454          */
455         mLocked.animating = false;
456     }
457     return keepAnimating;
458 }
459 
startAnimationLocked()460 void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
461     using namespace std::placeholders;
462 
463     if (mLocked.animating) {
464         return;
465     }
466     mLocked.animating = true;
467 
468     std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
469     /*
470      * Using -1 for displayId here to avoid removing the callback
471      * if a TouchSpotController with the same display is removed.
472      */
473     mContext.addAnimationCallback(-1, func);
474 }
475 
476 } // namespace android
477