Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6eafa9dd authored by Harry Cutts's avatar Harry Cutts Committed by Android (Google) Code Review
Browse files

Merge "Split up TouchInputMapper::preparePointerGestures"

parents c3927860 bea6ce5a
Loading
Loading
Loading
Loading
+400 −394
Original line number Diff line number Diff line
@@ -2844,54 +2844,20 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
    // Otherwise choose an arbitrary remaining pointer.
    // This guarantees we always have an active touch id when there is at least one pointer.
    // We keep the same active touch id for as long as possible.
    int32_t lastActiveTouchId = mPointerGesture.activeTouchId;
    int32_t activeTouchId = lastActiveTouchId;
    if (activeTouchId < 0) {
    if (mPointerGesture.activeTouchId < 0) {
        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
            activeTouchId = mPointerGesture.activeTouchId =
                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
            mPointerGesture.activeTouchId = mCurrentCookedState.fingerIdBits.firstMarkedBit();
            mPointerGesture.firstTouchTime = when;
        }
    } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) {
        if (!mCurrentCookedState.fingerIdBits.isEmpty()) {
            activeTouchId = mPointerGesture.activeTouchId =
                    mCurrentCookedState.fingerIdBits.firstMarkedBit();
        } else {
            activeTouchId = mPointerGesture.activeTouchId = -1;
        }
    }

    // Determine whether we are in quiet time.
    bool isQuietTime = false;
    if (activeTouchId < 0) {
        mPointerGesture.resetQuietTime();
    } else {
        isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval;
        if (!isQuietTime) {
            if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
                 mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
                 mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
                currentFingerCount < 2) {
                // Enter quiet time when exiting swipe or freeform state.
                // This is to prevent accidentally entering the hover state and flinging the
                // pointer when finishing a swipe and there is still one pointer left onscreen.
                isQuietTime = true;
            } else if (mPointerGesture.lastGestureMode ==
                               PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
                       currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
                // Enter quiet time when releasing the button and there are still two or more
                // fingers down.  This may indicate that one finger was used to press the button
                // but it has not gone up yet.
                isQuietTime = true;
            }
            if (isQuietTime) {
                mPointerGesture.quietTime = when;
            }
        }
    } else if (!mCurrentCookedState.fingerIdBits.hasBit(mPointerGesture.activeTouchId)) {
        mPointerGesture.activeTouchId = !mCurrentCookedState.fingerIdBits.isEmpty()
                ? mCurrentCookedState.fingerIdBits.firstMarkedBit()
                : -1;
    }
    const int32_t& activeTouchId = mPointerGesture.activeTouchId;

    // Switch states based on button and pointer state.
    if (isQuietTime) {
    if (checkForTouchpadQuietTime(when)) {
        // Case 1: Quiet time. (QUIET)
        ALOGD_IF(DEBUG_GESTURES, "Gestures: QUIET for next %0.3fms",
                 (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
@@ -2931,24 +2897,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        // Switch pointers if needed.
        // Find the fastest pointer and follow it.
        if (activeTouchId >= 0 && currentFingerCount > 1) {
            int32_t bestId = -1;
            float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
            for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
                uint32_t id = idBits.clearFirstMarkedBit();
                std::optional<float> vx =
                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
                std::optional<float> vy =
                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
                if (vx && vy) {
                    float speed = hypotf(*vx, *vy);
                    if (speed > bestSpeed) {
                        bestId = id;
                        bestSpeed = speed;
                    }
                }
            }
            const auto [bestId, bestSpeed] = getFastestFinger();
            if (bestId >= 0 && bestId != activeTouchId) {
                mPointerGesture.activeTouchId = activeTouchId = bestId;
                mPointerGesture.activeTouchId = bestId;
                ALOGD_IF(DEBUG_GESTURES,
                         "Gestures: BUTTON_CLICK_OR_DRAG switched pointers, bestId=%d, "
                         "bestSpeed=%0.3f",
@@ -3114,42 +3065,137 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        }
    } else {
        // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
        // We need to provide feedback for each finger that goes down so we cannot wait
        // for the fingers to move before deciding what to do.
        prepareMultiFingerPointerGestures(when, outCancelPreviousGesture, outFinishPreviousGesture);
    }

    mPointerController->setButtonState(mCurrentRawState.buttonState);

    if (DEBUG_GESTURES) {
        ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
              "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
              "lastGestureMode=%d, lastGestureIdBits=0x%08x",
              toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
              mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
              mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
        for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
            uint32_t id = idBits.clearFirstMarkedBit();
            uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
            const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
            const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
            ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
        }
        for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
            uint32_t id = idBits.clearFirstMarkedBit();
            uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
            const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
            const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
            ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
        }
    }
    return true;
}

bool TouchInputMapper::checkForTouchpadQuietTime(nsecs_t when) {
    if (mPointerGesture.activeTouchId < 0) {
        mPointerGesture.resetQuietTime();
        return false;
    }

    if (when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval) {
        return true;
    }

    const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
    bool isQuietTime = false;
    if ((mPointerGesture.lastGestureMode == PointerGesture::Mode::PRESS ||
         mPointerGesture.lastGestureMode == PointerGesture::Mode::SWIPE ||
         mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) &&
        currentFingerCount < 2) {
        // Enter quiet time when exiting swipe or freeform state.
        // This is to prevent accidentally entering the hover state and flinging the
        // pointer when finishing a swipe and there is still one pointer left onscreen.
        isQuietTime = true;
    } else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::BUTTON_CLICK_OR_DRAG &&
               currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) {
        // Enter quiet time when releasing the button and there are still two or more
        // fingers down.  This may indicate that one finger was used to press the button
        // but it has not gone up yet.
        isQuietTime = true;
    }
    if (isQuietTime) {
        mPointerGesture.quietTime = when;
    }
    return isQuietTime;
}

std::pair<int32_t, float> TouchInputMapper::getFastestFinger() {
    int32_t bestId = -1;
    float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
    for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
        uint32_t id = idBits.clearFirstMarkedBit();
        std::optional<float> vx =
                mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
        std::optional<float> vy =
                mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
        if (vx && vy) {
            float speed = hypotf(*vx, *vy);
            if (speed > bestSpeed) {
                bestId = id;
                bestSpeed = speed;
            }
        }
    }
    return std::make_pair(bestId, bestSpeed);
}

void TouchInputMapper::prepareMultiFingerPointerGestures(nsecs_t when, bool* cancelPreviousGesture,
                                                         bool* finishPreviousGesture) {
    // We need to provide feedback for each finger that goes down so we cannot wait for the fingers
    // to move before deciding what to do.
    //
        // The ambiguous case is deciding what to do when there are two fingers down but they
        // have not moved enough to determine whether they are part of a drag or part of a
        // freeform gesture, or just a press or long-press at the pointer location.
    // The ambiguous case is deciding what to do when there are two fingers down but they have not
    // moved enough to determine whether they are part of a drag or part of a freeform gesture, or
    // just a press or long-press at the pointer location.
    //
        // When there are two fingers we start with the PRESS hypothesis and we generate a
        // down at the pointer location.
    // When there are two fingers we start with the PRESS hypothesis and we generate a down at the
    // pointer location.
    //
        // When the two fingers move enough or when additional fingers are added, we make
        // a decision to transition into SWIPE or FREEFORM mode accordingly.
    // When the two fingers move enough or when additional fingers are added, we make a decision to
    // transition into SWIPE or FREEFORM mode accordingly.
    const int32_t activeTouchId = mPointerGesture.activeTouchId;
    ALOG_ASSERT(activeTouchId >= 0);

        bool settled = when >=
                mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
    const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count();
    const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count();
    bool settled =
            when >= mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval;
    if (mPointerGesture.lastGestureMode != PointerGesture::Mode::PRESS &&
        mPointerGesture.lastGestureMode != PointerGesture::Mode::SWIPE &&
        mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
            *outFinishPreviousGesture = true;
        *finishPreviousGesture = true;
    } else if (!settled && currentFingerCount > lastFingerCount) {
        // Additional pointers have gone down but not yet settled.
        // Reset the gesture.
        ALOGD_IF(DEBUG_GESTURES,
                 "Gestures: Resetting gesture since additional pointers went down for "
                 "MULTITOUCH, settle time remaining %0.3fms",
                     (mPointerGesture.firstTouchTime +
                      mConfig.pointerGestureMultitouchSettleInterval - when) *
                             0.000001f);
            *outCancelPreviousGesture = true;
                 (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
                  when) * 0.000001f);
        *cancelPreviousGesture = true;
    } else {
        // Continue previous gesture.
        mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode;
    }

        if (*outFinishPreviousGesture || *outCancelPreviousGesture) {
    if (*finishPreviousGesture || *cancelPreviousGesture) {
        mPointerGesture.currentGestureMode = PointerGesture::Mode::PRESS;
        mPointerGesture.activeGestureId = 0;
        mPointerGesture.referenceIdBits.clear();
@@ -3159,9 +3205,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        ALOGD_IF(DEBUG_GESTURES,
                 "Gestures: Using centroid as reference for MULTITOUCH, settle time remaining "
                 "%0.3fms",
                     (mPointerGesture.firstTouchTime +
                      mConfig.pointerGestureMultitouchSettleInterval - when) *
                             0.000001f);
                 (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
                  when) * 0.000001f);
        mCurrentRawState.rawPointerData
                .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
                                               &mPointerGesture.referenceTouchY);
@@ -3222,7 +3267,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
                ALOGD_IF(DEBUG_GESTURES,
                         "Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
                         currentFingerCount);
                    *outCancelPreviousGesture = true;
                *cancelPreviousGesture = true;
                mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
            } else {
                // There are exactly two pointers.
@@ -3240,7 +3285,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
                    ALOGD_IF(DEBUG_GESTURES,
                             "Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
                             mutualDistance, mPointerGestureMaxSwipeWidth);
                        *outCancelPreviousGesture = true;
                    *cancelPreviousGesture = true;
                    mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                } else {
                    // There are two pointers.  Wait for both pointers to start moving
@@ -3252,7 +3297,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
                        // Calculate the dot product of the displacement vectors.
                        // When the vectors are oriented in approximately the same direction,
                        // the angle betweeen them is near zero and the cosine of the angle
                            // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) *
                        // approaches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) *
                        // mag(v2).
                        PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
                        PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
@@ -3281,7 +3326,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
                                     dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
                                     mConfig.pointerGestureMultitouchMinDistance, cosine,
                                     mConfig.pointerGestureSwipeTransitionAngleCosine);
                                *outCancelPreviousGesture = true;
                            *cancelPreviousGesture = true;
                            mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
                        }
                    }
@@ -3295,7 +3340,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
            ALOGD_IF(DEBUG_GESTURES,
                     "Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
                     currentFingerCount);
                *outCancelPreviousGesture = true;
            *cancelPreviousGesture = true;
            mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
        }
    }
@@ -3371,7 +3416,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        if (mPointerGesture.lastGestureMode != PointerGesture::Mode::FREEFORM) {
            // Initially, assign the active gesture id to the active touch point
            // if there is one.  No other touch id bits are mapped yet.
                if (!*outCancelPreviousGesture) {
            if (!*cancelPreviousGesture) {
                mappedTouchIdBits.markBit(activeTouchId);
                usedGestureIdBits.markBit(mPointerGesture.activeGestureId);
                mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] =
@@ -3382,8 +3427,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        } else {
            // Otherwise, assume we mapped all touches from the previous frame.
            // Reuse all mappings that are still applicable.
                mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value &
                        mCurrentCookedState.fingerIdBits.value;
            mappedTouchIdBits.value =
                    mLastCookedState.fingerIdBits.value & mCurrentCookedState.fingerIdBits.value;
            usedGestureIdBits = mPointerGesture.lastGestureIdBits;

            // Check whether we need to choose a new active gesture id because the
@@ -3401,10 +3446,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
        }

        ALOGD_IF(DEBUG_GESTURES,
                     "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, "
                     "usedGestureIdBits=0x%08x, activeGestureId=%d",
                     mappedTouchIdBits.value, usedGestureIdBits.value,
                     mPointerGesture.activeGestureId);
                 "Gestures: FREEFORM follow up mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
                 "activeGestureId=%d",
                 mappedTouchIdBits.value, usedGestureIdBits.value, mPointerGesture.activeGestureId);

        BitSet32 idBits(mCurrentCookedState.fingerIdBits);
        for (uint32_t i = 0; i < currentFingerCount; i++) {
@@ -3414,8 +3458,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi
                gestureId = usedGestureIdBits.markFirstUnmarkedBit();
                mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
                ALOGD_IF(DEBUG_GESTURES,
                             "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d",
                             touchId, gestureId);
                         "Gestures: FREEFORM new mapping for touch id %d -> gesture id %d", touchId,
                         gestureId);
            } else {
                gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
                ALOGD_IF(DEBUG_GESTURES,
@@ -3433,63 +3477,25 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi

            mPointerGesture.currentGestureProperties[i].clear();
            mPointerGesture.currentGestureProperties[i].id = gestureId;
                mPointerGesture.currentGestureProperties[i].toolType =
                        AMOTION_EVENT_TOOL_TYPE_FINGER;
            mPointerGesture.currentGestureProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
            mPointerGesture.currentGestureCoords[i].clear();
                mPointerGesture.currentGestureCoords[i]
                        .setAxisValue(AMOTION_EVENT_AXIS_X,
                                      mPointerGesture.referenceGestureX + deltaX);
                mPointerGesture.currentGestureCoords[i]
                        .setAxisValue(AMOTION_EVENT_AXIS_Y,
                                      mPointerGesture.referenceGestureY + deltaY);
                mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
                                                                     1.0f);
            mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X,
                                                                 mPointerGesture.referenceGestureX +
                                                                         deltaX);
            mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y,
                                                                 mPointerGesture.referenceGestureY +
                                                                         deltaY);
            mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
        }

        if (mPointerGesture.activeGestureId < 0) {
                mPointerGesture.activeGestureId =
                        mPointerGesture.currentGestureIdBits.firstMarkedBit();
            mPointerGesture.activeGestureId = mPointerGesture.currentGestureIdBits.firstMarkedBit();
            ALOGD_IF(DEBUG_GESTURES, "Gestures: FREEFORM new activeGestureId=%d",
                     mPointerGesture.activeGestureId);
        }
    }
}

    mPointerController->setButtonState(mCurrentRawState.buttonState);

    if (DEBUG_GESTURES) {
        ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
              "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
              "lastGestureMode=%d, lastGestureIdBits=0x%08x",
              toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
              mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
              mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
        for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
            uint32_t id = idBits.clearFirstMarkedBit();
            uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
            const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
            const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
            ALOGD("  currentGesture[%d]: index=%d, toolType=%d, "
                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
        }
        for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
            uint32_t id = idBits.clearFirstMarkedBit();
            uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
            const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
            const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
            ALOGD("  lastGesture[%d]: index=%d, toolType=%d, "
                  "x=%0.3f, y=%0.3f, pressure=%0.3f",
                  id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
                  coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
        }
    }
    return true;
}

void TouchInputMapper::moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId) {
    const RawPointerData::Pointer& currentPointer =
            mCurrentRawState.rawPointerData.pointerForId(pointerId);
+8 −0
Original line number Diff line number Diff line
@@ -773,6 +773,14 @@ private:
    bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
                                bool* outFinishPreviousGesture, bool isTimeout);

    // Returns true if we're in a period of "quiet time" when touchpad gestures should be ignored.
    bool checkForTouchpadQuietTime(nsecs_t when);

    std::pair<int32_t, float> getFastestFinger();

    void prepareMultiFingerPointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
                                           bool* outFinishPreviousGesture);

    // Moves the on-screen mouse pointer based on the movement of the pointer of the given ID
    // between the last and current events. Uses a relative motion.
    void moveMousePointerFromPointerDelta(nsecs_t when, uint32_t pointerId);