Loading services/inputflinger/reader/mapper/TouchInputMapper.cpp +400 −394 Original line number Diff line number Diff line Loading @@ -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) * Loading Loading @@ -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", Loading Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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. Loading @@ -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 Loading @@ -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]; Loading Loading @@ -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; } } Loading @@ -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; } } Loading Loading @@ -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] = Loading @@ -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 Loading @@ -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++) { Loading @@ -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, Loading @@ -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); Loading services/inputflinger/reader/mapper/TouchInputMapper.h +8 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading
services/inputflinger/reader/mapper/TouchInputMapper.cpp +400 −394 Original line number Diff line number Diff line Loading @@ -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) * Loading Loading @@ -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", Loading Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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. Loading @@ -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 Loading @@ -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]; Loading Loading @@ -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; } } Loading @@ -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; } } Loading Loading @@ -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] = Loading @@ -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 Loading @@ -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++) { Loading @@ -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, Loading @@ -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); Loading
services/inputflinger/reader/mapper/TouchInputMapper.h +8 −0 Original line number Diff line number Diff line Loading @@ -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); Loading