1 /* 2 * Copyright (C) 2020 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 "TouchSpotController" 18 19 // Log debug messages about pointer updates 20 #define DEBUG_SPOT_UPDATES 0 21 22 #include "TouchSpotController.h" 23 24 #include <log/log.h> 25 26 #include <SkBitmap.h> 27 #include <SkBlendMode.h> 28 #include <SkCanvas.h> 29 #include <SkColor.h> 30 #include <SkPaint.h> 31 32 namespace { 33 // Time to spend fading out the spot completely. 34 const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms 35 } // namespace 36 37 namespace android { 38 39 // --- Spot --- 40 41 void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, 42 int32_t displayId) { 43 sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); 44 sprite->setAlpha(alpha); 45 sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale)); 46 sprite->setPosition(x, y); 47 sprite->setDisplayId(displayId); 48 this->x = x; 49 this->y = y; 50 51 if (icon != mLastIcon) { 52 mLastIcon = icon; 53 if (icon) { 54 sprite->setIcon(*icon); 55 sprite->setVisible(true); 56 } else { 57 sprite->setVisible(false); 58 } 59 } 60 } 61 62 // --- TouchSpotController --- 63 64 TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context) 65 : mDisplayId(displayId), mContext(context) { 66 mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); 67 } 68 69 TouchSpotController::~TouchSpotController() { 70 std::scoped_lock lock(mLock); 71 72 size_t numSpots = mLocked.displaySpots.size(); 73 for (size_t i = 0; i < numSpots; i++) { 74 delete mLocked.displaySpots[i]; 75 } 76 mLocked.displaySpots.clear(); 77 } 78 79 void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, 80 BitSet32 spotIdBits) { 81 #if DEBUG_SPOT_UPDATES 82 ALOGD("setSpots: idBits=%08x", spotIdBits.value); 83 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { 84 uint32_t id = idBits.firstMarkedBit(); 85 idBits.clearBit(id); 86 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 87 ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id, 88 c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), 89 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId); 90 } 91 #endif 92 93 std::scoped_lock lock(mLock); 94 sp<SpriteController> spriteController = mContext.getSpriteController(); 95 spriteController->openTransaction(); 96 97 // Add or move spots for fingers that are down. 98 for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { 99 uint32_t id = idBits.clearFirstMarkedBit(); 100 const PointerCoords& c = spotCoords[spotIdToIndex[id]]; 101 const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0 102 ? mResources.spotTouch 103 : mResources.spotHover; 104 float x = c.getAxisValue(AMOTION_EVENT_AXIS_X); 105 float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y); 106 107 Spot* spot = getSpot(id, mLocked.displaySpots); 108 if (!spot) { 109 spot = createAndAddSpotLocked(id, mLocked.displaySpots); 110 } 111 112 spot->updateSprite(&icon, x, y, mDisplayId); 113 } 114 115 for (Spot* spot : mLocked.displaySpots) { 116 if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) { 117 fadeOutAndReleaseSpotLocked(spot); 118 } 119 } 120 121 spriteController->closeTransaction(); 122 } 123 124 void TouchSpotController::clearSpots() { 125 #if DEBUG_SPOT_UPDATES 126 ALOGD("clearSpots"); 127 #endif 128 129 std::scoped_lock lock(mLock); 130 fadeOutAndReleaseAllSpotsLocked(); 131 } 132 133 TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id, 134 const std::vector<Spot*>& spots) { 135 for (size_t i = 0; i < spots.size(); i++) { 136 Spot* spot = spots[i]; 137 if (spot->id == id) { 138 return spot; 139 } 140 } 141 return nullptr; 142 } 143 144 TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id, 145 std::vector<Spot*>& spots) 146 REQUIRES(mLock) { 147 // Remove spots until we have fewer than MAX_SPOTS remaining. 148 while (spots.size() >= MAX_SPOTS) { 149 Spot* spot = removeFirstFadingSpotLocked(spots); 150 if (!spot) { 151 spot = spots[0]; 152 spots.erase(spots.begin()); 153 } 154 releaseSpotLocked(spot); 155 } 156 157 // Obtain a sprite from the recycled pool. 158 sp<Sprite> sprite; 159 if (!mLocked.recycledSprites.empty()) { 160 sprite = mLocked.recycledSprites.back(); 161 mLocked.recycledSprites.pop_back(); 162 } else { 163 sprite = mContext.getSpriteController()->createSprite(); 164 } 165 166 // Return the new spot. 167 Spot* spot = new Spot(id, sprite); 168 spots.push_back(spot); 169 return spot; 170 } 171 172 TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked( 173 std::vector<Spot*>& spots) REQUIRES(mLock) { 174 for (size_t i = 0; i < spots.size(); i++) { 175 Spot* spot = spots[i]; 176 if (spot->id == Spot::INVALID_ID) { 177 spots.erase(spots.begin() + i); 178 return spot; 179 } 180 } 181 return NULL; 182 } 183 184 void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) { 185 spot->sprite->clearIcon(); 186 187 if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) { 188 mLocked.recycledSprites.push_back(spot->sprite); 189 } 190 delete spot; 191 } 192 193 void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) { 194 if (spot->id != Spot::INVALID_ID) { 195 spot->id = Spot::INVALID_ID; 196 startAnimationLocked(); 197 } 198 } 199 200 void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) { 201 size_t numSpots = mLocked.displaySpots.size(); 202 for (size_t i = 0; i < numSpots; i++) { 203 Spot* spot = mLocked.displaySpots[i]; 204 fadeOutAndReleaseSpotLocked(spot); 205 } 206 } 207 208 void TouchSpotController::reloadSpotResources() { 209 mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId); 210 } 211 212 bool TouchSpotController::doAnimations(nsecs_t timestamp) { 213 std::scoped_lock lock(mLock); 214 bool keepAnimating = doFadingAnimationLocked(timestamp); 215 if (!keepAnimating) { 216 /* 217 * We know that this callback will be removed before another 218 * is added. mLock in PointerAnimator will not be released 219 * until after this is removed, and adding another callback 220 * requires that lock. Thus it's safe to set mLocked.animating 221 * here. 222 */ 223 mLocked.animating = false; 224 } 225 return keepAnimating; 226 } 227 228 bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) { 229 bool keepAnimating = false; 230 nsecs_t animationTime = mContext.getAnimationTime(); 231 nsecs_t frameDelay = timestamp - animationTime; 232 size_t numSpots = mLocked.displaySpots.size(); 233 for (size_t i = 0; i < numSpots;) { 234 Spot* spot = mLocked.displaySpots[i]; 235 if (spot->id == Spot::INVALID_ID) { 236 spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION; 237 if (spot->alpha <= 0) { 238 mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i); 239 releaseSpotLocked(spot); 240 numSpots--; 241 continue; 242 } else { 243 spot->sprite->setAlpha(spot->alpha); 244 keepAnimating = true; 245 } 246 } 247 ++i; 248 } 249 return keepAnimating; 250 } 251 252 void TouchSpotController::startAnimationLocked() REQUIRES(mLock) { 253 using namespace std::placeholders; 254 255 if (mLocked.animating) { 256 return; 257 } 258 mLocked.animating = true; 259 260 std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1); 261 mContext.addAnimationCallback(mDisplayId, func); 262 } 263 264 } // namespace android 265