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

Commit c5748d19 authored by Harry Cutts's avatar Harry Cutts
Browse files

Report three- and four-finger swipes

The dispatcher still needs to be modified to only dispatch these to
SysUI windows.

Bug: 251196347
Test: check events received by a custom tester app, and touches shown by
      pointer location overlay
Test: atest inputflinger_tests
Change-Id: I3a7211d4a67e6388231bef158d3748c2e72e128d
parent edf6ce71
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -870,6 +870,14 @@ enum AMotionClassification : uint32_t {
     * The current event stream represents the user swiping with two fingers on a touchpad.
     */
    AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE = 3,
    /**
     * Classification constant: multi-finger swipe.
     *
     * The current event stream represents the user swiping with three or more fingers on a
     * touchpad. Unlike two-finger swipes, these are only to be handled by the system UI, which is
     * why they have a separate constant from two-finger swipes.
     */
    AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE = 4,
};

/**
+6 −0
Original line number Diff line number Diff line
@@ -302,6 +302,12 @@ enum class MotionClassification : uint8_t {
     * The current gesture represents the user swiping with two fingers on a touchpad.
     */
    TWO_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE,
    /**
     * The current gesture represents the user swiping with three or more fingers on a touchpad.
     * Unlike two-finger swipes, these are only to be handled by the system UI, which is why they
     * have a separate constant from two-finger swipes.
     */
    MULTI_FINGER_SWIPE = AMOTION_EVENT_CLASSIFICATION_MULTI_FINGER_SWIPE,
};

/**
+2 −0
Original line number Diff line number Diff line
@@ -72,6 +72,8 @@ const char* motionClassificationToString(MotionClassification classification) {
            return "DEEP_PRESS";
        case MotionClassification::TWO_FINGER_SWIPE:
            return "TWO_FINGER_SWIPE";
        case MotionClassification::MULTI_FINGER_SWIPE:
            return "MULTI_FINGER_SWIPE";
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext)
        mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
        mPointerController(getContext()->getPointerController(getDeviceId())),
        mStateConverter(deviceContext),
        mGestureConverter(*getContext(), getDeviceId()) {
        mGestureConverter(*getContext(), deviceContext, getDeviceId()) {
    mGestureInterpreter->Initialize(GESTURES_DEVCLASS_TOUCHPAD);
    mGestureInterpreter->SetHardwareProperties(createHardwareProperties(deviceContext));
    // Even though we don't explicitly delete copy/move semantics, it's safe to
+119 −23
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "gestures/GestureConverter.h"

#include <android/input.h>
#include <linux/input-event-codes.h>

#include "TouchCursorInputMapperCommon.h"
#include "input/Input.h"
@@ -44,10 +45,14 @@ uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) {

} // namespace

GestureConverter::GestureConverter(InputReaderContext& readerContext, int32_t deviceId)
GestureConverter::GestureConverter(InputReaderContext& readerContext,
                                   const InputDeviceContext& deviceContext, int32_t deviceId)
      : mDeviceId(deviceId),
        mReaderContext(readerContext),
        mPointerController(readerContext.getPointerController(deviceId)) {}
        mPointerController(readerContext.getPointerController(deviceId)) {
    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
    deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
}

void GestureConverter::reset() {
    mButtonState = 0;
@@ -60,6 +65,15 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read
            return {handleMove(when, readTime, gesture)};
        case kGestureTypeButtonsChange:
            return handleButtonsChange(when, readTime, gesture);
        case kGestureTypeSwipe:
            return handleMultiFingerSwipe(when, readTime, 3, gesture.details.swipe.dx,
                                          gesture.details.swipe.dy);
        case kGestureTypeFourFingerSwipe:
            return handleMultiFingerSwipe(when, readTime, 4, gesture.details.four_finger_swipe.dx,
                                          gesture.details.four_finger_swipe.dy);
        case kGestureTypeSwipeLift:
        case kGestureTypeFourFingerSwipeLift:
            return handleMultiFingerSwipeLift(when, readTime);
        default:
            // TODO(b/251196347): handle more gesture types.
            return {};
@@ -67,11 +81,6 @@ std::list<NotifyArgs> GestureConverter::handleGesture(nsecs_t when, nsecs_t read
}

NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture) {
    PointerProperties props;
    props.clear();
    props.id = 0;
    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;

    float deltaX = gesture.details.move.dx;
    float deltaY = gesture.details.move.dy;
    rotateDelta(mOrientation, &deltaX, &deltaY);
@@ -93,7 +102,8 @@ NotifyArgs GestureConverter::handleMove(nsecs_t when, nsecs_t readTime, const Ge

    const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
    return makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState,
                          /* pointerCount= */ 1, &props, &coords, xCursorPosition, yCursorPosition);
                          /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition,
                          yCursorPosition);
}

std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime,
@@ -103,11 +113,6 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_
    mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
    mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);

    PointerProperties props;
    props.clear();
    props.id = 0;
    props.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;

    float xCursorPosition, yCursorPosition;
    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);

@@ -131,15 +136,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_
            newButtonState |= actionButton;
            pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
                                                 actionButton, newButtonState,
                                                 /* pointerCount= */ 1, &props, &coords,
                                                 xCursorPosition, yCursorPosition));
                                                 /* pointerCount= */ 1, mFingerProps.data(),
                                                 &coords, xCursorPosition, yCursorPosition));
        }
    }
    if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
        mDownTime = when;
        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
                                     /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
                                     &props, &coords, xCursorPosition, yCursorPosition));
                                     mFingerProps.data(), &coords, xCursorPosition,
                                     yCursorPosition));
    }
    out.splice(out.end(), pressEvents);

@@ -155,19 +161,109 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_
            newButtonState &= ~actionButton;
            out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
                                         actionButton, newButtonState, /* pointerCount= */ 1,
                                         &props, &coords, xCursorPosition, yCursorPosition));
                                         mFingerProps.data(), &coords, xCursorPosition,
                                         yCursorPosition));
        }
    }
    if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
        coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
                                     newButtonState, /* pointerCount= */ 1, &props, &coords,
                                     xCursorPosition, yCursorPosition));
                                     newButtonState, /* pointerCount= */ 1, mFingerProps.data(),
                                     &coords, xCursorPosition, yCursorPosition));
    }
    mButtonState = newButtonState;
    return out;
}

[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when,
                                                                             nsecs_t readTime,
                                                                             uint32_t fingerCount,
                                                                             float dx, float dy) {
    std::list<NotifyArgs> out = {};
    float xCursorPosition, yCursorPosition;
    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
    if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
        // If the user changes the number of fingers mid-way through a swipe (e.g. they start with
        // three and then put a fourth finger down), the gesture library will treat it as two
        // separate swipes with an appropriate lift event between them, so we don't have to worry
        // about the finger count changing mid-swipe.
        mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
        mSwipeFingerCount = fingerCount;

        constexpr float FAKE_FINGER_SPACING = 100;
        float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
        for (size_t i = 0; i < mSwipeFingerCount; i++) {
            PointerCoords& coords = mFakeFingerCoords[i];
            coords.clear();
            coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
            coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
            coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
            xCoord += FAKE_FINGER_SPACING;
        }

        mDownTime = when;
        out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
                                     yCursorPosition));
        for (size_t i = 1; i < mSwipeFingerCount; i++) {
            out.push_back(makeMotionArgs(when, readTime,
                                         AMOTION_EVENT_ACTION_POINTER_DOWN |
                                                 (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                                         /* actionButton= */ 0, mButtonState,
                                         /* pointerCount= */ i + 1, mFingerProps.data(),
                                         mFakeFingerCoords.data(), xCursorPosition,
                                         yCursorPosition));
        }
    }
    for (size_t i = 0; i < mSwipeFingerCount; i++) {
        PointerCoords& coords = mFakeFingerCoords[i];
        coords.setAxisValue(AMOTION_EVENT_AXIS_X, coords.getAxisValue(AMOTION_EVENT_AXIS_X) + dx);
        // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate
        // the Y values.
        coords.setAxisValue(AMOTION_EVENT_AXIS_Y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y) - dy);
    }
    float xOffset = dx / (mXAxisInfo.maxValue - mXAxisInfo.minValue);
    // TODO(b/251196347): Set the gesture properties appropriately to avoid needing to negate the Y
    // values.
    float yOffset = -dy / (mYAxisInfo.maxValue - mYAxisInfo.minValue);
    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, xOffset);
    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
                                 mButtonState, /* pointerCount= */ mSwipeFingerCount,
                                 mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
                                 yCursorPosition));
    return out;
}

[[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipeLift(nsecs_t when,
                                                                                 nsecs_t readTime) {
    std::list<NotifyArgs> out = {};
    if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
        return out;
    }
    float xCursorPosition, yCursorPosition;
    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
    mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);

    for (size_t i = mSwipeFingerCount; i > 1; i--) {
        out.push_back(makeMotionArgs(when, readTime,
                                     AMOTION_EVENT_ACTION_POINTER_UP |
                                             ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
                                     /* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
                                     mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
                                     yCursorPosition));
    }
    out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
                                 /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
                                 mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
                                 yCursorPosition));
    mCurrentClassification = MotionClassification::NONE;
    mSwipeFingerCount = 0;
    return out;
}

NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
                                                  int32_t actionButton, int32_t buttonState,
                                                  uint32_t pointerCount,
@@ -181,10 +277,10 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime
                            mPointerController->getDisplayId(), /* policyFlags= */ 0, action,
                            /* actionButton= */ actionButton, /* flags= */ 0,
                            mReaderContext.getGlobalMetaState(), buttonState,
                            MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
                            pointerProperties, pointerCoords,
                            /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, xCursorPosition,
                            yCursorPosition, /* downTime= */ mDownTime, /* videoFrames= */ {});
                            mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount,
                            pointerProperties, pointerCoords, /* xPrecision= */ 1.0f,
                            /* yPrecision= */ 1.0f, xCursorPosition, yCursorPosition,
                            /* downTime= */ mDownTime, /* videoFrames= */ {});
}

} // namespace android
Loading