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 wait between animation frames.
     46 static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60;
     47 
     48 // Time to spend fading out the spot completely.
     49 static const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
     50 
     51 // Time to spend fading out the pointer completely.
     52 static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
     53 
     54 
     55 // --- PointerController ---
     56 
     57 PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
     58         const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
     59         mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
     60     mHandler = new WeakMessageHandler(this);
     61 
     62     AutoMutex _l(mLock);
     63 
     64     mLocked.animationPending = false;
     65 
     66     mLocked.displayWidth = -1;
     67     mLocked.displayHeight = -1;
     68     mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
     69 
     70     mLocked.presentation = PRESENTATION_POINTER;
     71     mLocked.presentationChanged = false;
     72 
     73     mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
     74 
     75     mLocked.pointerFadeDirection = 0;
     76     mLocked.pointerX = 0;
     77     mLocked.pointerY = 0;
     78     mLocked.pointerAlpha = 0.0f; // pointer is initially faded
     79     mLocked.pointerSprite = mSpriteController->createSprite();
     80     mLocked.pointerIconChanged = false;
     81 
     82     mLocked.buttonState = 0;
     83 
     84     loadResources();
     85 }
     86 
     87 PointerController::~PointerController() {
     88     mLooper->removeMessages(mHandler);
     89 
     90     AutoMutex _l(mLock);
     91 
     92     mLocked.pointerSprite.clear();
     93 
     94     for (size_t i = 0; i < mLocked.spots.size(); i++) {
     95         delete mLocked.spots.itemAt(i);
     96     }
     97     mLocked.spots.clear();
     98     mLocked.recycledSprites.clear();
     99 }
    100 
    101 bool PointerController::getBounds(float* outMinX, float* outMinY,
    102         float* outMaxX, float* outMaxY) const {
    103     AutoMutex _l(mLock);
    104 
    105     return getBoundsLocked(outMinX, outMinY, outMaxX, outMaxY);
    106 }
    107 
    108 bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
    109         float* outMaxX, float* outMaxY) const {
    110     if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
    111         return false;
    112     }
    113 
    114     *outMinX = 0;
    115     *outMinY = 0;
    116     switch (mLocked.displayOrientation) {
    117     case DISPLAY_ORIENTATION_90:
    118     case DISPLAY_ORIENTATION_270:
    119         *outMaxX = mLocked.displayHeight - 1;
    120         *outMaxY = mLocked.displayWidth - 1;
    121         break;
    122     default:
    123         *outMaxX = mLocked.displayWidth - 1;
    124         *outMaxY = mLocked.displayHeight - 1;
    125         break;
    126     }
    127     return true;
    128 }
    129 
    130 void PointerController::move(float deltaX, float deltaY) {
    131 #if DEBUG_POINTER_UPDATES
    132     ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY);
    133 #endif
    134     if (deltaX == 0.0f && deltaY == 0.0f) {
    135         return;
    136     }
    137 
    138     AutoMutex _l(mLock);
    139 
    140     setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY);
    141 }
    142 
    143 void PointerController::setButtonState(int32_t buttonState) {
    144 #if DEBUG_POINTER_UPDATES
    145     ALOGD("Set button state 0x%08x", buttonState);
    146 #endif
    147     AutoMutex _l(mLock);
    148 
    149     if (mLocked.buttonState != buttonState) {
    150         mLocked.buttonState = buttonState;
    151     }
    152 }
    153 
    154 int32_t PointerController::getButtonState() const {
    155     AutoMutex _l(mLock);
    156 
    157     return mLocked.buttonState;
    158 }
    159 
    160 void PointerController::setPosition(float x, float y) {
    161 #if DEBUG_POINTER_UPDATES
    162     ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y);
    163 #endif
    164     AutoMutex _l(mLock);
    165 
    166     setPositionLocked(x, y);
    167 }
    168 
    169 void PointerController::setPositionLocked(float x, float y) {
    170     float minX, minY, maxX, maxY;
    171     if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
    172         if (x <= minX) {
    173             mLocked.pointerX = minX;
    174         } else if (x >= maxX) {
    175             mLocked.pointerX = maxX;
    176         } else {
    177             mLocked.pointerX = x;
    178         }
    179         if (y <= minY) {
    180             mLocked.pointerY = minY;
    181         } else if (y >= maxY) {
    182             mLocked.pointerY = maxY;
    183         } else {
    184             mLocked.pointerY = y;
    185         }
    186         updatePointerLocked();
    187     }
    188 }
    189 
    190 void PointerController::getPosition(float* outX, float* outY) const {
    191     AutoMutex _l(mLock);
    192 
    193     *outX = mLocked.pointerX;
    194     *outY = mLocked.pointerY;
    195 }
    196 
    197 void PointerController::fade(Transition transition) {
    198     AutoMutex _l(mLock);
    199 
    200     // Remove the inactivity timeout, since we are fading now.
    201     removeInactivityTimeoutLocked();
    202 
    203     // Start fading.
    204     if (transition == TRANSITION_IMMEDIATE) {
    205         mLocked.pointerFadeDirection = 0;
    206         mLocked.pointerAlpha = 0.0f;
    207         updatePointerLocked();
    208     } else {
    209         mLocked.pointerFadeDirection = -1;
    210         startAnimationLocked();
    211     }
    212 }
    213 
    214 void PointerController::unfade(Transition transition) {
    215     AutoMutex _l(mLock);
    216 
    217     // Always reset the inactivity timer.
    218     resetInactivityTimeoutLocked();
    219 
    220     // Start unfading.
    221     if (transition == TRANSITION_IMMEDIATE) {
    222         mLocked.pointerFadeDirection = 0;
    223         mLocked.pointerAlpha = 1.0f;
    224         updatePointerLocked();
    225     } else {
    226         mLocked.pointerFadeDirection = 1;
    227         startAnimationLocked();
    228     }
    229 }
    230 
    231 void PointerController::setPresentation(Presentation presentation) {
    232     AutoMutex _l(mLock);
    233 
    234     if (mLocked.presentation != presentation) {
    235         mLocked.presentation = presentation;
    236         mLocked.presentationChanged = true;
    237 
    238         if (presentation != PRESENTATION_SPOT) {
    239             fadeOutAndReleaseAllSpotsLocked();
    240         }
    241 
    242         updatePointerLocked();
    243     }
    244 }
    245 
    246 void PointerController::setSpots(const PointerCoords* spotCoords,
    247         const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
    248 #if DEBUG_POINTER_UPDATES
    249     ALOGD("setSpots: idBits=%08x", spotIdBits.value);
    250     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
    251         uint32_t id = idBits.firstMarkedBit();
    252         idBits.clearBit(id);
    253         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
    254         ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
    255                 c.getAxisValue(AMOTION_EVENT_AXIS_X),
    256                 c.getAxisValue(AMOTION_EVENT_AXIS_Y),
    257                 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
    258     }
    259 #endif
    260 
    261     AutoMutex _l(mLock);
    262 
    263     mSpriteController->openTransaction();
    264 
    265     // Add or move spots for fingers that are down.
    266     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
    267         uint32_t id = idBits.clearFirstMarkedBit();
    268         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
    269         const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
    270                 ? mResources.spotTouch : mResources.spotHover;
    271         float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
    272         float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
    273 
    274         Spot* spot = getSpotLocked(id);
    275         if (!spot) {
    276             spot = createAndAddSpotLocked(id);
    277         }
    278 
    279         spot->updateSprite(&icon, x, y);
    280     }
    281 
    282     // Remove spots for fingers that went up.
    283     for (size_t i = 0; i < mLocked.spots.size(); i++) {
    284         Spot* spot = mLocked.spots.itemAt(i);
    285         if (spot->id != Spot::INVALID_ID
    286                 && !spotIdBits.hasBit(spot->id)) {
    287             fadeOutAndReleaseSpotLocked(spot);
    288         }
    289     }
    290 
    291     mSpriteController->closeTransaction();
    292 }
    293 
    294 void PointerController::clearSpots() {
    295 #if DEBUG_POINTER_UPDATES
    296     ALOGD("clearSpots");
    297 #endif
    298 
    299     AutoMutex _l(mLock);
    300 
    301     fadeOutAndReleaseAllSpotsLocked();
    302 }
    303 
    304 void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) {
    305     AutoMutex _l(mLock);
    306 
    307     if (mLocked.inactivityTimeout != inactivityTimeout) {
    308         mLocked.inactivityTimeout = inactivityTimeout;
    309         resetInactivityTimeoutLocked();
    310     }
    311 }
    312 
    313 void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
    314     AutoMutex _l(mLock);
    315 
    316     // Adjust to use the display's unrotated coordinate frame.
    317     if (orientation == DISPLAY_ORIENTATION_90
    318             || orientation == DISPLAY_ORIENTATION_270) {
    319         int32_t temp = height;
    320         height = width;
    321         width = temp;
    322     }
    323 
    324     if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
    325         mLocked.displayWidth = width;
    326         mLocked.displayHeight = height;
    327 
    328         float minX, minY, maxX, maxY;
    329         if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
    330             mLocked.pointerX = (minX + maxX) * 0.5f;
    331             mLocked.pointerY = (minY + maxY) * 0.5f;
    332         } else {
    333             mLocked.pointerX = 0;
    334             mLocked.pointerY = 0;
    335         }
    336 
    337         fadeOutAndReleaseAllSpotsLocked();
    338     }
    339 
    340     if (mLocked.displayOrientation != orientation) {
    341         // Apply offsets to convert from the pixel top-left corner position to the pixel center.
    342         // This creates an invariant frame of reference that we can easily rotate when
    343         // taking into account that the pointer may be located at fractional pixel offsets.
    344         float x = mLocked.pointerX + 0.5f;
    345         float y = mLocked.pointerY + 0.5f;
    346         float temp;
    347 
    348         // Undo the previous rotation.
    349         switch (mLocked.displayOrientation) {
    350         case DISPLAY_ORIENTATION_90:
    351             temp = x;
    352             x = mLocked.displayWidth - y;
    353             y = temp;
    354             break;
    355         case DISPLAY_ORIENTATION_180:
    356             x = mLocked.displayWidth - x;
    357             y = mLocked.displayHeight - y;
    358             break;
    359         case DISPLAY_ORIENTATION_270:
    360             temp = x;
    361             x = y;
    362             y = mLocked.displayHeight - temp;
    363             break;
    364         }
    365 
    366         // Perform the new rotation.
    367         switch (orientation) {
    368         case DISPLAY_ORIENTATION_90:
    369             temp = x;
    370             x = y;
    371             y = mLocked.displayWidth - temp;
    372             break;
    373         case DISPLAY_ORIENTATION_180:
    374             x = mLocked.displayWidth - x;
    375             y = mLocked.displayHeight - y;
    376             break;
    377         case DISPLAY_ORIENTATION_270:
    378             temp = x;
    379             x = mLocked.displayHeight - y;
    380             y = temp;
    381             break;
    382         }
    383 
    384         // Apply offsets to convert from the pixel center to the pixel top-left corner position
    385         // and save the results.
    386         mLocked.pointerX = x - 0.5f;
    387         mLocked.pointerY = y - 0.5f;
    388         mLocked.displayOrientation = orientation;
    389     }
    390 
    391     updatePointerLocked();
    392 }
    393 
    394 void PointerController::setPointerIcon(const SpriteIcon& icon) {
    395     AutoMutex _l(mLock);
    396 
    397     mLocked.pointerIcon = icon.copy();
    398     mLocked.pointerIconChanged = true;
    399 
    400     updatePointerLocked();
    401 }
    402 
    403 void PointerController::handleMessage(const Message& message) {
    404     switch (message.what) {
    405     case MSG_ANIMATE:
    406         doAnimate();
    407         break;
    408     case MSG_INACTIVITY_TIMEOUT:
    409         doInactivityTimeout();
    410         break;
    411     }
    412 }
    413 
    414 void PointerController::doAnimate() {
    415     AutoMutex _l(mLock);
    416 
    417     bool keepAnimating = false;
    418     mLocked.animationPending = false;
    419     nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime;
    420 
    421     // Animate pointer fade.
    422     if (mLocked.pointerFadeDirection < 0) {
    423         mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION;
    424         if (mLocked.pointerAlpha <= 0.0f) {
    425             mLocked.pointerAlpha = 0.0f;
    426             mLocked.pointerFadeDirection = 0;
    427         } else {
    428             keepAnimating = true;
    429         }
    430         updatePointerLocked();
    431     } else if (mLocked.pointerFadeDirection > 0) {
    432         mLocked.pointerAlpha += float(frameDelay) / POINTER_FADE_DURATION;
    433         if (mLocked.pointerAlpha >= 1.0f) {
    434             mLocked.pointerAlpha = 1.0f;
    435             mLocked.pointerFadeDirection = 0;
    436         } else {
    437             keepAnimating = true;
    438         }
    439         updatePointerLocked();
    440     }
    441 
    442     // Animate spots that are fading out and being removed.
    443     for (size_t i = 0; i < mLocked.spots.size(); i++) {
    444         Spot* spot = mLocked.spots.itemAt(i);
    445         if (spot->id == Spot::INVALID_ID) {
    446             spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
    447             if (spot->alpha <= 0) {
    448                 mLocked.spots.removeAt(i--);
    449                 releaseSpotLocked(spot);
    450             } else {
    451                 spot->sprite->setAlpha(spot->alpha);
    452                 keepAnimating = true;
    453             }
    454         }
    455     }
    456 
    457     if (keepAnimating) {
    458         startAnimationLocked();
    459     }
    460 }
    461 
    462 void PointerController::doInactivityTimeout() {
    463     fade(TRANSITION_GRADUAL);
    464 }
    465 
    466 void PointerController::startAnimationLocked() {
    467     if (!mLocked.animationPending) {
    468         mLocked.animationPending = true;
    469         mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
    470         mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE));
    471     }
    472 }
    473 
    474 void PointerController::resetInactivityTimeoutLocked() {
    475     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
    476 
    477     nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
    478             ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
    479     mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
    480 }
    481 
    482 void PointerController::removeInactivityTimeoutLocked() {
    483     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
    484 }
    485 
    486 void PointerController::updatePointerLocked() {
    487     mSpriteController->openTransaction();
    488 
    489     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
    490     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
    491 
    492     if (mLocked.pointerAlpha > 0) {
    493         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
    494         mLocked.pointerSprite->setVisible(true);
    495     } else {
    496         mLocked.pointerSprite->setVisible(false);
    497     }
    498 
    499     if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
    500         mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER
    501                 ? mLocked.pointerIcon : mResources.spotAnchor);
    502         mLocked.pointerIconChanged = false;
    503         mLocked.presentationChanged = false;
    504     }
    505 
    506     mSpriteController->closeTransaction();
    507 }
    508 
    509 PointerController::Spot* PointerController::getSpotLocked(uint32_t id) {
    510     for (size_t i = 0; i < mLocked.spots.size(); i++) {
    511         Spot* spot = mLocked.spots.itemAt(i);
    512         if (spot->id == id) {
    513             return spot;
    514         }
    515     }
    516     return NULL;
    517 }
    518 
    519 PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) {
    520     // Remove spots until we have fewer than MAX_SPOTS remaining.
    521     while (mLocked.spots.size() >= MAX_SPOTS) {
    522         Spot* spot = removeFirstFadingSpotLocked();
    523         if (!spot) {
    524             spot = mLocked.spots.itemAt(0);
    525             mLocked.spots.removeAt(0);
    526         }
    527         releaseSpotLocked(spot);
    528     }
    529 
    530     // Obtain a sprite from the recycled pool.
    531     sp<Sprite> sprite;
    532     if (! mLocked.recycledSprites.isEmpty()) {
    533         sprite = mLocked.recycledSprites.top();
    534         mLocked.recycledSprites.pop();
    535     } else {
    536         sprite = mSpriteController->createSprite();
    537     }
    538 
    539     // Return the new spot.
    540     Spot* spot = new Spot(id, sprite);
    541     mLocked.spots.push(spot);
    542     return spot;
    543 }
    544 
    545 PointerController::Spot* PointerController::removeFirstFadingSpotLocked() {
    546     for (size_t i = 0; i < mLocked.spots.size(); i++) {
    547         Spot* spot = mLocked.spots.itemAt(i);
    548         if (spot->id == Spot::INVALID_ID) {
    549             mLocked.spots.removeAt(i);
    550             return spot;
    551         }
    552     }
    553     return NULL;
    554 }
    555 
    556 void PointerController::releaseSpotLocked(Spot* spot) {
    557     spot->sprite->clearIcon();
    558 
    559     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
    560         mLocked.recycledSprites.push(spot->sprite);
    561     }
    562 
    563     delete spot;
    564 }
    565 
    566 void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) {
    567     if (spot->id != Spot::INVALID_ID) {
    568         spot->id = Spot::INVALID_ID;
    569         startAnimationLocked();
    570     }
    571 }
    572 
    573 void PointerController::fadeOutAndReleaseAllSpotsLocked() {
    574     for (size_t i = 0; i < mLocked.spots.size(); i++) {
    575         Spot* spot = mLocked.spots.itemAt(i);
    576         fadeOutAndReleaseSpotLocked(spot);
    577     }
    578 }
    579 
    580 void PointerController::loadResources() {
    581     mPolicy->loadPointerResources(&mResources);
    582 }
    583 
    584 
    585 // --- PointerController::Spot ---
    586 
    587 void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) {
    588     sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
    589     sprite->setAlpha(alpha);
    590     sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
    591     sprite->setPosition(x, y);
    592 
    593     this->x = x;
    594     this->y = y;
    595 
    596     if (icon != lastIcon) {
    597         lastIcon = icon;
    598         if (icon) {
    599             sprite->setIcon(*icon);
    600             sprite->setVisible(true);
    601         } else {
    602             sprite->setVisible(false);
    603         }
    604     }
    605 }
    606 
    607 } // namespace android
    608