1 /*
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 <input/Input.h>
26 #include <log/log.h>
27
28 namespace {
29 // Time to spend fading out the pointer completely.
30 const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
31 } // namespace
32
33 namespace android {
34
35 // --- MouseCursorController ---
36
MouseCursorController(PointerControllerContext & context)37 MouseCursorController::MouseCursorController(PointerControllerContext& context)
38 : mContext(context) {
39 std::scoped_lock lock(mLock);
40
41 mLocked.stylusHoverMode = false;
42
43 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;
52 mLocked.requestedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
53 mLocked.resolvedPointerType = PointerIconStyle::TYPE_NOT_SPECIFIED;
54
55 mLocked.resourcesLoaded = false;
56 }
57
~MouseCursorController()58 MouseCursorController::~MouseCursorController() {
59 std::scoped_lock lock(mLock);
60
61 mLocked.pointerSprite.clear();
62 }
63
getBounds() const64 std::optional<FloatRect> MouseCursorController::getBounds() const {
65 std::scoped_lock lock(mLock);
66
67 return getBoundsLocked();
68 }
69
getBoundsLocked() const70 std::optional<FloatRect> MouseCursorController::getBoundsLocked() const REQUIRES(mLock) {
71 if (!mLocked.viewport.isValid()) {
72 return {};
73 }
74
75 return FloatRect{
76 static_cast<float>(mLocked.viewport.logicalLeft),
77 static_cast<float>(mLocked.viewport.logicalTop),
78 static_cast<float>(mLocked.viewport.logicalRight - 1),
79 static_cast<float>(mLocked.viewport.logicalBottom - 1),
80 };
81 }
82
move(float deltaX,float deltaY)83 void 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
setPosition(float x,float y)96 void MouseCursorController::setPosition(float x, float y) {
97 #if DEBUG_MOUSE_CURSOR_UPDATES
98 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
99 #endif
100 std::scoped_lock lock(mLock);
101 setPositionLocked(x, y);
102 }
103
setPositionLocked(float x,float y)104 void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) {
105 const auto bounds = getBoundsLocked();
106 if (!bounds) return;
107
108 mLocked.pointerX = std::max(bounds->left, std::min(bounds->right, x));
109 mLocked.pointerY = std::max(bounds->top, std::min(bounds->bottom, y));
110
111 updatePointerLocked();
112 }
113
getPosition() const114 FloatPoint MouseCursorController::getPosition() const {
115 std::scoped_lock lock(mLock);
116
117 return {mLocked.pointerX, mLocked.pointerY};
118 }
119
getDisplayId() const120 int32_t MouseCursorController::getDisplayId() const {
121 std::scoped_lock lock(mLock);
122 return mLocked.viewport.displayId;
123 }
124
fade(PointerControllerInterface::Transition transition)125 void MouseCursorController::fade(PointerControllerInterface::Transition transition) {
126 std::scoped_lock lock(mLock);
127
128 // Remove the inactivity timeout, since we are fading now.
129 mContext.removeInactivityTimeout();
130
131 // Start fading.
132 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
133 mLocked.pointerFadeDirection = 0;
134 mLocked.pointerAlpha = 0.0f;
135 updatePointerLocked();
136 } else {
137 mLocked.pointerFadeDirection = -1;
138 startAnimationLocked();
139 }
140 }
141
unfade(PointerControllerInterface::Transition transition)142 void MouseCursorController::unfade(PointerControllerInterface::Transition transition) {
143 std::scoped_lock lock(mLock);
144
145 // Always reset the inactivity timer.
146 mContext.resetInactivityTimeout();
147
148 // Start unfading.
149 if (transition == PointerControllerInterface::Transition::IMMEDIATE) {
150 mLocked.pointerFadeDirection = 0;
151 mLocked.pointerAlpha = 1.0f;
152 updatePointerLocked();
153 } else {
154 mLocked.pointerFadeDirection = 1;
155 startAnimationLocked();
156 }
157 }
158
setStylusHoverMode(bool stylusHoverMode)159 void MouseCursorController::setStylusHoverMode(bool stylusHoverMode) {
160 std::scoped_lock lock(mLock);
161
162 if (mLocked.stylusHoverMode != stylusHoverMode) {
163 mLocked.stylusHoverMode = stylusHoverMode;
164 mLocked.updatePointerIcon = true;
165 }
166 }
167
reloadPointerResources(bool getAdditionalMouseResources)168 void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
169 std::scoped_lock lock(mLock);
170
171 loadResourcesLocked(getAdditionalMouseResources);
172 updatePointerLocked();
173 }
174
175 /**
176 * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
177 * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
178 */
getNonRotatedSize(const DisplayViewport & viewport,int32_t & width,int32_t & height)179 static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
180 width = viewport.deviceWidth;
181 height = viewport.deviceHeight;
182
183 if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
184 std::swap(width, height);
185 }
186 }
187
setDisplayViewport(const DisplayViewport & viewport,bool getAdditionalMouseResources)188 void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport,
189 bool getAdditionalMouseResources) {
190 std::scoped_lock lock(mLock);
191
192 if (viewport == mLocked.viewport) {
193 return;
194 }
195
196 const DisplayViewport oldViewport = mLocked.viewport;
197 mLocked.viewport = viewport;
198
199 int32_t oldDisplayWidth, oldDisplayHeight;
200 getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
201 int32_t newDisplayWidth, newDisplayHeight;
202 getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
203
204 // Reset cursor position to center if size or display changed.
205 if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth ||
206 oldDisplayHeight != newDisplayHeight) {
207 if (const auto bounds = getBoundsLocked(); bounds) {
208 mLocked.pointerX = (bounds->left + bounds->right) * 0.5f;
209 mLocked.pointerY = (bounds->top + bounds->bottom) * 0.5f;
210 // Reload icon resources for density may be changed.
211 loadResourcesLocked(getAdditionalMouseResources);
212 } else {
213 mLocked.pointerX = 0;
214 mLocked.pointerY = 0;
215 }
216 } else if (oldViewport.orientation != viewport.orientation) {
217 // Apply offsets to convert from the pixel top-left corner position to the pixel center.
218 // This creates an invariant frame of reference that we can easily rotate when
219 // taking into account that the pointer may be located at fractional pixel offsets.
220 float x = mLocked.pointerX + 0.5f;
221 float y = mLocked.pointerY + 0.5f;
222 float temp;
223
224 // Undo the previous rotation.
225 switch (oldViewport.orientation) {
226 case ui::ROTATION_90:
227 temp = x;
228 x = oldViewport.deviceHeight - y;
229 y = temp;
230 break;
231 case ui::ROTATION_180:
232 x = oldViewport.deviceWidth - x;
233 y = oldViewport.deviceHeight - y;
234 break;
235 case ui::ROTATION_270:
236 temp = x;
237 x = y;
238 y = oldViewport.deviceWidth - temp;
239 break;
240 case ui::ROTATION_0:
241 break;
242 }
243
244 // Perform the new rotation.
245 switch (viewport.orientation) {
246 case ui::ROTATION_90:
247 temp = x;
248 x = y;
249 y = viewport.deviceHeight - temp;
250 break;
251 case ui::ROTATION_180:
252 x = viewport.deviceWidth - x;
253 y = viewport.deviceHeight - y;
254 break;
255 case ui::ROTATION_270:
256 temp = x;
257 x = viewport.deviceWidth - y;
258 y = temp;
259 break;
260 case ui::ROTATION_0:
261 break;
262 }
263
264 // Apply offsets to convert from the pixel center to the pixel top-left corner position
265 // and save the results.
266 mLocked.pointerX = x - 0.5f;
267 mLocked.pointerY = y - 0.5f;
268 }
269
270 updatePointerLocked();
271 }
272
updatePointerIcon(PointerIconStyle iconId)273 void MouseCursorController::updatePointerIcon(PointerIconStyle iconId) {
274 std::scoped_lock lock(mLock);
275
276 if (mLocked.requestedPointerType != iconId) {
277 mLocked.requestedPointerType = iconId;
278 mLocked.updatePointerIcon = true;
279 updatePointerLocked();
280 }
281 }
282
setCustomPointerIcon(const SpriteIcon & icon)283 void MouseCursorController::setCustomPointerIcon(const SpriteIcon& icon) {
284 std::scoped_lock lock(mLock);
285
286 const PointerIconStyle iconId = mContext.getPolicy()->getCustomPointerIconId();
287 mLocked.additionalMouseResources[iconId] = icon;
288 mLocked.requestedPointerType = iconId;
289 mLocked.updatePointerIcon = true;
290 updatePointerLocked();
291 }
292
doFadingAnimationLocked(nsecs_t timestamp)293 bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
294 nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
295 bool keepAnimating = false;
296
297 // Animate pointer fade.
298 if (mLocked.pointerFadeDirection < 0) {
299 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
300 if (mLocked.pointerAlpha <= 0.0f) {
301 mLocked.pointerAlpha = 0.0f;
302 mLocked.pointerFadeDirection = 0;
303 } else {
304 keepAnimating = true;
305 }
306 updatePointerLocked();
307 } else if (mLocked.pointerFadeDirection > 0) {
308 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
309 if (mLocked.pointerAlpha >= 1.0f) {
310 mLocked.pointerAlpha = 1.0f;
311 mLocked.pointerFadeDirection = 0;
312 } else {
313 keepAnimating = true;
314 }
315 updatePointerLocked();
316 }
317 return keepAnimating;
318 }
319
doBitmapAnimationLocked(nsecs_t timestamp)320 bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
321 std::map<PointerIconStyle, PointerAnimation>::const_iterator iter =
322 mLocked.animationResources.find(mLocked.resolvedPointerType);
323 if (iter == mLocked.animationResources.end()) {
324 return false;
325 }
326
327 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) {
328 sp<SpriteController> spriteController = mContext.getSpriteController();
329 spriteController->openTransaction();
330
331 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame;
332 mLocked.animationFrameIndex += incr;
333 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr;
334 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) {
335 mLocked.animationFrameIndex -= iter->second.animationFrames.size();
336 }
337 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]);
338
339 spriteController->closeTransaction();
340 }
341 // Keep animating.
342 return true;
343 }
344
updatePointerLocked()345 void MouseCursorController::updatePointerLocked() REQUIRES(mLock) {
346 if (!mLocked.viewport.isValid()) {
347 return;
348 }
349 sp<SpriteController> spriteController = mContext.getSpriteController();
350 spriteController->openTransaction();
351
352 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
353 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
354 mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
355
356 if (mLocked.pointerAlpha > 0) {
357 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
358 mLocked.pointerSprite->setVisible(true);
359 } else {
360 mLocked.pointerSprite->setVisible(false);
361 }
362
363 if (mLocked.updatePointerIcon) {
364 mLocked.resolvedPointerType = mLocked.requestedPointerType;
365 const PointerIconStyle defaultPointerIconId =
366 mContext.getPolicy()->getDefaultPointerIconId();
367 if (mLocked.resolvedPointerType == PointerIconStyle::TYPE_NOT_SPECIFIED) {
368 mLocked.resolvedPointerType = mLocked.stylusHoverMode
369 ? mContext.getPolicy()->getDefaultStylusIconId()
370 : defaultPointerIconId;
371 }
372
373 if (mLocked.resolvedPointerType == defaultPointerIconId) {
374 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
375 } else {
376 std::map<PointerIconStyle, SpriteIcon>::const_iterator iter =
377 mLocked.additionalMouseResources.find(mLocked.resolvedPointerType);
378 if (iter != mLocked.additionalMouseResources.end()) {
379 std::map<PointerIconStyle, PointerAnimation>::const_iterator anim_iter =
380 mLocked.animationResources.find(mLocked.resolvedPointerType);
381 if (anim_iter != mLocked.animationResources.end()) {
382 mLocked.animationFrameIndex = 0;
383 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
384 startAnimationLocked();
385 }
386 mLocked.pointerSprite->setIcon(iter->second);
387 } else {
388 ALOGW("Can't find the resource for icon id %d", mLocked.resolvedPointerType);
389 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
390 }
391 }
392 mLocked.updatePointerIcon = false;
393 }
394
395 spriteController->closeTransaction();
396 }
397
loadResourcesLocked(bool getAdditionalMouseResources)398 void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) {
399 if (!mLocked.viewport.isValid()) {
400 return;
401 }
402
403 if (!mLocked.resourcesLoaded) mLocked.resourcesLoaded = true;
404
405 sp<PointerControllerPolicyInterface> policy = mContext.getPolicy();
406 policy->loadPointerResources(&mResources, mLocked.viewport.displayId);
407 policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
408
409 mLocked.additionalMouseResources.clear();
410 mLocked.animationResources.clear();
411 if (getAdditionalMouseResources) {
412 policy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
413 &mLocked.animationResources,
414 mLocked.viewport.displayId);
415 }
416
417 mLocked.updatePointerIcon = true;
418 }
419
isViewportValid()420 bool MouseCursorController::isViewportValid() {
421 std::scoped_lock lock(mLock);
422 return mLocked.viewport.isValid();
423 }
424
getAdditionalMouseResources()425 void MouseCursorController::getAdditionalMouseResources() {
426 std::scoped_lock lock(mLock);
427
428 if (mLocked.additionalMouseResources.empty()) {
429 mContext.getPolicy()->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
430 &mLocked.animationResources,
431 mLocked.viewport.displayId);
432 }
433 mLocked.updatePointerIcon = true;
434 updatePointerLocked();
435 }
436
resourcesLoaded()437 bool MouseCursorController::resourcesLoaded() {
438 std::scoped_lock lock(mLock);
439 return mLocked.resourcesLoaded;
440 }
441
doAnimations(nsecs_t timestamp)442 bool MouseCursorController::doAnimations(nsecs_t timestamp) {
443 std::scoped_lock lock(mLock);
444 bool keepFading = doFadingAnimationLocked(timestamp);
445 bool keepBitmap = doBitmapAnimationLocked(timestamp);
446 bool keepAnimating = keepFading || keepBitmap;
447 if (!keepAnimating) {
448 /*
449 * We know that this callback will be removed before another
450 * is added. mLock in PointerAnimator will not be released
451 * until after this is removed, and adding another callback
452 * requires that lock. Thus it's safe to set mLocked.animating
453 * here.
454 */
455 mLocked.animating = false;
456 }
457 return keepAnimating;
458 }
459
startAnimationLocked()460 void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
461 using namespace std::placeholders;
462
463 if (mLocked.animating) {
464 return;
465 }
466 mLocked.animating = true;
467
468 std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
469 /*
470 * Using -1 for displayId here to avoid removing the callback
471 * if a TouchSpotController with the same display is removed.
472 */
473 mContext.addAnimationCallback(-1, func);
474 }
475
476 } // namespace android
477