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