blob: 0e7b7ff4319de553e6b614a37154233bd7038f57 [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
202 if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
203 viewport.orientation == DISPLAY_ORIENTATION_270) {
204 std::swap(width, height);
205 }
206}
207
208void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
209 bool getAdditionalMouseResources) {
210 std::scoped_lock lock(mLock);
211
212 if (viewport == mLocked.viewport) {
213 return;
214 }
215
216 const DisplayViewport oldViewport = mLocked.viewport;
217 mLocked.viewport = viewport;
218
219 int32_t oldDisplayWidth, oldDisplayHeight;
220 getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
221 int32_t newDisplayWidth, newDisplayHeight;
222 getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
223
224 // Reset cursor position to center if size or display changed.
225 if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
226 oldDisplayHeight != newDisplayHeight) {
227 float minX, minY, maxX, maxY;
228 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
229 mLocked.pointerX = (minX + maxX) * 0.5f;
230 mLocked.pointerY = (minY + maxY) * 0.5f;
231 // Reload icon resources for density may be changed.
232 loadResourcesLocked(getAdditionalMouseResources);
233 } else {
234 mLocked.pointerX = 0;
235 mLocked.pointerY = 0;
236 }
237 } else if (oldViewport.orientation != viewport.orientation) {
238 // Apply offsets to convert from the pixel top-left corner position to the pixel center.
239 // This creates an invariant frame of reference that we can easily rotate when
240 // taking into account that the pointer may be located at fractional pixel offsets.
241 float x = mLocked.pointerX + 0.5f;
242 float y = mLocked.pointerY + 0.5f;
243 float temp;
244
245 // Undo the previous rotation.
246 switch (oldViewport.orientation) {
247 case DISPLAY_ORIENTATION_90:
248 temp = x;
249 x = oldViewport.deviceHeight - y;
250 y = temp;
251 break;
252 case DISPLAY_ORIENTATION_180:
253 x = oldViewport.deviceWidth - x;
254 y = oldViewport.deviceHeight - y;
255 break;
256 case DISPLAY_ORIENTATION_270:
257 temp = x;
258 x = y;
259 y = oldViewport.deviceWidth - temp;
260 break;
261 }
262
263 // Perform the new rotation.
264 switch (viewport.orientation) {
265 case DISPLAY_ORIENTATION_90:
266 temp = x;
267 x = y;
268 y = viewport.deviceHeight - temp;
269 break;
270 case DISPLAY_ORIENTATION_180:
271 x = viewport.deviceWidth - x;
272 y = viewport.deviceHeight - y;
273 break;
274 case DISPLAY_ORIENTATION_270:
275 temp = x;
276 x = viewport.deviceWidth - y;
277 y = temp;
278 break;
279 }
280
281 // Apply offsets to convert from the pixel center to the pixel top-left corner position
282 // and save the results.
283 mLocked.pointerX = x - 0.5f;
284 mLocked.pointerY = y - 0.5f;
285 }
286
287 updatePointerLocked();
288}
289
Brandon Pollack015f5d92022-06-02 06:59:33 +0000290void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000291 std::scoped_lock lock(mLock);
292
293 if (mLocked.requestedPointerType != iconId) {
294 mLocked.requestedPointerType = iconId;
295 mLocked.updatePointerIcon = true;
296 updatePointerLocked();
297 }
298}
299
300void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
301 std::scoped_lock lock(mLock);
302
Brandon Pollack015f5d92022-06-02 06:59:33 +0000303 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
Liam Harringtonc782be62020-07-17 19:48:24 +0000304 mLocked.additionalMouseResources[iconId] = icon;
305 mLocked.requestedPointerType = iconId;
306 mLocked.updatePointerIcon = true;
307 updatePointerLocked();
308}
309
Liam Harringtonce637132020-08-14 04:00:11 +0000310bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000311 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
Liam Harringtonce637132020-08-14 04:00:11 +0000312 bool keepAnimating = false;
Liam Harringtonc782be62020-07-17 19:48:24 +0000313
314 // Animate pointer fade.
315 if (mLocked.pointerFadeDirection < 0) {
316 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
317 if (mLocked.pointerAlpha <= 0.0f) {
318 mLocked.pointerAlpha = 0.0f;
319 mLocked.pointerFadeDirection = 0;
320 } else {
321 keepAnimating = true;
322 }
323 updatePointerLocked();
324 } else if (mLocked.pointerFadeDirection > 0) {
325 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
326 if (mLocked.pointerAlpha >= 1.0f) {
327 mLocked.pointerAlpha = 1.0f;
328 mLocked.pointerFadeDirection = 0;
329 } else {
330 keepAnimating = true;
331 }
332 updatePointerLocked();
333 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000334 return keepAnimating;
335}
336
Liam Harringtonce637132020-08-14 04:00:11 +0000337bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000338 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000339 mLocked.animationResources.find(mLocked.requestedPointerType);
340 if (iter == mLocked.animationResources.end()) {
341 return false;
342 }
343
344 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
345 sp<SpriteController> spriteController = mContext.getSpriteController();
346 spriteController->openTransaction();
347
348 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
349 mLocked.animationFrameIndex += incr;
350 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
351 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
352 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
353 }
354 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
355
356 spriteController->closeTransaction();
357 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000358 // Keep animating.
359 return true;
360}
361
362void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
363 if (!mLocked.viewport.isValid()) {
364 return;
365 }
366 sp<SpriteController> spriteController = mContext.getSpriteController();
367 spriteController->openTransaction();
368
369 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
370 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
371 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
372
373 if (mLocked.pointerAlpha > 0) {
374 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
375 mLocked.pointerSprite->setVisible(true);
376 } else {
377 mLocked.pointerSprite->setVisible(false);
378 }
379
380 if (mLocked.updatePointerIcon) {
381 if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) {
382 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
383 } else {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000384 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000385 mLocked.additionalMouseResources.find(mLocked.requestedPointerType);
386 if (iter != mLocked.additionalMouseResources.end()) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000387 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
Liam Harringtonc782be62020-07-17 19:48:24 +0000388 mLocked.animationResources.find(mLocked.requestedPointerType);
389 if (anim_iter != mLocked.animationResources.end()) {
390 mLocked.animationFrameIndex = 0;
391 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
Liam Harringtonce637132020-08-14 04:00:11 +0000392 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000393 }
394 mLocked.pointerSprite->setIcon(iter->second);
395 } else {
396 ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType);
397 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
398 }
399 }
400 mLocked.updatePointerIcon = false;
401 }
402
403 spriteController->closeTransaction();
404}
405
406void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
407 if (!mLocked.viewport.isValid()) {
408 return;
409 }
410
411 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
412
413 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
414 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
415 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
416
417 mLocked.additionalMouseResources.clear();
418 mLocked.animationResources.clear();
419 if (getAdditionalMouseResources) {
420 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
421 &mLocked.animationResources,
422 mLocked.viewport.displayId);
423 }
424
425 mLocked.updatePointerIcon = true;
426}
427
428bool MouseCursorController::isViewportValid() {
429 std::scoped_lock lock(mLock);
430 return mLocked.viewport.isValid();
431}
432
433void MouseCursorController::getAdditionalMouseResources() {
434 std::scoped_lock lock(mLock);
435
436 if (mLocked.additionalMouseResources.empty()) {
437 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
438 &mLocked.animationResources,
439 mLocked.viewport.displayId);
440 }
441 mLocked.updatePointerIcon = true;
442 updatePointerLocked();
443}
444
445bool MouseCursorController::resourcesLoaded() {
446 std::scoped_lock lock(mLock);
447 return mLocked.resourcesLoaded;
448}
449
Liam Harringtonce637132020-08-14 04:00:11 +0000450bool MouseCursorController::doAnimations(nsecs_t timestamp) {
451 std::scoped_lock lock(mLock);
452 bool keepFading = doFadingAnimationLocked(timestamp);
453 bool keepBitmap = doBitmapAnimationLocked(timestamp);
454 bool keepAnimating = keepFading || keepBitmap;
455 if (!keepAnimating) {
456 /*
457 * We know that this callback will be removed before another
458 * is added. mLock in PointerAnimator will not be released
459 * until after this is removed, and adding another callback
460 * requires that lock. Thus it's safe to set mLocked.animating
461 * here.
462 */
463 mLocked.animating = false;
464 }
465 return keepAnimating;
466}
467
468void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
469 using namespace std::placeholders;
470
471 if (mLocked.animating) {
472 return;
473 }
474 mLocked.animating = true;
475
476 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
477 /*
478 * Using -1 for displayId here to avoid removing the callback
479 * if a TouchSpotController with the same display is removed.
480 */
481 mContext.addAnimationCallback(-1, func);
482}
483
Liam Harringtonc782be62020-07-17 19:48:24 +0000484} // namespace android