blob: c3984055a27823183327ac02a122b5dd09d0fde3 [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
Prabir Pradhanb5dadec2023-02-28 17:43:09 +000066std::optional<FloatRect> MouseCursorController::getBounds() const {
Liam Harringtonc782be62020-07-17 19:48:24 +000067 std::scoped_lock lock(mLock);
68
Prabir Pradhanb5dadec2023-02-28 17:43:09 +000069 return getBoundsLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +000070}
71
Prabir Pradhanb5dadec2023-02-28 17:43:09 +000072std::optional<FloatRect> MouseCursorController::getBoundsLocked() const REQUIRES(mLock) {
Liam Harringtonc782be62020-07-17 19:48:24 +000073 if (!mLocked.viewport.isValid()) {
Prabir Pradhanb5dadec2023-02-28 17:43:09 +000074 return {};
Liam Harringtonc782be62020-07-17 19:48:24 +000075 }
76
Prabir Pradhanb5dadec2023-02-28 17:43:09 +000077 return FloatRect{
78 static_cast<float>(mLocked.viewport.logicalLeft),
79 static_cast<float>(mLocked.viewport.logicalTop),
80 static_cast<float>(mLocked.viewport.logicalRight - 1),
81 static_cast<float>(mLocked.viewport.logicalBottom - 1),
82 };
Liam Harringtonc782be62020-07-17 19:48:24 +000083}
84
85void MouseCursorController::move(float deltaX, float deltaY) {
86#if DEBUG_MOUSE_CURSOR_UPDATES
87 ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
88#endif
89 if (deltaX == 0.0f && deltaY == 0.0f) {
90 return;
91 }
92
93 std::scoped_lock lock(mLock);
94
95 setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
96}
97
98void MouseCursorController::setButtonState(int32_t buttonState) {
99#if DEBUG_MOUSE_CURSOR_UPDATES
100 ALOGD("Set button state 0x%08x", buttonState);
101#endif
102 std::scoped_lock lock(mLock);
103
104 if (mLocked.buttonState != buttonState) {
105 mLocked.buttonState = buttonState;
106 }
107}
108
109int32_t MouseCursorController::getButtonState() const {
110 std::scoped_lock lock(mLock);
111 return mLocked.buttonState;
112}
113
114void MouseCursorController::setPosition(float x, float y) {
115#if DEBUG_MOUSE_CURSOR_UPDATES
116 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
117#endif
118 std::scoped_lock lock(mLock);
119 setPositionLocked(x, y);
120}
121
122void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
Prabir Pradhanb5dadec2023-02-28 17:43:09 +0000123 const auto bounds = getBoundsLocked();
124 if (!bounds) return;
125
126 mLocked.pointerX = std::max(bounds->left, std::min(bounds->right, x));
127 mLocked.pointerY = std::max(bounds->top, std::min(bounds->bottom, y));
128
129 updatePointerLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000130}
131
Prabir Pradhanb5dadec2023-02-28 17:43:09 +0000132FloatPoint MouseCursorController::getPosition() const {
Liam Harringtonc782be62020-07-17 19:48:24 +0000133 std::scoped_lock lock(mLock);
134
Prabir Pradhanb5dadec2023-02-28 17:43:09 +0000135 return {mLocked.pointerX, mLocked.pointerY};
Liam Harringtonc782be62020-07-17 19:48:24 +0000136}
137
138int32_t MouseCursorController::getDisplayId() const {
139 std::scoped_lock lock(mLock);
140 return mLocked.viewport.displayId;
141}
142
143void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
144 std::scoped_lock lock(mLock);
145
146 // Remove the inactivity timeout, since we are fading now.
147 mContext.removeInactivityTimeout();
148
149 // Start fading.
150 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
151 mLocked.pointerFadeDirection = 0;
152 mLocked.pointerAlpha = 0.0f;
153 updatePointerLocked();
154 } else {
155 mLocked.pointerFadeDirection = -1;
Liam Harringtonce637132020-08-14 04:00:11 +0000156 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000157 }
158}
159
160void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
161 std::scoped_lock lock(mLock);
162
163 // Always reset the inactivity timer.
164 mContext.resetInactivityTimeout();
165
166 // Start unfading.
167 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
168 mLocked.pointerFadeDirection = 0;
169 mLocked.pointerAlpha = 1.0f;
170 updatePointerLocked();
171 } else {
172 mLocked.pointerFadeDirection = 1;
Liam Harringtonce637132020-08-14 04:00:11 +0000173 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000174 }
175}
176
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900177void MouseCursorController::setStylusHoverMode(bool stylusHoverMode) {
178 std::scoped_lock lock(mLock);
179
180 if (mLocked.stylusHoverMode != stylusHoverMode) {
181 mLocked.stylusHoverMode = stylusHoverMode;
182 mLocked.updatePointerIcon = true;
183 }
184}
185
Liam Harringtonc782be62020-07-17 19:48:24 +0000186void 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
Michael Wrightecde4d02022-11-25 00:20:19 +0000201 if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000202 std::swap(width, height);
203 }
204}
205
206void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
207 bool getAdditionalMouseResources) {
208 std::scoped_lock lock(mLock);
209
210 if (viewport == mLocked.viewport) {
211 return;
212 }
213
214 const DisplayViewport oldViewport = mLocked.viewport;
215 mLocked.viewport = viewport;
216
217 int32_t oldDisplayWidth, oldDisplayHeight;
218 getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
219 int32_t newDisplayWidth, newDisplayHeight;
220 getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
221
222 // Reset cursor position to center if size or display changed.
223 if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
224 oldDisplayHeight != newDisplayHeight) {
Prabir Pradhanb5dadec2023-02-28 17:43:09 +0000225 if (const auto bounds = getBoundsLocked(); bounds) {
226 mLocked.pointerX = (bounds->left + bounds->right) * 0.5f;
227 mLocked.pointerY = (bounds->top + bounds->bottom) * 0.5f;
Liam Harringtonc782be62020-07-17 19:48:24 +0000228 // Reload icon resources for density may be changed.
229 loadResourcesLocked(getAdditionalMouseResources);
230 } else {
231 mLocked.pointerX = 0;
232 mLocked.pointerY = 0;
233 }
234 } else if (oldViewport.orientation != viewport.orientation) {
235 // Apply offsets to convert from the pixel top-left corner position to the pixel center.
236 // This creates an invariant frame of reference that we can easily rotate when
237 // taking into account that the pointer may be located at fractional pixel offsets.
238 float x = mLocked.pointerX + 0.5f;
239 float y = mLocked.pointerY + 0.5f;
240 float temp;
241
242 // Undo the previous rotation.
243 switch (oldViewport.orientation) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000244 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000245 temp = x;
246 x = oldViewport.deviceHeight - y;
247 y = temp;
248 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000249 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000250 x = oldViewport.deviceWidth - x;
251 y = oldViewport.deviceHeight - y;
252 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000253 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000254 temp = x;
255 x = y;
256 y = oldViewport.deviceWidth - temp;
257 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000258 case ui::ROTATION_0:
259 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000260 }
261
262 // Perform the new rotation.
263 switch (viewport.orientation) {
Michael Wrightecde4d02022-11-25 00:20:19 +0000264 case ui::ROTATION_90:
Liam Harringtonc782be62020-07-17 19:48:24 +0000265 temp = x;
266 x = y;
267 y = viewport.deviceHeight - temp;
268 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000269 case ui::ROTATION_180:
Liam Harringtonc782be62020-07-17 19:48:24 +0000270 x = viewport.deviceWidth - x;
271 y = viewport.deviceHeight - y;
272 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000273 case ui::ROTATION_270:
Liam Harringtonc782be62020-07-17 19:48:24 +0000274 temp = x;
275 x = viewport.deviceWidth - y;
276 y = temp;
277 break;
Michael Wrightecde4d02022-11-25 00:20:19 +0000278 case ui::ROTATION_0:
279 break;
Liam Harringtonc782be62020-07-17 19:48:24 +0000280 }
281
282 // Apply offsets to convert from the pixel center to the pixel top-left corner position
283 // and save the results.
284 mLocked.pointerX = x - 0.5f;
285 mLocked.pointerY = y - 0.5f;
286 }
287
288 updatePointerLocked();
289}
290
Brandon Pollack015f5d92022-06-02 06:59:33 +0000291void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000292 std::scoped_lock lock(mLock);
293
294 if (mLocked.requestedPointerType != iconId) {
295 mLocked.requestedPointerType = iconId;
296 mLocked.updatePointerIcon = true;
297 updatePointerLocked();
298 }
299}
300
301void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
302 std::scoped_lock lock(mLock);
303
Brandon Pollack015f5d92022-06-02 06:59:33 +0000304 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
Liam Harringtonc782be62020-07-17 19:48:24 +0000305 mLocked.additionalMouseResources[iconId] = icon;
306 mLocked.requestedPointerType = iconId;
307 mLocked.updatePointerIcon = true;
308 updatePointerLocked();
309}
310
Liam Harringtonce637132020-08-14 04:00:11 +0000311bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000312 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
Liam Harringtonce637132020-08-14 04:00:11 +0000313 bool keepAnimating = false;
Liam Harringtonc782be62020-07-17 19:48:24 +0000314
315 // Animate pointer fade.
316 if (mLocked.pointerFadeDirection < 0) {
317 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
318 if (mLocked.pointerAlpha <= 0.0f) {
319 mLocked.pointerAlpha = 0.0f;
320 mLocked.pointerFadeDirection = 0;
321 } else {
322 keepAnimating = true;
323 }
324 updatePointerLocked();
325 } else if (mLocked.pointerFadeDirection > 0) {
326 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
327 if (mLocked.pointerAlpha >= 1.0f) {
328 mLocked.pointerAlpha = 1.0f;
329 mLocked.pointerFadeDirection = 0;
330 } else {
331 keepAnimating = true;
332 }
333 updatePointerLocked();
334 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000335 return keepAnimating;
336}
337
Liam Harringtonce637132020-08-14 04:00:11 +0000338bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000339 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900340 mLocked.animationResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000341 if (iter == mLocked.animationResources.end()) {
342 return false;
343 }
344
345 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
346 sp<SpriteController> spriteController = mContext.getSpriteController();
347 spriteController->openTransaction();
348
349 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
350 mLocked.animationFrameIndex += incr;
351 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
352 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
353 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
354 }
355 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
356
357 spriteController->closeTransaction();
358 }
Liam Harringtonc782be62020-07-17 19:48:24 +0000359 // Keep animating.
360 return true;
361}
362
363void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
364 if (!mLocked.viewport.isValid()) {
365 return;
366 }
367 sp<SpriteController> spriteController = mContext.getSpriteController();
368 spriteController->openTransaction();
369
370 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
371 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
372 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
373
374 if (mLocked.pointerAlpha > 0) {
375 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
376 mLocked.pointerSprite->setVisible(true);
377 } else {
378 mLocked.pointerSprite->setVisible(false);
379 }
380
381 if (mLocked.updatePointerIcon) {
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900382 mLocked.resolvedPointerType = mLocked.requestedPointerType;
383 const PointerIconStyle defaultPointerIconId =
384 mContext.getPolicy()->getDefaultPointerIconId();
385 if (mLocked.resolvedPointerType == PointerIconStyle::TYPE_NOT_SPECIFIED) {
386 mLocked.resolvedPointerType = mLocked.stylusHoverMode
387 ? mContext.getPolicy()->getDefaultStylusIconId()
388 : defaultPointerIconId;
389 }
390
391 if (mLocked.resolvedPointerType == defaultPointerIconId) {
Liam Harringtonc782be62020-07-17 19:48:24 +0000392 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
393 } else {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000394 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900395 mLocked.additionalMouseResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000396 if (iter != mLocked.additionalMouseResources.end()) {
Brandon Pollack015f5d92022-06-02 06:59:33 +0000397 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900398 mLocked.animationResources.find(mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000399 if (anim_iter != mLocked.animationResources.end()) {
400 mLocked.animationFrameIndex = 0;
401 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
Liam Harringtonce637132020-08-14 04:00:11 +0000402 startAnimationLocked();
Liam Harringtonc782be62020-07-17 19:48:24 +0000403 }
404 mLocked.pointerSprite->setIcon(iter->second);
405 } else {
Seunghwan Choi670b33d2023-01-13 21:12:59 +0900406 ALOGW("Can't find the resource for icon id %d", mLocked.resolvedPointerType);
Liam Harringtonc782be62020-07-17 19:48:24 +0000407 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
408 }
409 }
410 mLocked.updatePointerIcon = false;
411 }
412
413 spriteController->closeTransaction();
414}
415
416void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
417 if (!mLocked.viewport.isValid()) {
418 return;
419 }
420
421 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
422
423 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
424 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
425 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
426
427 mLocked.additionalMouseResources.clear();
428 mLocked.animationResources.clear();
429 if (getAdditionalMouseResources) {
430 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
431 &mLocked.animationResources,
432 mLocked.viewport.displayId);
433 }
434
435 mLocked.updatePointerIcon = true;
436}
437
438bool MouseCursorController::isViewportValid() {
439 std::scoped_lock lock(mLock);
440 return mLocked.viewport.isValid();
441}
442
443void MouseCursorController::getAdditionalMouseResources() {
444 std::scoped_lock lock(mLock);
445
446 if (mLocked.additionalMouseResources.empty()) {
447 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
448 &mLocked.animationResources,
449 mLocked.viewport.displayId);
450 }
451 mLocked.updatePointerIcon = true;
452 updatePointerLocked();
453}
454
455bool MouseCursorController::resourcesLoaded() {
456 std::scoped_lock lock(mLock);
457 return mLocked.resourcesLoaded;
458}
459
Liam Harringtonce637132020-08-14 04:00:11 +0000460bool MouseCursorController::doAnimations(nsecs_t timestamp) {
461 std::scoped_lock lock(mLock);
462 bool keepFading = doFadingAnimationLocked(timestamp);
463 bool keepBitmap = doBitmapAnimationLocked(timestamp);
464 bool keepAnimating = keepFading || keepBitmap;
465 if (!keepAnimating) {
466 /*
467 * We know that this callback will be removed before another
468 * is added. mLock in PointerAnimator will not be released
469 * until after this is removed, and adding another callback
470 * requires that lock. Thus it's safe to set mLocked.animating
471 * here.
472 */
473 mLocked.animating = false;
474 }
475 return keepAnimating;
476}
477
478void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
479 using namespace std::placeholders;
480
481 if (mLocked.animating) {
482 return;
483 }
484 mLocked.animating = true;
485
486 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
487 /*
488 * Using -1 for displayId here to avoid removing the callback
489 * if a TouchSpotController with the same display is removed.
490 */
491 mContext.addAnimationCallback(-1, func);
492}
493
Liam Harringtonc782be62020-07-17 19:48:24 +0000494} // namespace android