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