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
updateSprite(const SpriteIcon * icon,float x,float y,int32_t displayId)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
TouchSpotController(int32_t displayId,PointerControllerContext & context)64 TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
65 : mDisplayId(displayId), mContext(context) {
66 mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
67 }
68
~TouchSpotController()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
setSpots(const PointerCoords * spotCoords,const uint32_t * spotIdToIndex,BitSet32 spotIdBits)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
clearSpots()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
getSpot(uint32_t id,const std::vector<Spot * > & spots)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
createAndAddSpotLocked(uint32_t id,std::vector<Spot * > & spots)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
removeFirstFadingSpotLocked(std::vector<Spot * > & spots)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
releaseSpotLocked(Spot * spot)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
fadeOutAndReleaseSpotLocked(Spot * spot)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
fadeOutAndReleaseAllSpotsLocked()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
reloadSpotResources()208 void TouchSpotController::reloadSpotResources() {
209 mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
210 }
211
doAnimations(nsecs_t timestamp)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
doFadingAnimationLocked(nsecs_t timestamp)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
startAnimationLocked()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