Merge changes from topic "ToupadPointerCapture"
* changes:
Add relative field to captured cursor device
Add relative coordinates for touchpad events.
Support "pointer" capture for touchpads
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index d1f20c7..9118805 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -82,6 +82,10 @@
} else {
info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
+ 0.0f);
+ info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
+ 0.0f);
}
info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
@@ -341,6 +345,8 @@
} else {
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+ pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
displayId = ADISPLAY_ID_NONE;
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 15d5288..afbec99 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -142,12 +142,14 @@
hoveringIdBits.clear();
touchingIdBits.clear();
canceledIdBits.clear();
+ validIdBits.clear();
}
void CookedPointerData::copyFrom(const CookedPointerData& other) {
pointerCount = other.pointerCount;
hoveringIdBits = other.hoveringIdBits;
touchingIdBits = other.touchingIdBits;
+ validIdBits = other.validIdBits;
for (uint32_t i = 0; i < pointerCount; i++) {
pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -288,12 +290,14 @@
const PointerProperties& pointerProperties =
mLastCookedState.cookedPointerData.pointerProperties[i];
const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i];
- dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, "
- "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, "
- "toolMinor=%0.3f, "
+ dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, dx=%0.3f, dy=%0.3f, "
+ "pressure=%0.3f, touchMajor=%0.3f, touchMinor=%0.3f, "
+ "toolMajor=%0.3f, toolMinor=%0.3f, "
"orientation=%0.3f, tilt=%0.3f, distance=%0.3f, "
"toolType=%d, isHovering=%s\n",
i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
@@ -379,6 +383,7 @@
if (!changes ||
(changes &
(InputReaderConfiguration::CHANGE_DISPLAY_INFO |
+ InputReaderConfiguration::CHANGE_POINTER_CAPTURE |
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
@@ -562,7 +567,7 @@
* 4. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
- if (mParameters.hasAssociatedDisplay) {
+ if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
if (displayPort) {
// Find the viewport that contains the same port
@@ -620,7 +625,7 @@
// Determine device mode.
if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerGesturesEnabled) {
+ mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) {
mSource = AINPUT_SOURCE_MOUSE;
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
@@ -2269,15 +2274,26 @@
out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
}
+ // Write output relative fieldis if applicable.
+ uint32_t id = in.id;
+ if (mSource == AINPUT_SOURCE_TOUCHPAD &&
+ mLastCookedState.cookedPointerData.hasPointerCoordsForId(id)) {
+ const PointerCoords& p = mLastCookedState.cookedPointerData.pointerCoordsForId(id);
+ float dx = xTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_X);
+ float dy = yTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_Y);
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx);
+ out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
+ }
+
// Write output properties.
PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i];
- uint32_t id = in.id;
properties.clear();
properties.id = id;
properties.toolType = in.toolType;
- // Write id index.
+ // Write id index and mark id as valid.
mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
+ mCurrentCookedState.cookedPointerData.validIdBits.markBit(id);
}
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 94486a6..df6581d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -103,7 +103,7 @@
uint32_t pointerCount;
PointerProperties pointerProperties[MAX_POINTERS];
PointerCoords pointerCoords[MAX_POINTERS];
- BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits;
+ BitSet32 hoveringIdBits, touchingIdBits, canceledIdBits, validIdBits;
uint32_t idToIndex[MAX_POINTER_ID + 1];
CookedPointerData();
@@ -129,6 +129,8 @@
inline bool isTouching(uint32_t pointerIndex) const {
return touchingIdBits.hasBit(pointerProperties[pointerIndex].id);
}
+
+ inline bool hasPointerCoordsForId(uint32_t id) const { return validIdBits.hasBit(id); }
};
class TouchInputMapper : public InputMapper {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 21dd3c7..232045b 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -297,6 +297,8 @@
mConfig.defaultPointerDisplayId = pointerDisplayId;
}
+ float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; }
+
private:
DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
@@ -7533,4 +7535,207 @@
constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
}
+
+TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
+ // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(0, 0);
+ fakePointerController->setButtonState(0);
+
+ // prepare device and capture
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerCapture(true);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // captured touchpad should be a touchpad source
+ NotifyDeviceResetArgs resetArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+
+ // run captured pointer tests - note that this is unscaled, so input listener events should be
+ // identical to what the hardware sends (accounting for any
+ // calibration).
+ // FINGER 0 DOWN
+ processId(mapper, 1);
+ processPosition(mapper, 100 + RAW_X_MIN, 100 + RAW_Y_MIN);
+ processKey(mapper, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // expect coord[0] to contain initial location of touch 0
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+ ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 DOWN
+ processSlot(mapper, 1);
+ processId(mapper, 2);
+ processPosition(mapper, 560 + RAW_X_MIN, 154 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | 0x0100, args.action);
+ ASSERT_EQ(2U, args.pointerCount);
+ ASSERT_EQ(0, args.pointerProperties[0].id);
+ ASSERT_EQ(1, args.pointerProperties[1].id);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 560, 154, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 MOVE
+ processPosition(mapper, 540 + RAW_X_MIN, 690 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
+ // from move
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 0 MOVE
+ processSlot(mapper, 0);
+ processPosition(mapper, 50 + RAW_X_MIN, 800 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain new touch 0 location, coord[1] to contain previous location
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 50, 800, 1, 0, 0, 0, 0, 0, 0, 0));
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // BUTTON DOWN
+ processKey(mapper, BTN_LEFT, 1);
+ processSync(mapper);
+
+ // touchinputmapper design sends a move before button press
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
+
+ // BUTTON UP
+ processKey(mapper, BTN_LEFT, 0);
+ processSync(mapper);
+
+ // touchinputmapper design sends a move after button release
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+ // FINGER 0 UP
+ processId(mapper, -1);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | 0x0000, args.action);
+
+ // FINGER 1 MOVE
+ processSlot(mapper, 1);
+ processPosition(mapper, 320 + RAW_X_MIN, 900 + RAW_Y_MIN);
+ processSync(mapper);
+
+ // expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+ ASSERT_EQ(1U, args.pointerCount);
+ ASSERT_EQ(1, args.pointerProperties[0].id);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 1 UP
+ processId(mapper, -1);
+ processKey(mapper, BTN_TOUCH, 0);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+
+ // non captured touchpad should be a mouse source
+ mFakePolicy->setPointerCapture(false);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+ fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ fakePointerController->setPosition(0, 0);
+ fakePointerController->setButtonState(0);
+
+ // prepare device and capture
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+ // run uncaptured pointer tests - pushes out generic events
+ // FINGER 0 DOWN
+ processId(mapper, 3);
+ processPosition(mapper, 100, 100);
+ processKey(mapper, BTN_TOUCH, 1);
+ processSync(mapper);
+
+ // start at (100,100), cursor should be at (0,0) * scale
+ NotifyMotionArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(args.pointerCoords[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+
+ // FINGER 0 MOVE
+ processPosition(mapper, 200, 200);
+ processSync(mapper);
+
+ // compute scaling to help with touch position checking
+ float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN);
+ float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+ float scale =
+ mFakePolicy->getPointerGestureMovementSpeedRatio() * displayDiagonal / rawDiagonal;
+
+ // translate from (100,100) -> (200,200), cursor should have changed to (100,100) * scale)
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 100 * scale, 100 * scale, 0,
+ 0, 0, 0, 0, 0, 0, 0));
+}
+
+TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) {
+ std::shared_ptr<FakePointerController> fakePointerController =
+ std::make_shared<FakePointerController>();
+
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ prepareAxes(POSITION | ID | SLOT);
+ mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
+ mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
+ mFakePolicy->setPointerCapture(false);
+ MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+ // uncaptured touchpad should be a pointer device
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
+
+ // captured touchpad should be a touchpad device
+ mFakePolicy->setPointerCapture(true);
+ configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
+ ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
+}
+
} // namespace android