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