blob: a83516791f332393f9ae2b9fb14e651da3682bb3 [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
41 mLocked.animationFrameIndex = 0;
42 mLocked.lastFrameUpdatedTime = 0;
43
44 mLocked.pointerFadeDirection = 0;
45 mLocked.pointerX = 0;
46 mLocked.pointerY = 0;
47 mLocked.pointerAlpha = 0.0f; // pointer is initially faded
48 mLocked.pointerSprite = mContext.getSpriteController()->createSprite();
49 mLocked.updatePointerIcon = false;
50 mLocked.requestedPointerType = mContext.getPolicy()->getDefaultPointerIconId();
51
52 mLocked.resourcesLoaded = false;
53
54 mLocked.buttonState = 0;
55}
56
57MouseCursorController::~MouseCursorController() {
58 std::scoped_lock lock(mLock);
59
60 mLocked.pointerSprite.clear();
61}
62
63bool MouseCursorController::getBounds(float* outMinX, float* outMinY, float* outMaxX,
64 float* outMaxY) const {
65 std::scoped_lock lock(mLock);
66
67 return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
68}
69
70bool MouseCursorController::getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX,
71 float* outMaxY) const REQUIRES(mLock) {
72 if (!mLocked.viewport.isValid()) {
73 return false;
74 }
75
76 *outMinX = mLocked.viewport.logicalLeft;
77 *outMinY = mLocked.viewport.logicalTop;
78 *outMaxX = mLocked.viewport.logicalRight - 1;
79 *outMaxY = mLocked.viewport.logicalBottom - 1;
80 return true;
81}
82
83void MouseCursorController::move(float deltaX, float deltaY) {
84#if DEBUG_MOUSE_CURSOR_UPDATES
85 ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
86#endif
87 if (deltaX == 0.0f && deltaY == 0.0f) {
88 return;
89 }
90
91 std::scoped_lock lock(mLock);
92
93 setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
94}
95
96void MouseCursorController::setButtonState(int32_t buttonState) {
97#if DEBUG_MOUSE_CURSOR_UPDATES
98 ALOGD("Set button state 0x%08x", buttonState);
99#endif
100 std::scoped_lock lock(mLock);
101
102 if (mLocked.buttonState != buttonState) {
103 mLocked.buttonState = buttonState;
104 }
105}
106
107int32_t MouseCursorController::getButtonState() const {
108 std::scoped_lock lock(mLock);
109 return mLocked.buttonState;
110}
111
112void MouseCursorController::setPosition(float x, float y) {
113#if DEBUG_MOUSE_CURSOR_UPDATES
114 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
115#endif
116 std::scoped_lock lock(mLock);
117 setPositionLocked(x, y);
118}
119
120void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
121 float minX, minY, maxX, maxY;
122 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
123 if (x <= minX) {
124 mLocked.pointerX = minX;
125 } else if (x >= maxX) {
126 mLocked.pointerX = maxX;
127 } else {
128 mLocked.pointerX = x;
129 }
130 if (y <= minY) {
131 mLocked.pointerY = minY;
132 } else if (y >= maxY) {
133 mLocked.pointerY = maxY;
134 } else {
135 mLocked.pointerY = y;
136 }
137 updatePointerLocked();
138 }
139}
140
141void MouseCursorController::getPosition(float* outX, float* outY) const {
142 std::scoped_lock lock(mLock);
143
144 *outX = mLocked.pointerX;
145 *outY = mLocked.pointerY;
146}
147
148int32_t MouseCursorController::getDisplayId() const {
149 std::scoped_lock lock(mLock);
150 return mLocked.viewport.displayId;
151}
152
153void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
154 std::scoped_lock lock(mLock);
155
156 // Remove the inactivity timeout, since we are fading now.
157 mContext.removeInactivityTimeout();
158
159 // Start fading.
160 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
161 mLocked.pointerFadeDirection = 0;
162 mLocked.pointerAlpha = 0.0f;
163 updatePointerLocked();
164 } else {
165 mLocked.pointerFadeDirection = -1;
Liam Harringtonce637132020-08-14 04:00:11 +0000166 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000167 }
168}
169
170void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
171 std::scoped_lock lock(mLock);
172
173 // Always reset the inactivity timer.
174 mContext.resetInactivityTimeout();
175
176 // Start unfading.
177 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
178 mLocked.pointerFadeDirection = 0;
179 mLocked.pointerAlpha = 1.0f;
180 updatePointerLocked();
181 } else {
182 mLocked.pointerFadeDirection = 1;
Liam Harringtonce637132020-08-14 04:00:11 +0000183 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000184 }
185}
186
187void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
188 std::scoped_lock lock(mLock);
189
190 loadResourcesLocked(getAdditionalMouseResources);
191 updatePointerLocked();
192}
193
194/**
195 * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
196 * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
197 */
198static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
199 width = viewport.deviceWidth;
200 height = viewport.deviceHeight;
201
Michael Wrightecde4d02022-11-25 00:20:19 +0000202 if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000203 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) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000246 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000247 temp = x;
248 x = oldViewport.deviceHeight - y;
249 y = temp;
250 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000251 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000252 x = oldViewport.deviceWidth - x;
253 y = oldViewport.deviceHeight - y;
254 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000255 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000256 temp = x;
257 x = y;
258 y = oldViewport.deviceWidth - temp;
259 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000260 case ui::ROTATION_0:
261 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000262 }
263
264 // Perform the new rotation.
265 switch (viewport.orientation) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000266 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000267 temp = x;
268 x = y;
269 y = viewport.deviceHeight - temp;
270 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000271 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000272 x = viewport.deviceWidth - x;
273 y = viewport.deviceHeight - y;
274 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000275 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000276 temp = x;
277 x = viewport.deviceWidth - y;
278 y = temp;
279 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000280 case ui::ROTATION_0:
281 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000282 }
283
284 // Apply offsets to convert from the pixel center to the pixel top-left corner position
285 // and save the results.
286 mLocked.pointerX = x - 0.5f;
287 mLocked.pointerY = y - 0.5f;
288 }
289
290 updatePointerLocked();
291}
292
Brandon Pollack015f5d92022-06-02 06:59:33 +0000293void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000294 std::scoped_lock lock(mLock);
295
296 if (mLocked.requestedPointerType != iconId) {
297 mLocked.requestedPointerType = iconId;
298 mLocked.updatePointerIcon = true;
299 updatePointerLocked();
300 }
301}
302
303void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
304 std::scoped_lock lock(mLock);
305
Brandon Pollack015f5d92022-06-02 06:59:33 +0000306 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
Liam Harringtonc782be62020-07-17 19:48:24 +0000307 mLocked.additionalMouseResources[iconId] = icon;
308 mLocked.requestedPointerType = iconId;
309 mLocked.updatePointerIcon = true;
310 updatePointerLocked();
311}
312
Liam Harringtonce637132020-08-14 04:00:11 +0000313bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000314 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
Liam Harringtonce637132020-08-14 04:00:11 +0000315 bool keepAnimating = false;
Liam Harringtonc782be62020-07-17 19:48:24 +0000316
317 // Animate pointer fade.
318 if (mLocked.pointerFadeDirection < 0) {
319 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
320 if (mLocked.pointerAlpha <= 0.0f) {
321 mLocked.pointerAlpha = 0.0f;
322 mLocked.pointerFadeDirection = 0;
323 } else {
324 keepAnimating = true;
325 }
326 updatePointerLocked();
327 } else if (mLocked.pointerFadeDirection > 0) {
328 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
329 if (mLocked.pointerAlpha >= 1.0f) {
330 mLocked.pointerAlpha = 1.0f;
331 mLocked.pointerFadeDirection = 0;
332 } else {
333 keepAnimating = true;
334 }
335 updatePointerLocked();
336 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000337 return keepAnimating;
338}
339
Liam Harringtonce637132020-08-14 04:00:11 +0000340bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000341 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000342 mLocked.animationResources.find(mLocked.requestedPointerType);
343 if (iter == mLocked.animationResources.end()) {
344 return false;
345 }
346
347 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
348 sp<SpriteController> spriteController = mContext.getSpriteController();
349 spriteController->openTransaction();
350
351 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
352 mLocked.animationFrameIndex += incr;
353 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
354 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
355 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
356 }
357 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
358
359 spriteController->closeTransaction();
360 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000361 // Keep animating.
362 return true;
363}
364
365void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
366 if (!mLocked.viewport.isValid()) {
367 return;
368 }
369 sp<SpriteController> spriteController = mContext.getSpriteController();
370 spriteController->openTransaction();
371
372 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
373 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
374 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
375
376 if (mLocked.pointerAlpha > 0) {
377 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
378 mLocked.pointerSprite->setVisible(true);
379 } else {
380 mLocked.pointerSprite->setVisible(false);
381 }
382
383 if (mLocked.updatePointerIcon) {
384 if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
385 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
386 } else {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000387 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000388 mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
389 if (iter != mLocked.additionalMouseResources.end()) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000390 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000391 mLocked.animationResources.find(mLocked.requestedPointerType);
392 if (anim_iter != mLocked.animationResources.end()) {
393 mLocked.animationFrameIndex = 0;
394 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
Liam Harringtonce637132020-08-14 04:00:11 +0000395 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000396 }
397 mLocked.pointerSprite->setIcon(iter->second);
398 } else {
399 ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
400 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
401 }
402 }
403 mLocked.updatePointerIcon = false;
404 }
405
406 spriteController->closeTransaction();
407}
408
409void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
410 if (!mLocked.viewport.isValid()) {
411 return;
412 }
413
414 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
415
416 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
417 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
418 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
419
420 mLocked.additionalMouseResources.clear();
421 mLocked.animationResources.clear();
422 if (getAdditionalMouseResources) {
423 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
424 &mLocked.animationResources,
425 mLocked.viewport.displayId);
426 }
427
428 mLocked.updatePointerIcon = true;
429}
430
431bool MouseCursorController::isViewportValid() {
432 std::scoped_lock lock(mLock);
433 return mLocked.viewport.isValid();
434}
435
436void MouseCursorController::getAdditionalMouseResources() {
437 std::scoped_lock lock(mLock);
438
439 if (mLocked.additionalMouseResources.empty()) {
440 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
441 &mLocked.animationResources,
442 mLocked.viewport.displayId);
443 }
444 mLocked.updatePointerIcon = true;
445 updatePointerLocked();
446}
447
448bool MouseCursorController::resourcesLoaded() {
449 std::scoped_lock lock(mLock);
450 return mLocked.resourcesLoaded;
451}
452
Liam Harringtonce637132020-08-14 04:00:11 +0000453bool MouseCursorController::doAnimations(nsecs_t timestamp) {
454 std::scoped_lock lock(mLock);
455 bool keepFading = doFadingAnimationLocked(timestamp);
456 bool keepBitmap = doBitmapAnimationLocked(timestamp);
457 bool keepAnimating = keepFading || keepBitmap;
458 if (!keepAnimating) {
459 /*
460 * We know that this callback will be removed before another
461 * is added. mLock in PointerAnimator will not be released
462 * until after this is removed, and adding another callback
463 * requires that lock. Thus it's safe to set mLocked.animating
464 * here.
465 */
466 mLocked.animating = false;
467 }
468 return keepAnimating;
469}
470
471void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
472 using namespace std::placeholders;
473
474 if (mLocked.animating) {
475 return;
476 }
477 mLocked.animating = true;
478
479 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
480 /*
481 * Using -1 for displayId here to avoid removing the callback
482 * if a TouchSpotController with the same display is removed.
483 */
484 mContext.addAnimationCallback(-1, func);
485}
486
Liam Harringtonc782be62020-07-17 19:48:24 +0000487} // namespace android