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
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 23d7fdf..3b51be8 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -92,7 +92,7 @@
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
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 8600065..ffc0523 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -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 @@
} // 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index d6a51d2..ae5581d 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -16,12 +16,15 @@
#pragma once
+#include <array>
#include <list>
#include <memory>
#include <PointerControllerInterface.h>
#include <utils/Timers.h>
+#include "EventHub.h"
+#include "InputDevice.h"
#include "InputReaderContext.h"
#include "NotifyArgs.h"
#include "ui/Rotation.h"
@@ -34,7 +37,8 @@
// PointerController calls.
class GestureConverter {
public:
- GestureConverter(InputReaderContext& readerContext, int32_t deviceId);
+ GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
+ int32_t deviceId);
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
void reset();
@@ -46,6 +50,10 @@
NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
+ uint32_t fingerCount, float dx,
+ float dy);
+ [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime);
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
@@ -59,10 +67,26 @@
std::shared_ptr<PointerControllerInterface> mPointerController;
ui::Rotation mOrientation = ui::ROTATION_0;
+ RawAbsoluteAxisInfo mXAxisInfo;
+ RawAbsoluteAxisInfo mYAxisInfo;
+
// The current button state according to the gestures library, but converted into MotionEvent
// button values (AMOTION_EVENT_BUTTON_...).
uint32_t mButtonState = 0;
nsecs_t mDownTime = 0;
+
+ MotionClassification mCurrentClassification = MotionClassification::NONE;
+ uint32_t mSwipeFingerCount = 0;
+ static constexpr size_t MAX_FAKE_FINGERS = 4;
+ // We never need any PointerProperties other than the finger tool type, so we can just keep a
+ // const array of them.
+ const std::array<PointerProperties, MAX_FAKE_FINGERS> mFingerProps = {{
+ {.id = 0, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 1, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 2, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ {.id = 3, .toolType = AMOTION_EVENT_TOOL_TYPE_FINGER},
+ }};
+ std::array<PointerCoords, MAX_FAKE_FINGERS> mFakeFingerCoords = {};
};
} // namespace android