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