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