Home | History | Annotate | Download | only in input
      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