PointerController.cpp revision 6b83b76dd18e2a727936839b397e6ed2ed4f5a46
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 82 mLocked.buttonState = 0; 83 84 loadResources(); 85} 86 87PointerController::~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 101bool 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 108bool 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 130void 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 143void 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 154int32_t PointerController::getButtonState() const { 155 AutoMutex _l(mLock); 156 157 return mLocked.buttonState; 158} 159 160void 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 169void 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 190void PointerController::getPosition(float* outX, float* outY) const { 191 AutoMutex _l(mLock); 192 193 *outX = mLocked.pointerX; 194 *outY = mLocked.pointerY; 195} 196 197void 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 214void 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 231void 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 246void 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 294void PointerController::clearSpots() { 295#if DEBUG_POINTER_UPDATES 296 ALOGD("clearSpots"); 297#endif 298 299 AutoMutex _l(mLock); 300 301 fadeOutAndReleaseAllSpotsLocked(); 302} 303 304void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { 305 AutoMutex _l(mLock); 306 307 if (mLocked.inactivityTimeout != inactivityTimeout) { 308 mLocked.inactivityTimeout = inactivityTimeout; 309 resetInactivityTimeoutLocked(); 310 } 311} 312 313void 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 394void PointerController::setPointerIcon(const SpriteIcon& icon) { 395 AutoMutex _l(mLock); 396 397 mLocked.pointerIcon = icon.copy(); 398 mLocked.pointerIconChanged = true; 399 400 updatePointerLocked(); 401} 402 403void 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 414void 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 462void PointerController::doInactivityTimeout() { 463 fade(TRANSITION_GRADUAL); 464} 465 466void 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 474void 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 482void PointerController::removeInactivityTimeoutLocked() { 483 mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); 484} 485 486void 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 509PointerController::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 519PointerController::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 545PointerController::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 556void 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 566void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { 567 if (spot->id != Spot::INVALID_ID) { 568 spot->id = Spot::INVALID_ID; 569 startAnimationLocked(); 570 } 571} 572 573void 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 580void PointerController::loadResources() { 581 mPolicy->loadPointerResources(&mResources); 582} 583 584 585// --- PointerController::Spot --- 586 587void 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