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