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