PointerController.cpp revision 86ea1f5f521981d075aef56f11693e4f3bc32fdb
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 34namespace android { 35 36// --- PointerController --- 37 38// Time to wait before starting the fade when the pointer is inactive. 39static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds 40static const nsecs_t INACTIVITY_TIMEOUT_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds 41 42// Time to wait between animation frames. 43static const nsecs_t ANIMATION_FRAME_INTERVAL = 1000000000LL / 60; 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 52// --- PointerController --- 53 54PointerController::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.pointerIsFading = true; // keep the pointer initially faded 73 mLocked.pointerX = 0; 74 mLocked.pointerY = 0; 75 mLocked.pointerAlpha = 0.0f; 76 mLocked.pointerSprite = mSpriteController->createSprite(); 77 mLocked.pointerIconChanged = false; 78 79 mLocked.buttonState = 0; 80 81 loadResources(); 82} 83 84PointerController::~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 98bool 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 105bool 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 127void PointerController::move(float deltaX, float deltaY) { 128#if DEBUG_POINTER_UPDATES 129 LOGD("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 140void PointerController::setButtonState(uint32_t buttonState) { 141#if DEBUG_POINTER_UPDATES 142 LOGD("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 151uint32_t PointerController::getButtonState() const { 152 AutoMutex _l(mLock); 153 154 return mLocked.buttonState; 155} 156 157void PointerController::setPosition(float x, float y) { 158#if DEBUG_POINTER_UPDATES 159 LOGD("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 166void 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 187void PointerController::getPosition(float* outX, float* outY) const { 188 AutoMutex _l(mLock); 189 190 *outX = mLocked.pointerX; 191 *outY = mLocked.pointerY; 192} 193 194void PointerController::fade() { 195 AutoMutex _l(mLock); 196 197 sendImmediateInactivityTimeoutLocked(); 198} 199 200void PointerController::unfade() { 201 AutoMutex _l(mLock); 202 203 // Always reset the inactivity timer. 204 resetInactivityTimeoutLocked(); 205 206 // Unfade immediately if needed. 207 if (mLocked.pointerIsFading) { 208 mLocked.pointerIsFading = false; 209 mLocked.pointerAlpha = 1.0f; 210 updatePointerLocked(); 211 } 212} 213 214void PointerController::setPresentation(Presentation presentation) { 215 AutoMutex _l(mLock); 216 217 if (mLocked.presentation != presentation) { 218 mLocked.presentation = presentation; 219 mLocked.presentationChanged = true; 220 221 if (presentation != PRESENTATION_SPOT) { 222 fadeOutAndReleaseAllSpotsLocked(); 223 } 224 225 updatePointerLocked(); 226 } 227} 228 229void PointerController::setSpots(SpotGesture spotGesture, 230 const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { 231#if DEBUG_POINTER_UPDATES 232 LOGD("setSpots: spotGesture=%d", spotGesture); 233 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { 234 uint32_t id = idBits.firstMarkedBit(); 235 idBits.clearBit(id); 236 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 237 LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, 238 c.getAxisValue(AMOTION_EVENT_AXIS_X), 239 c.getAxisValue(AMOTION_EVENT_AXIS_Y), 240 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); 241 } 242#endif 243 244 AutoMutex _l(mLock); 245 246 mSpriteController->openTransaction(); 247 248 // Add or move spots for fingers that are down. 249 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { 250 uint32_t id = idBits.firstMarkedBit(); 251 idBits.clearBit(id); 252 253 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 254 const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 255 ? mResources.spotTouch : mResources.spotHover; 256 float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); 257 float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); 258 259 Spot* spot = getSpotLocked(id); 260 if (!spot) { 261 spot = createAndAddSpotLocked(id); 262 } 263 264 spot->updateSprite(&icon, x, y); 265 } 266 267 // Remove spots for fingers that went up. 268 for (size_t i = 0; i < mLocked.spots.size(); i++) { 269 Spot* spot = mLocked.spots.itemAt(i); 270 if (spot->id != Spot::INVALID_ID 271 && !spotIdBits.hasBit(spot->id)) { 272 fadeOutAndReleaseSpotLocked(spot); 273 } 274 } 275 276 mSpriteController->closeTransaction(); 277} 278 279void PointerController::clearSpots() { 280#if DEBUG_POINTER_UPDATES 281 LOGD("clearSpots"); 282#endif 283 284 AutoMutex _l(mLock); 285 286 fadeOutAndReleaseAllSpotsLocked(); 287} 288 289void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout) { 290 AutoMutex _l(mLock); 291 292 if (mLocked.inactivityTimeout != inactivityTimeout) { 293 mLocked.inactivityTimeout = inactivityTimeout; 294 resetInactivityTimeoutLocked(); 295 } 296} 297 298void PointerController::setDisplaySize(int32_t width, int32_t height) { 299 AutoMutex _l(mLock); 300 301 if (mLocked.displayWidth != width || mLocked.displayHeight != height) { 302 mLocked.displayWidth = width; 303 mLocked.displayHeight = height; 304 305 float minX, minY, maxX, maxY; 306 if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { 307 mLocked.pointerX = (minX + maxX) * 0.5f; 308 mLocked.pointerY = (minY + maxY) * 0.5f; 309 } else { 310 mLocked.pointerX = 0; 311 mLocked.pointerY = 0; 312 } 313 314 fadeOutAndReleaseAllSpotsLocked(); 315 updatePointerLocked(); 316 } 317} 318 319void PointerController::setDisplayOrientation(int32_t orientation) { 320 AutoMutex _l(mLock); 321 322 if (mLocked.displayOrientation != orientation) { 323 // Apply offsets to convert from the pixel top-left corner position to the pixel center. 324 // This creates an invariant frame of reference that we can easily rotate when 325 // taking into account that the pointer may be located at fractional pixel offsets. 326 float x = mLocked.pointerX + 0.5f; 327 float y = mLocked.pointerY + 0.5f; 328 float temp; 329 330 // Undo the previous rotation. 331 switch (mLocked.displayOrientation) { 332 case DISPLAY_ORIENTATION_90: 333 temp = x; 334 x = mLocked.displayWidth - y; 335 y = temp; 336 break; 337 case DISPLAY_ORIENTATION_180: 338 x = mLocked.displayWidth - x; 339 y = mLocked.displayHeight - y; 340 break; 341 case DISPLAY_ORIENTATION_270: 342 temp = x; 343 x = y; 344 y = mLocked.displayHeight - temp; 345 break; 346 } 347 348 // Perform the new rotation. 349 switch (orientation) { 350 case DISPLAY_ORIENTATION_90: 351 temp = x; 352 x = y; 353 y = mLocked.displayWidth - 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 = mLocked.displayHeight - y; 362 y = temp; 363 break; 364 } 365 366 // Apply offsets to convert from the pixel center to the pixel top-left corner position 367 // and save the results. 368 mLocked.pointerX = x - 0.5f; 369 mLocked.pointerY = y - 0.5f; 370 mLocked.displayOrientation = orientation; 371 372 updatePointerLocked(); 373 } 374} 375 376void PointerController::setPointerIcon(const SpriteIcon& icon) { 377 AutoMutex _l(mLock); 378 379 mLocked.pointerIcon = icon.copy(); 380 mLocked.pointerIconChanged = true; 381 382 updatePointerLocked(); 383} 384 385void PointerController::handleMessage(const Message& message) { 386 switch (message.what) { 387 case MSG_ANIMATE: 388 doAnimate(); 389 break; 390 case MSG_INACTIVITY_TIMEOUT: 391 doInactivityTimeout(); 392 break; 393 } 394} 395 396void PointerController::doAnimate() { 397 AutoMutex _l(mLock); 398 399 bool keepAnimating = false; 400 mLocked.animationPending = false; 401 nsecs_t frameDelay = systemTime(SYSTEM_TIME_MONOTONIC) - mLocked.animationTime; 402 403 // Animate pointer fade. 404 if (mLocked.pointerIsFading) { 405 mLocked.pointerAlpha -= float(frameDelay) / POINTER_FADE_DURATION; 406 if (mLocked.pointerAlpha <= 0) { 407 mLocked.pointerAlpha = 0; 408 } else { 409 keepAnimating = true; 410 } 411 updatePointerLocked(); 412 } 413 414 // Animate spots that are fading out and being removed. 415 for (size_t i = 0; i < mLocked.spots.size(); i++) { 416 Spot* spot = mLocked.spots.itemAt(i); 417 if (spot->id == Spot::INVALID_ID) { 418 spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; 419 if (spot->alpha <= 0) { 420 mLocked.spots.removeAt(i--); 421 releaseSpotLocked(spot); 422 } else { 423 spot->sprite->setAlpha(spot->alpha); 424 keepAnimating = true; 425 } 426 } 427 } 428 429 if (keepAnimating) { 430 startAnimationLocked(); 431 } 432} 433 434void PointerController::doInactivityTimeout() { 435 AutoMutex _l(mLock); 436 437 if (!mLocked.pointerIsFading) { 438 mLocked.pointerIsFading = true; 439 startAnimationLocked(); 440 } 441} 442 443void PointerController::startAnimationLocked() { 444 if (!mLocked.animationPending) { 445 mLocked.animationPending = true; 446 mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); 447 mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE)); 448 } 449} 450 451void PointerController::resetInactivityTimeoutLocked() { 452 mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); 453 454 nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT 455 ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; 456 mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); 457} 458 459void PointerController::sendImmediateInactivityTimeoutLocked() { 460 mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); 461 mLooper->sendMessage(mHandler, MSG_INACTIVITY_TIMEOUT); 462} 463 464void PointerController::updatePointerLocked() { 465 mSpriteController->openTransaction(); 466 467 mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); 468 mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); 469 470 if (mLocked.pointerAlpha > 0) { 471 mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); 472 mLocked.pointerSprite->setVisible(true); 473 } else { 474 mLocked.pointerSprite->setVisible(false); 475 } 476 477 if (mLocked.pointerIconChanged || mLocked.presentationChanged) { 478 mLocked.pointerSprite->setIcon(mLocked.presentation == PRESENTATION_POINTER 479 ? mLocked.pointerIcon : mResources.spotAnchor); 480 mLocked.pointerIconChanged = false; 481 mLocked.presentationChanged = false; 482 } 483 484 mSpriteController->closeTransaction(); 485} 486 487PointerController::Spot* PointerController::getSpotLocked(uint32_t id) { 488 for (size_t i = 0; i < mLocked.spots.size(); i++) { 489 Spot* spot = mLocked.spots.itemAt(i); 490 if (spot->id == id) { 491 return spot; 492 } 493 } 494 return NULL; 495} 496 497PointerController::Spot* PointerController::createAndAddSpotLocked(uint32_t id) { 498 // Remove spots until we have fewer than MAX_SPOTS remaining. 499 while (mLocked.spots.size() >= MAX_SPOTS) { 500 Spot* spot = removeFirstFadingSpotLocked(); 501 if (!spot) { 502 spot = mLocked.spots.itemAt(0); 503 mLocked.spots.removeAt(0); 504 } 505 releaseSpotLocked(spot); 506 } 507 508 // Obtain a sprite from the recycled pool. 509 sp<Sprite> sprite; 510 if (! mLocked.recycledSprites.isEmpty()) { 511 sprite = mLocked.recycledSprites.top(); 512 mLocked.recycledSprites.pop(); 513 } else { 514 sprite = mSpriteController->createSprite(); 515 } 516 517 // Return the new spot. 518 Spot* spot = new Spot(id, sprite); 519 mLocked.spots.push(spot); 520 return spot; 521} 522 523PointerController::Spot* PointerController::removeFirstFadingSpotLocked() { 524 for (size_t i = 0; i < mLocked.spots.size(); i++) { 525 Spot* spot = mLocked.spots.itemAt(i); 526 if (spot->id == Spot::INVALID_ID) { 527 mLocked.spots.removeAt(i); 528 return spot; 529 } 530 } 531 return NULL; 532} 533 534void PointerController::releaseSpotLocked(Spot* spot) { 535 spot->sprite->clearIcon(); 536 537 if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { 538 mLocked.recycledSprites.push(spot->sprite); 539 } 540 541 delete spot; 542} 543 544void PointerController::fadeOutAndReleaseSpotLocked(Spot* spot) { 545 if (spot->id != Spot::INVALID_ID) { 546 spot->id = Spot::INVALID_ID; 547 startAnimationLocked(); 548 } 549} 550 551void PointerController::fadeOutAndReleaseAllSpotsLocked() { 552 for (size_t i = 0; i < mLocked.spots.size(); i++) { 553 Spot* spot = mLocked.spots.itemAt(i); 554 fadeOutAndReleaseSpotLocked(spot); 555 } 556} 557 558void PointerController::loadResources() { 559 mPolicy->loadPointerResources(&mResources); 560} 561 562 563// --- PointerController::Spot --- 564 565void PointerController::Spot::updateSprite(const SpriteIcon* icon, float x, float y) { 566 sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); 567 sprite->setAlpha(alpha); 568 sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); 569 sprite->setPosition(x, y); 570 571 this->x = x; 572 this->y = y; 573 574 if (icon != lastIcon) { 575 lastIcon = icon; 576 if (icon) { 577 sprite->setIcon(*icon); 578 sprite->setVisible(true); 579 } else { 580 sprite->setVisible(false); 581 } 582 } 583} 584 585} // namespace android 586