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