xref: /aosp_15_r20/frameworks/base/libs/input/MouseCursorController.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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