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