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