blob: 24cfc9d70c9b4c3abcc3716c7233c268ca622906 [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
Brandon Pollack015f5d92022-06-02 06:59:33 +000025#include <input/Input.h>
Liam Harringtonc782be62020-07-17 19:48:24 +000026#include <log/log.h>
27
Liam Harringtonc782be62020-07-17 19:48:24 +000028namespace {
29// Time to spend fading out the pointer completely.
30const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
31} // namespace
32
33namespace android {
34
35// --- MouseCursorController ---
36
37MouseCursorController::MouseCursorController(PointerControllerContext& context)
38 : mContext(context) {
39 std::scoped_lock lock(mLock);
40
Seunghwan Choi670b33d2023-01-13 21:12:59 +090041 mLocked.stylusHoverMode = false;
42
Liam Harringtonc782be62020-07-17 19:48:24 +000043 mLocked.animationFrameIndex = 0;
44 mLocked.lastFrameUpdatedTime = 0;
45
46 mLocked.pointerFadeDirection = 0;
47 mLocked.pointerX = 0;
48 mLocked.pointerY = 0;
49 mLocked.pointerAlpha = 0.0f; // pointer is initially faded
50 mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
51 mLocked.updatePointerIcon = false;
Seunghwan Choi670b33d2023-01-13 21:12:59 +090052 mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
53 mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
Liam Harringtonc782be62020-07-17 19:48:24 +000054
55 mLocked.resourcesLoaded = false;
56
57 mLocked.buttonState = 0;
58}
59
60MouseCursorController::~MouseCursorController() {
61 std::scoped_lock lock(mLock);
62
63 mLocked.pointerSprite.clear();
64}
65
66bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
67 float* outMaxY) const {
68 std::scoped_lock lock(mLock);
69
70 return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
71}
72
73bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX,
74 float* outMaxY) const REQUIRES(mLock) {
75 if (!mLocked.viewport.isValid()) {
76 return false;
77 }
78
79 *outMinX = mLocked.viewport.logicalLeft;
80 *outMinY = mLocked.viewport.logicalTop;
81 *outMaxX = mLocked.viewport.logicalRight - 1;
82 *outMaxY = mLocked.viewport.logicalBottom - 1;
83 return true;
84}
85
86void MouseCursorController::move(float deltaX, float deltaY) {
87#if DEBUG_MOUSE_CURSOR_UPDATES
88 ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
89#endif
90 if (deltaX == 0.0f && deltaY == 0.0f) {
91 return;
92 }
93
94 std::scoped_lock lock(mLock);
95
96 setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
97}
98
99void MouseCursorController::setButtonState(int32_t buttonState) {
100#if DEBUG_MOUSE_CURSOR_UPDATES
101 ALOGD("Set button state 0x%08x", buttonState);
102#endif
103 std::scoped_lock lock(mLock);
104
105 if (mLocked.buttonState != buttonState) {
106 mLocked.buttonState = buttonState;
107 }
108}
109
110int32_t MouseCursorController::getButtonState() const {
111 std::scoped_lock lock(mLock);
112 return mLocked.buttonState;
113}
114
115void MouseCursorController::setPosition(float x, float y) {
116#if DEBUG_MOUSE_CURSOR_UPDATES
117 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
118#endif
119 std::scoped_lock lock(mLock);
120 setPositionLocked(x, y);
121}
122
123void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
124 float minX, minY, maxX, maxY;
125 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
126 if (x <= minX) {
127 mLocked.pointerX = minX;
128 } else if (x >= maxX) {
129 mLocked.pointerX = maxX;
130 } else {
131 mLocked.pointerX = x;
132 }
133 if (y <= minY) {
134 mLocked.pointerY = minY;
135 } else if (y >= maxY) {
136 mLocked.pointerY = maxY;
137 } else {
138 mLocked.pointerY = y;
139 }
140 updatePointerLocked();
141 }
142}
143
144void MouseCursorController::getPosition(float* outX, float* outY) const {
145 std::scoped_lock lock(mLock);
146
147 *outX = mLocked.pointerX;
148 *outY = mLocked.pointerY;
149}
150
151int32_t MouseCursorController::getDisplayId() const {
152 std::scoped_lock lock(mLock);
153 return mLocked.viewport.displayId;
154}
155
156void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
157 std::scoped_lock lock(mLock);
158
159 // Remove the inactivity timeout, since we are fading now.
160 mContext.removeInactivityTimeout();
161
162 // Start fading.
163 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
164 mLocked.pointerFadeDirection = 0;
165 mLocked.pointerAlpha = 0.0f;
166 updatePointerLocked();
167 } else {
168 mLocked.pointerFadeDirection = -1;
Liam Harringtonce637132020-08-14 04:00:11 +0000169 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000170 }
171}
172
173void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
174 std::scoped_lock lock(mLock);
175
176 // Always reset the inactivity timer.
177 mContext.resetInactivityTimeout();
178
179 // Start unfading.
180 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
181 mLocked.pointerFadeDirection = 0;
182 mLocked.pointerAlpha = 1.0f;
183 updatePointerLocked();
184 } else {
185 mLocked.pointerFadeDirection = 1;
Liam Harringtonce637132020-08-14 04:00:11 +0000186 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000187 }
188}
189
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900190void MouseCursorController::setStylusHoverMode(bool stylusHoverMode) {
191 std::scoped_lock lock(mLock);
192
193 if (mLocked.stylusHoverMode != stylusHoverMode) {
194 mLocked.stylusHoverMode = stylusHoverMode;
195 mLocked.updatePointerIcon = true;
196 }
197}
198
Liam Harringtonc782be62020-07-17 19:48:24 +0000199void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
200 std::scoped_lock lock(mLock);
201
202 loadResourcesLocked(getAdditionalMouseResources);
203 updatePointerLocked();
204}
205
206/**
207 * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
208 * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
209 */
210static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
211 width = viewport.deviceWidth;
212 height = viewport.deviceHeight;
213
Michael Wrightecde4d02022-11-25 00:20:19 +0000214 if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000215 std::swap(width, height);
216 }
217}
218
219void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
220 bool getAdditionalMouseResources) {
221 std::scoped_lock lock(mLock);
222
223 if (viewport == mLocked.viewport) {
224 return;
225 }
226
227 const DisplayViewport oldViewport = mLocked.viewport;
228 mLocked.viewport = viewport;
229
230 int32_t oldDisplayWidth, oldDisplayHeight;
231 getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
232 int32_t newDisplayWidth, newDisplayHeight;
233 getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
234
235 // Reset cursor position to center if size or display changed.
236 if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
237 oldDisplayHeight != newDisplayHeight) {
238 float minX, minY, maxX, maxY;
239 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
240 mLocked.pointerX = (minX + maxX) * 0.5f;
241 mLocked.pointerY = (minY + maxY) * 0.5f;
242 // Reload icon resources for density may be changed.
243 loadResourcesLocked(getAdditionalMouseResources);
244 } else {
245 mLocked.pointerX = 0;
246 mLocked.pointerY = 0;
247 }
248 } else if (oldViewport.orientation != viewport.orientation) {
249 // Apply offsets to convert from the pixel top-left corner position to the pixel center.
250 // This creates an invariant frame of reference that we can easily rotate when
251 // taking into account that the pointer may be located at fractional pixel offsets.
252 float x = mLocked.pointerX + 0.5f;
253 float y = mLocked.pointerY + 0.5f;
254 float temp;
255
256 // Undo the previous rotation.
257 switch (oldViewport.orientation) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000258 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000259 temp = x;
260 x = oldViewport.deviceHeight - y;
261 y = temp;
262 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000263 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000264 x = oldViewport.deviceWidth - x;
265 y = oldViewport.deviceHeight - y;
266 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000267 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000268 temp = x;
269 x = y;
270 y = oldViewport.deviceWidth - temp;
271 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000272 case ui::ROTATION_0:
273 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000274 }
275
276 // Perform the new rotation.
277 switch (viewport.orientation) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000278 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000279 temp = x;
280 x = y;
281 y = viewport.deviceHeight - temp;
282 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000283 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000284 x = viewport.deviceWidth - x;
285 y = viewport.deviceHeight - y;
286 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000287 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000288 temp = x;
289 x = viewport.deviceWidth - y;
290 y = temp;
291 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000292 case ui::ROTATION_0:
293 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000294 }
295
296 // Apply offsets to convert from the pixel center to the pixel top-left corner position
297 // and save the results.
298 mLocked.pointerX = x - 0.5f;
299 mLocked.pointerY = y - 0.5f;
300 }
301
302 updatePointerLocked();
303}
304
Brandon Pollack015f5d92022-06-02 06:59:33 +0000305void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000306 std::scoped_lock lock(mLock);
307
308 if (mLocked.requestedPointerType != iconId) {
309 mLocked.requestedPointerType = iconId;
310 mLocked.updatePointerIcon = true;
311 updatePointerLocked();
312 }
313}
314
315void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
316 std::scoped_lock lock(mLock);
317
Brandon Pollack015f5d92022-06-02 06:59:33 +0000318 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
Liam Harringtonc782be62020-07-17 19:48:24 +0000319 mLocked.additionalMouseResources[iconId] = icon;
320 mLocked.requestedPointerType = iconId;
321 mLocked.updatePointerIcon = true;
322 updatePointerLocked();
323}
324
Liam Harringtonce637132020-08-14 04:00:11 +0000325bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000326 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
Liam Harringtonce637132020-08-14 04:00:11 +0000327 bool keepAnimating = false;
Liam Harringtonc782be62020-07-17 19:48:24 +0000328
329 // Animate pointer fade.
330 if (mLocked.pointerFadeDirection < 0) {
331 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
332 if (mLocked.pointerAlpha <= 0.0f) {
333 mLocked.pointerAlpha = 0.0f;
334 mLocked.pointerFadeDirection = 0;
335 } else {
336 keepAnimating = true;
337 }
338 updatePointerLocked();
339 } else if (mLocked.pointerFadeDirection > 0) {
340 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
341 if (mLocked.pointerAlpha >= 1.0f) {
342 mLocked.pointerAlpha = 1.0f;
343 mLocked.pointerFadeDirection = 0;
344 } else {
345 keepAnimating = true;
346 }
347 updatePointerLocked();
348 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000349 return keepAnimating;
350}
351
Liam Harringtonce637132020-08-14 04:00:11 +0000352bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000353 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900354 mLocked.animationResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000355 if (iter == mLocked.animationResources.end()) {
356 return false;
357 }
358
359 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
360 sp<SpriteController> spriteController = mContext.getSpriteController();
361 spriteController->openTransaction();
362
363 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
364 mLocked.animationFrameIndex += incr;
365 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
366 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
367 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
368 }
369 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
370
371 spriteController->closeTransaction();
372 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000373 // Keep animating.
374 return true;
375}
376
377void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
378 if (!mLocked.viewport.isValid()) {
379 return;
380 }
381 sp<SpriteController> spriteController = mContext.getSpriteController();
382 spriteController->openTransaction();
383
384 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
385 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
386 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
387
388 if (mLocked.pointerAlpha > 0) {
389 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
390 mLocked.pointerSprite->setVisible(true);
391 } else {
392 mLocked.pointerSprite->setVisible(false);
393 }
394
395 if (mLocked.updatePointerIcon) {
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900396 mLocked.resolvedPointerType = mLocked.requestedPointerType;
397 const PointerIconStyle defaultPointerIconId =
398 mContext.getPolicy()->getDefaultPointerIconId();
399 if (mLocked.resolvedPointerType == PointerIconStyle::TYPE_NOT_SPECIFIED) {
400 mLocked.resolvedPointerType = mLocked.stylusHoverMode
401 ? mContext.getPolicy()->getDefaultStylusIconId()
402 : defaultPointerIconId;
403 }
404
405 if (mLocked.resolvedPointerType == defaultPointerIconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000406 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
407 } else {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000408 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900409 mLocked.additionalMouseResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000410 if (iter != mLocked.additionalMouseResources.end()) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000411 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900412 mLocked.animationResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000413 if (anim_iter != mLocked.animationResources.end()) {
414 mLocked.animationFrameIndex = 0;
415 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
Liam Harringtonce637132020-08-14 04:00:11 +0000416 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000417 }
418 mLocked.pointerSprite->setIcon(iter->second);
419 } else {
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900420 ALOGW("Can't find the resource for icon id %d", mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000421 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
422 }
423 }
424 mLocked.updatePointerIcon = false;
425 }
426
427 spriteController->closeTransaction();
428}
429
430void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
431 if (!mLocked.viewport.isValid()) {
432 return;
433 }
434
435 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
436
437 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
438 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
439 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
440
441 mLocked.additionalMouseResources.clear();
442 mLocked.animationResources.clear();
443 if (getAdditionalMouseResources) {
444 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
445 &mLocked.animationResources,
446 mLocked.viewport.displayId);
447 }
448
449 mLocked.updatePointerIcon = true;
450}
451
452bool MouseCursorController::isViewportValid() {
453 std::scoped_lock lock(mLock);
454 return mLocked.viewport.isValid();
455}
456
457void MouseCursorController::getAdditionalMouseResources() {
458 std::scoped_lock lock(mLock);
459
460 if (mLocked.additionalMouseResources.empty()) {
461 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
462 &mLocked.animationResources,
463 mLocked.viewport.displayId);
464 }
465 mLocked.updatePointerIcon = true;
466 updatePointerLocked();
467}
468
469bool MouseCursorController::resourcesLoaded() {
470 std::scoped_lock lock(mLock);
471 return mLocked.resourcesLoaded;
472}
473
Liam Harringtonce637132020-08-14 04:00:11 +0000474bool MouseCursorController::doAnimations(nsecs_t timestamp) {
475 std::scoped_lock lock(mLock);
476 bool keepFading = doFadingAnimationLocked(timestamp);
477 bool keepBitmap = doBitmapAnimationLocked(timestamp);
478 bool keepAnimating = keepFading || keepBitmap;
479 if (!keepAnimating) {
480 /*
481 * We know that this callback will be removed before another
482 * is added. mLock in PointerAnimator will not be released
483 * until after this is removed, and adding another callback
484 * requires that lock. Thus it's safe to set mLocked.animating
485 * here.
486 */
487 mLocked.animating = false;
488 }
489 return keepAnimating;
490}
491
492void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
493 using namespace std::placeholders;
494
495 if (mLocked.animating) {
496 return;
497 }
498 mLocked.animating = true;
499
500 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
501 /*
502 * Using -1 for displayId here to avoid removing the callback
503 * if a TouchSpotController with the same display is removed.
504 */
505 mContext.addAnimationCallback(-1, func);
506}
507
Liam Harringtonc782be62020-07-17 19:48:24 +0000508} // namespace android