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