summaryrefslogtreecommitdiff
path: root/libs/input/TouchSpotController.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/input/TouchSpotController.cpp')
-rw-r--r--libs/input/TouchSpotController.cpp264
1 files changed, 264 insertions, 0 deletions
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
new file mode 100644
index 000000000000..f7c685ff8ba6
--- /dev/null
+++ b/libs/input/TouchSpotController.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TouchSpotController"
+
+// Log debug messages about pointer updates
+#define DEBUG_SPOT_UPDATES 0
+
+#include "TouchSpotController.h"
+
+#include <log/log.h>
+
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <SkPaint.h>
+
+namespace {
+// Time to spend fading out the spot completely.
+const nsecs_t SPOT_FADE_DURATION = 200 * 1000000LL; // 200 ms
+} // namespace
+
+namespace android {
+
+// --- Spot ---
+
+void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y,
+ int32_t displayId) {
+ sprite->setLayer(Sprite::BASE_LAYER_SPOT + id);
+ sprite->setAlpha(alpha);
+ sprite->setTransformationMatrix(SpriteTransformationMatrix(scale, 0.0f, 0.0f, scale));
+ sprite->setPosition(x, y);
+ sprite->setDisplayId(displayId);
+ this->x = x;
+ this->y = y;
+
+ if (icon != mLastIcon) {
+ mLastIcon = icon;
+ if (icon) {
+ sprite->setIcon(*icon);
+ sprite->setVisible(true);
+ } else {
+ sprite->setVisible(false);
+ }
+ }
+}
+
+// --- TouchSpotController ---
+
+TouchSpotController::TouchSpotController(int32_t displayId, PointerControllerContext& context)
+ : mDisplayId(displayId), mContext(context) {
+ mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+TouchSpotController::~TouchSpotController() {
+ std::scoped_lock lock(mLock);
+
+ size_t numSpots = mLocked.displaySpots.size();
+ for (size_t i = 0; i < numSpots; i++) {
+ delete mLocked.displaySpots[i];
+ }
+ mLocked.displaySpots.clear();
+}
+
+void TouchSpotController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+ BitSet32 spotIdBits) {
+#if DEBUG_SPOT_UPDATES
+ ALOGD("setSpots: idBits=%08x", spotIdBits.value);
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.firstMarkedBit();
+ idBits.clearBit(id);
+ const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+ ALOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f, displayId=%" PRId32 ".", id,
+ c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), displayId);
+ }
+#endif
+
+ std::scoped_lock lock(mLock);
+ sp<SpriteController> spriteController = mContext.getSpriteController();
+ spriteController->openTransaction();
+
+ // Add or move spots for fingers that are down.
+ for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ const PointerCoords& c = spotCoords[spotIdToIndex[id]];
+ const SpriteIcon& icon = c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE) > 0
+ ? mResources.spotTouch
+ : mResources.spotHover;
+ float x = c.getAxisValue(AMOTION_EVENT_AXIS_X);
+ float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+
+ Spot* spot = getSpot(id, mLocked.displaySpots);
+ if (!spot) {
+ spot = createAndAddSpotLocked(id, mLocked.displaySpots);
+ }
+
+ spot->updateSprite(&icon, x, y, mDisplayId);
+ }
+
+ for (Spot* spot : mLocked.displaySpots) {
+ if (spot->id != Spot::INVALID_ID && !spotIdBits.hasBit(spot->id)) {
+ fadeOutAndReleaseSpotLocked(spot);
+ }
+ }
+
+ spriteController->closeTransaction();
+}
+
+void TouchSpotController::clearSpots() {
+#if DEBUG_SPOT_UPDATES
+ ALOGD("clearSpots");
+#endif
+
+ std::scoped_lock lock(mLock);
+ fadeOutAndReleaseAllSpotsLocked();
+}
+
+TouchSpotController::Spot* TouchSpotController::getSpot(uint32_t id,
+ const std::vector<Spot*>& spots) {
+ for (size_t i = 0; i < spots.size(); i++) {
+ Spot* spot = spots[i];
+ if (spot->id == id) {
+ return spot;
+ }
+ }
+ return nullptr;
+}
+
+TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
+ std::vector<Spot*>& spots)
+ REQUIRES(mLock) {
+ // Remove spots until we have fewer than MAX_SPOTS remaining.
+ while (spots.size() >= MAX_SPOTS) {
+ Spot* spot = removeFirstFadingSpotLocked(spots);
+ if (!spot) {
+ spot = spots[0];
+ spots.erase(spots.begin());
+ }
+ releaseSpotLocked(spot);
+ }
+
+ // Obtain a sprite from the recycled pool.
+ sp<Sprite> sprite;
+ if (!mLocked.recycledSprites.empty()) {
+ sprite = mLocked.recycledSprites.back();
+ mLocked.recycledSprites.pop_back();
+ } else {
+ sprite = mContext.getSpriteController()->createSprite();
+ }
+
+ // Return the new spot.
+ Spot* spot = new Spot(id, sprite);
+ spots.push_back(spot);
+ return spot;
+}
+
+TouchSpotController::Spot* TouchSpotController::removeFirstFadingSpotLocked(
+ std::vector<Spot*>& spots) REQUIRES(mLock) {
+ for (size_t i = 0; i < spots.size(); i++) {
+ Spot* spot = spots[i];
+ if (spot->id == Spot::INVALID_ID) {
+ spots.erase(spots.begin() + i);
+ return spot;
+ }
+ }
+ return NULL;
+}
+
+void TouchSpotController::releaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+ spot->sprite->clearIcon();
+
+ if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
+ mLocked.recycledSprites.push_back(spot->sprite);
+ }
+ delete spot;
+}
+
+void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
+ if (spot->id != Spot::INVALID_ID) {
+ spot->id = Spot::INVALID_ID;
+ startAnimationLocked();
+ }
+}
+
+void TouchSpotController::fadeOutAndReleaseAllSpotsLocked() REQUIRES(mLock) {
+ size_t numSpots = mLocked.displaySpots.size();
+ for (size_t i = 0; i < numSpots; i++) {
+ Spot* spot = mLocked.displaySpots[i];
+ fadeOutAndReleaseSpotLocked(spot);
+ }
+}
+
+void TouchSpotController::reloadSpotResources() {
+ mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
+}
+
+bool TouchSpotController::doAnimations(nsecs_t timestamp) {
+ std::scoped_lock lock(mLock);
+ bool keepAnimating = doFadingAnimationLocked(timestamp);
+ if (!keepAnimating) {
+ /*
+ * We know that this callback will be removed before another
+ * is added. mLock in PointerAnimator will not be released
+ * until after this is removed, and adding another callback
+ * requires that lock. Thus it's safe to set mLocked.animating
+ * here.
+ */
+ mLocked.animating = false;
+ }
+ return keepAnimating;
+}
+
+bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
+ bool keepAnimating = false;
+ nsecs_t animationTime = mContext.getAnimationTime();
+ nsecs_t frameDelay = timestamp - animationTime;
+ size_t numSpots = mLocked.displaySpots.size();
+ for (size_t i = 0; i < numSpots;) {
+ Spot* spot = mLocked.displaySpots[i];
+ if (spot->id == Spot::INVALID_ID) {
+ spot->alpha -= float(frameDelay) / SPOT_FADE_DURATION;
+ if (spot->alpha <= 0) {
+ mLocked.displaySpots.erase(mLocked.displaySpots.begin() + i);
+ releaseSpotLocked(spot);
+ numSpots--;
+ continue;
+ } else {
+ spot->sprite->setAlpha(spot->alpha);
+ keepAnimating = true;
+ }
+ }
+ ++i;
+ }
+ return keepAnimating;
+}
+
+void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
+ using namespace std::placeholders;
+
+ if (mLocked.animating) {
+ return;
+ }
+ mLocked.animating = true;
+
+ std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1);
+ mContext.addAnimationCallback(mDisplayId, func);
+}
+
+} // namespace android