1 /* 2 * Copyright (C) 2010 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 "PointerController" 18 //#define LOG_NDEBUG 0 19 20 // Log debug messages about pointer updates 21 #define DEBUG_POINTER_UPDATES 0 22 23 #include "PointerController.h" 24 25 #include <log/log.h> 26 27 #include <SkBitmap.h> 28 #include <SkCanvas.h> 29 #include <SkColor.h> 30 #include <SkPaint.h> 31 #include <SkBlendMode.h> 32 33 namespace android { 34 35 // --- WeakLooperCallback --- 36 37 class WeakLooperCallback: public LooperCallback { 38 protected: 39 virtual ~WeakLooperCallback() { } 40 41 public: 42 WeakLooperCallback(const wp<LooperCallback>& callback) : 43 mCallback(callback) { 44 } 45 46 virtual int handleEvent(int fd, int events, void* data) { 47 sp<LooperCallback> callback = mCallback.promote(); 48 if (callback != NULL) { 49 return callback->handleEvent(fd, events, data); 50 } 51 return 0; // the client is gone, remove the callback 52 } 53 54 private: 55 wp<LooperCallback> mCallback; 56 }; 57 58 // --- PointerController --- 59 60 // Time to wait before starting the fade when the pointer is inactive. 61 static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds 62 static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds 63 64 // Time to spend fading out the spot completely. 65 static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms 66 67 // Time to spend fading out the pointer completely. 68 static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms 69 70 // The number of events to be read at once for DisplayEventReceiver. 71 static const int EVENT_BUFFER_SIZE = 100; 72 73 // --- PointerController --- 74 75 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy, 76 const sp<Looper>& looper, const sp<SpriteController>& spriteController) : 77 mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { 78 mHandler = new WeakMessageHandler(this); 79 mCallback = new WeakLooperCallback(this); 80 81 if (mDisplayEventReceiver.initCheck() == NO_ERROR) { 82 mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, 83 Looper::EVENT_INPUT, mCallback, nullptr); 84 } else { 85 ALOGE("Failed to initialize DisplayEventReceiver."); 86 } 87 88 AutoMutex _l(mLock); 89 90 mLocked.animationPending = false; 91 92 mLocked.displayWidth = -1; 93 mLocked.displayHeight = -1; 94 mLocked.displayOrientation = DISPLAY_ORIENTATION_0; 95 96 mLocked.presentation = PRESENTATION_POINTER; 97 mLocked.presentationChanged = false; 98 99 mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL; 100 101 mLocked.pointerFadeDirection = 0; 102 mLocked.pointerX = 0; 103 mLocked.pointerY = 0; 104 mLocked.pointerAlpha = 0.0f; // pointer is initially faded 105 mLocked.pointerSprite = mSpriteController->createSprite(); 106 mLocked.pointerIconChanged = false; 107 mLocked.requestedPointerType = mPolicy->getDefaultPointerIconId(); 108 109 mLocked.animationFrameIndex = 0; 110 mLocked.lastFrameUpdatedTime = 0; 111 112 mLocked.buttonState = 0; 113 114 mPolicy->loadPointerIcon(&mLocked.pointerIcon); 115 116 loadResources(); 117 118 if (mLocked.pointerIcon.isValid()) { 119 mLocked.pointerIconChanged = true; 120 updatePointerLocked(); 121 } 122 } 123 124 PointerController::~PointerController() { 125 mLooper->removeMessages(mHandler); 126 127 AutoMutex _l(mLock); 128 129 mLocked.pointerSprite.clear(); 130 131 for (size_t i = 0; i < mLocked.spots.size(); i++) { 132 delete mLocked.spots.itemAt(i); 133 } 134 mLocked.spots.clear(); 135 mLocked.recycledSprites.clear(); 136 } 137 138 bool PointerController::getBounds(float* outMinX, float* outMinY, 139 float* outMaxX, float* outMaxY) const { 140 AutoMutex _l(mLock); 141 142 return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY); 143 } 144 145 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY, 146 float* outMaxX, float* outMaxY) const { 147 if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) { 148 return false; 149 } 150 151 *outMinX = 0; 152 *outMinY = 0; 153 switch (mLocked.displayOrientation) { 154 case DISPLAY_ORIENTATION_90: 155 case DISPLAY_ORIENTATION_270: 156 *outMaxX = mLocked.displayHeight - 1; 157 *outMaxY = mLocked.displayWidth - 1; 158 break; 159 default: 160 *outMaxX = mLocked.displayWidth - 1; 161 *outMaxY = mLocked.displayHeight - 1; 162 break; 163 } 164 return true; 165 } 166 167 void PointerController::move(float deltaX, float deltaY) { 168 #if DEBUG_POINTER_UPDATES 169 ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); 170 #endif 171 if (deltaX == 0.0f && deltaY == 0.0f) { 172 return; 173 } 174 175 AutoMutex _l(mLock); 176 177 setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); 178 } 179 180 void PointerController::setButtonState(int32_t buttonState) { 181 #if DEBUG_POINTER_UPDATES 182 ALOGD("Set button state 0x%08x", buttonState); 183 #endif 184 AutoMutex _l(mLock); 185 186 if (mLocked.buttonState != buttonState) { 187 mLocked.buttonState = buttonState; 188 } 189 } 190 191 int32_t PointerController::getButtonState() const { 192 AutoMutex _l(mLock); 193 194 return mLocked.buttonState; 195 } 196 197 void PointerController::setPosition(float x, float y) { 198 #if DEBUG_POINTER_UPDATES 199 ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); 200 #endif 201 AutoMutex _l(mLock); 202 203 setPositionLocked(x, y); 204 } 205 206 void PointerController::setPositionLocked(float x, float y) { 207 float minX, minY, maxX, maxY; 208 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { 209 if (x <= minX) { 210 mLocked.pointerX = minX; 211 } else if (x >= maxX) { 212 mLocked.pointerX = maxX; 213 } else { 214 mLocked.pointerX = x; 215 } 216 if (y <= minY) { 217 mLocked.pointerY = minY; 218 } else if (y >= maxY) { 219 mLocked.pointerY = maxY; 220 } else { 221 mLocked.pointerY = y; 222 } 223 updatePointerLocked(); 224 } 225 } 226 227 void PointerController::getPosition(float* outX, float* outY) const { 228 AutoMutex _l(mLock); 229 230 *outX = mLocked.pointerX; 231 *outY = mLocked.pointerY; 232 } 233 234 void PointerController::fade(Transition transition) { 235 AutoMutex _l(mLock); 236 237 // Remove the inactivity timeout, since we are fading now. 238 removeInactivityTimeoutLocked(); 239 240 // Start fading. 241 if (transition == TRANSITION_IMMEDIATE) { 242 mLocked.pointerFadeDirection = 0; 243 mLocked.pointerAlpha = 0.0f; 244 updatePointerLocked(); 245 } else { 246 mLocked.pointerFadeDirection = -1; 247 startAnimationLocked(); 248 } 249 } 250 251 void PointerController::unfade(Transition transition) { 252 AutoMutex _l(mLock); 253 254 // Always reset the inactivity timer. 255 resetInactivityTimeoutLocked(); 256 257 // Start unfading. 258 if (transition == TRANSITION_IMMEDIATE) { 259 mLocked.pointerFadeDirection = 0; 260 mLocked.pointerAlpha = 1.0f; 261 updatePointerLocked(); 262 } else { 263 mLocked.pointerFadeDirection = 1; 264 startAnimationLocked(); 265 } 266 } 267 268 void PointerController::setPresentation(Presentation presentation) { 269 AutoMutex _l(mLock); 270 271 if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) { 272 mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, 273 &mLocked.animationResources); 274 } 275 276 if (mLocked.presentation != presentation) { 277 mLocked.presentation = presentation; 278 mLocked.presentationChanged = true; 279 280 if (presentation != PRESENTATION_SPOT) { 281 fadeOutAndReleaseAllSpotsLocked(); 282 } 283 284 updatePointerLocked(); 285 } 286 } 287 288 void PointerController::setSpots(const PointerCoords* spotCoords, 289 const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { 290 #if DEBUG_POINTER_UPDATES 291 ALOGD("setSpots: idBits=%08x", spotIdBits.value); 292 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { 293 uint32_t id = idBits.firstMarkedBit(); 294 idBits.clearBit(id); 295 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 296 ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, 297 c.getAxisValue(AMOTION_EVENT_AXIS_X), 298 c.getAxisValue(AMOTION_EVENT_AXIS_Y), 299 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); 300 } 301 #endif 302 303 AutoMutex _l(mLock); 304 305 mSpriteController->openTransaction(); 306 307 // Add or move spots for fingers that are down. 308 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { 309 uint32_t id = idBits.clearFirstMarkedBit(); 310 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 311 const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 312 ? mResources.spotTouch : mResources.spotHover; 313 float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); 314 float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); 315 316 Spot* spot = getSpotLocked(id); 317 if (!spot) { 318 spot = createAndAddSpotLocked(id); 319 } 320 321 spot->updateSprite(&icon, x, y); 322 } 323 324 // Remove spots for fingers that went up. 325 for (size_t i = 0; i < mLocked.spots.size(); i++) { 326 Spot* spot = mLocked.spots.itemAt(i); 327 if (spot->id != Spot::INVALID_ID 328 && !spotIdBits.hasBit(spot->id)) { 329 fadeOutAndReleaseSpotLocked(spot); 330 } 331 } 332 333 mSpriteController->closeTransaction(); 334 } 335 336 void PointerController::clearSpots() { 337 #if DEBUG_POINTER_UPDATES 338 ALOGD("clearSpots"); 339 #endif 340 341 AutoMutex _l(mLock); 342 343 fadeOutAndReleaseAllSpotsLocked(); 344 } 345 346 void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { 347 AutoMutex _l(mLock); 348 349 if (mLocked.inactivityTimeout != inactivityTimeout) { 350 mLocked.inactivityTimeout = inactivityTimeout; 351 resetInactivityTimeoutLocked(); 352 } 353 } 354 355 void PointerController::reloadPointerResources() { 356 AutoMutex _l(mLock); 357 358 loadResources(); 359 360 if (mLocked.presentation == PRESENTATION_POINTER) { 361 mLocked.additionalMouseResources.clear(); 362 mLocked.animationResources.clear(); 363 mPolicy->loadPointerIcon(&mLocked.pointerIcon); 364 mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, 365 &mLocked.animationResources); 366 } 367 368 mLocked.presentationChanged = true; 369 updatePointerLocked(); 370 } 371 372 void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) { 373 AutoMutex _l(mLock); 374 375 // Adjust to use the display's unrotated coordinate frame. 376 if (orientation == DISPLAY_ORIENTATION_90 377 || orientation == DISPLAY_ORIENTATION_270) { 378 int32_t temp = height; 379 height = width; 380 width = temp; 381 } 382 383 if (mLocked.displayWidth != width || mLocked.displayHeight != height) { 384 mLocked.displayWidth = width; 385 mLocked.displayHeight = height; 386 387 float minX, minY, maxX, maxY; 388 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { 389 mLocked.pointerX = (minX + maxX) * 0.5f; 390 mLocked.pointerY = (minY + maxY) * 0.5f; 391 } else { 392 mLocked.pointerX = 0; 393 mLocked.pointerY = 0; 394 } 395 396 fadeOutAndReleaseAllSpotsLocked(); 397 } 398 399 if (mLocked.displayOrientation != orientation) { 400 // Apply offsets to convert from the pixel top-left corner position to the pixel center. 401 // This creates an invariant frame of reference that we can easily rotate when 402 // taking into account that the pointer may be located at fractional pixel offsets. 403 float x = mLocked.pointerX + 0.5f; 404 float y = mLocked.pointerY + 0.5f; 405 float temp; 406 407 // Undo the previous rotation. 408 switch (mLocked.displayOrientation) { 409 case DISPLAY_ORIENTATION_90: 410 temp = x; 411 x = mLocked.displayWidth - y; 412 y = temp; 413 break; 414 case DISPLAY_ORIENTATION_180: 415 x = mLocked.displayWidth - x; 416 y = mLocked.displayHeight - y; 417 break; 418 case DISPLAY_ORIENTATION_270: 419 temp = x; 420 x = y; 421 y = mLocked.displayHeight - temp; 422 break; 423 } 424 425 // Perform the new rotation. 426 switch (orientation) { 427 case DISPLAY_ORIENTATION_90: 428 temp = x; 429 x = y; 430 y = mLocked.displayWidth - temp; 431 break; 432 case DISPLAY_ORIENTATION_180: 433 x = mLocked.displayWidth - x; 434 y = mLocked.displayHeight - y; 435 break; 436 case DISPLAY_ORIENTATION_270: 437 temp = x; 438 x = mLocked.displayHeight - y; 439 y = temp; 440 break; 441 } 442 443 // Apply offsets to convert from the pixel center to the pixel top-left corner position 444 // and save the results. 445 mLocked.pointerX = x - 0.5f; 446 mLocked.pointerY = y - 0.5f; 447 mLocked.displayOrientation = orientation; 448 } 449 450 updatePointerLocked(); 451 } 452 453 void PointerController::updatePointerIcon(int32_t iconId) { 454 AutoMutex _l(mLock); 455 if (mLocked.requestedPointerType != iconId) { 456 mLocked.requestedPointerType = iconId; 457 mLocked.presentationChanged = true; 458 updatePointerLocked(); 459 } 460 } 461 462 void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { 463 AutoMutex _l(mLock); 464 465 const int32_t iconId = mPolicy->getCustomPointerIconId(); 466 mLocked.additionalMouseResources[iconId] = icon; 467 mLocked.requestedPointerType = iconId; 468 mLocked.presentationChanged = true; 469 470 updatePointerLocked(); 471 } 472 473 void PointerController::handleMessage(const Message& message) { 474 switch (message.what) { 475 case MSG_INACTIVITY_TIMEOUT: 476 doInactivityTimeout(); 477 break; 478 } 479 } 480 481 int PointerController::handleEvent(int /* fd */, int events, void* /* data */) { 482 if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { 483 ALOGE("Display event receiver pipe was closed or an error occurred. " 484 "events=0x%x", events); 485 return 0; // remove the callback 486 } 487 488 if (!(events & Looper::EVENT_INPUT)) { 489 ALOGW("Received spurious callback for unhandled poll event. " 490 "events=0x%x", events); 491 return 1; // keep the callback 492 } 493 494 bool gotVsync = false; 495 ssize_t n; 496 nsecs_t timestamp; 497 DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; 498 while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) { 499 for (size_t i = 0; i < static_cast<size_t>(n); ++i) { 500 if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { 501 timestamp = buf[i].header.timestamp; 502 gotVsync = true; 503 } 504 } 505 } 506 if (gotVsync) { 507 doAnimate(timestamp); 508 } 509 return 1; // keep the callback 510 } 511 512 void PointerController::doAnimate(nsecs_t timestamp) { 513 AutoMutex _l(mLock); 514 515 mLocked.animationPending = false; 516 517 bool keepFading = doFadingAnimationLocked(timestamp); 518 bool keepBitmapFlipping = doBitmapAnimationLocked(timestamp); 519 if (keepFading || keepBitmapFlipping) { 520 startAnimationLocked(); 521 } 522 } 523 524 bool PointerController::doFadingAnimationLocked(nsecs_t timestamp) { 525 bool keepAnimating = false; 526 nsecs_t frameDelay = timestamp - mLocked.animationTime; 527 528 // Animate pointer fade. 529 if (mLocked.pointerFadeDirection < 0) { 530 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; 531 if (mLocked.pointerAlpha <= 0.0f) { 532 mLocked.pointerAlpha = 0.0f; 533 mLocked.pointerFadeDirection = 0; 534 } else { 535 keepAnimating = true; 536 } 537 updatePointerLocked(); 538 } else if (mLocked.pointerFadeDirection > 0) { 539 mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION; 540 if (mLocked.pointerAlpha >= 1.0f) { 541 mLocked.pointerAlpha = 1.0f; 542 mLocked.pointerFadeDirection = 0; 543 } else { 544 keepAnimating = true; 545 } 546 updatePointerLocked(); 547 } 548 549 // Animate spots that are fading out and being removed. 550 for (size_t i = 0; i < mLocked.spots.size();) { 551 Spot* spot = mLocked.spots.itemAt(i); 552 if (spot->id == Spot::INVALID_ID) { 553 spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; 554 if (spot->alpha <= 0) { 555 mLocked.spots.removeAt(i); 556 releaseSpotLocked(spot); 557 continue; 558 } else { 559 spot->sprite->setAlpha(spot->alpha); 560 keepAnimating = true; 561 } 562 } 563 ++i; 564 } 565 return keepAnimating; 566 } 567 568 bool PointerController::doBitmapAnimationLocked(nsecs_t timestamp) { 569 std::map<int32_t, PointerAnimation>::const_iterator iter = mLocked.animationResources.find( 570 mLocked.requestedPointerType); 571 if (iter == mLocked.animationResources.end()) { 572 return false; 573 } 574 575 if (timestamp - mLocked.lastFrameUpdatedTime > iter->second.durationPerFrame) { 576 mSpriteController->openTransaction(); 577 578 int incr = (timestamp - mLocked.lastFrameUpdatedTime) / iter->second.durationPerFrame; 579 mLocked.animationFrameIndex += incr; 580 mLocked.lastFrameUpdatedTime += iter->second.durationPerFrame * incr; 581 while (mLocked.animationFrameIndex >= iter->second.animationFrames.size()) { 582 mLocked.animationFrameIndex -= iter->second.animationFrames.size(); 583 } 584 mLocked.pointerSprite->setIcon(iter->second.animationFrames[mLocked.animationFrameIndex]); 585 586 mSpriteController->closeTransaction(); 587 } 588 589 // Keep animating. 590 return true; 591 } 592 593 void PointerController::doInactivityTimeout() { 594 fade(TRANSITION_GRADUAL); 595 } 596 597 void PointerController::startAnimationLocked() { 598 if (!mLocked.animationPending) { 599 mLocked.animationPending = true; 600 mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); 601 mDisplayEventReceiver.requestNextVsync(); 602 } 603 } 604 605 void PointerController::resetInactivityTimeoutLocked() { 606 mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); 607 608 nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT 609 ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; 610 mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); 611 } 612 613 void PointerController::removeInactivityTimeoutLocked() { 614 mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); 615 } 616 617 void PointerController::updatePointerLocked() { 618 mSpriteController->openTransaction(); 619 620 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); 621 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); 622 623 if (mLocked.pointerAlpha > 0) { 624 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); 625 mLocked.pointerSprite->setVisible(true); 626 } else { 627 mLocked.pointerSprite->setVisible(false); 628 } 629 630 if (mLocked.pointerIconChanged || mLocked.presentationChanged) { 631 if (mLocked.presentation == PRESENTATION_POINTER) { 632 if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) { 633 mLocked.pointerSprite->setIcon(mLocked.pointerIcon); 634 } else { 635 std::map<int32_t, SpriteIcon>::const_iterator iter = 636 mLocked.additionalMouseResources.find(mLocked.requestedPointerType); 637 if (iter != mLocked.additionalMouseResources.end()) { 638 std::map<int32_t, PointerAnimation>::const_iterator anim_iter = 639 mLocked.animationResources.find(mLocked.requestedPointerType); 640 if (anim_iter != mLocked.animationResources.end()) { 641 mLocked.animationFrameIndex = 0; 642 mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC); 643 startAnimationLocked(); 644 } 645 mLocked.pointerSprite->setIcon(iter->second); 646 } else { 647 ALOGW("Can't find the resource for icon id %d", mLocked.requestedPointerType); 648 mLocked.pointerSprite->setIcon(mLocked.pointerIcon); 649 } 650 } 651 } else { 652 mLocked.pointerSprite->setIcon(mResources.spotAnchor); 653 } 654 mLocked.pointerIconChanged = false; 655 mLocked.presentationChanged = false; 656 } 657 658 mSpriteController->closeTransaction(); 659 } 660 661 PointerController::Spot* PointerController::getSpotLocked(uint32_t id) { 662 for (size_t i = 0; i < mLocked.spots.size(); i++) { 663 Spot* spot = mLocked.spots.itemAt(i); 664 if (spot->id == id) { 665 return spot; 666 } 667 } 668 return NULL; 669 } 670 671 PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) { 672 // Remove spots until we have fewer than MAX_SPOTS remaining. 673 while (mLocked.spots.size() >= MAX_SPOTS) { 674 Spot* spot = removeFirstFadingSpotLocked(); 675 if (!spot) { 676 spot = mLocked.spots.itemAt(0); 677 mLocked.spots.removeAt(0); 678 } 679 releaseSpotLocked(spot); 680 } 681 682 // Obtain a sprite from the recycled pool. 683 sp<Sprite> sprite; 684 if (! mLocked.recycledSprites.isEmpty()) { 685 sprite = mLocked.recycledSprites.top(); 686 mLocked.recycledSprites.pop(); 687 } else { 688 sprite = mSpriteController->createSprite(); 689 } 690 691 // Return the new spot. 692 Spot* spot = new Spot(id, sprite); 693 mLocked.spots.push(spot); 694 return spot; 695 } 696 697 PointerController::Spot* PointerController::removeFirstFadingSpotLocked() { 698 for (size_t i = 0; i < mLocked.spots.size(); i++) { 699 Spot* spot = mLocked.spots.itemAt(i); 700 if (spot->id == Spot::INVALID_ID) { 701 mLocked.spots.removeAt(i); 702 return spot; 703 } 704 } 705 return NULL; 706 } 707 708 void PointerController::releaseSpotLocked(Spot* spot) { 709 spot->sprite->clearIcon(); 710 711 if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { 712 mLocked.recycledSprites.push(spot->sprite); 713 } 714 715 delete spot; 716 } 717 718 void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { 719 if (spot->id != Spot::INVALID_ID) { 720 spot->id = Spot::INVALID_ID; 721 startAnimationLocked(); 722 } 723 } 724 725 void PointerController::fadeOutAndReleaseAllSpotsLocked() { 726 for (size_t i = 0; i < mLocked.spots.size(); i++) { 727 Spot* spot = mLocked.spots.itemAt(i); 728 fadeOutAndReleaseSpotLocked(spot); 729 } 730 } 731 732 void PointerController::loadResources() { 733 mPolicy->loadPointerResources(&mResources); 734 } 735 736 737 // --- PointerController::Spot --- 738 739 void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) { 740 sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); 741 sprite->setAlpha(alpha); 742 sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); 743 sprite->setPosition(x, y); 744 745 this->x = x; 746 this->y = y; 747 748 if (icon != lastIcon) { 749 lastIcon = icon; 750 if (icon) { 751 sprite->setIcon(*icon); 752 sprite->setVisible(true); 753 } else { 754 sprite->setVisible(false); 755 } 756 } 757 } 758 759 } // namespace android 760