From 0909dc1c34c40aafbd6d633241e5622dc1cede0b Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 9 Mar 2023 20:11:09 +0000 Subject: MotionEvent: Round coordinates to a precision of 0.001 When tests inject input events at a location on the screen, the dispatched event is expected to have the same coordinates. However, on scaled devices, this may not always be the case due to precision losses incurred through floating point arithmetics. For example, it was possible for an injected event with a coordinate of 1.0 to end up with a value of 0.9997. To combat this issue, we will round transformed axis values that are leaving a MotionEvent to a precision of 0.001. After this CL, even if the injection process results in precision losses, they should be overcome by rounding, assuming injection does not require greater precision. This will solve the issue where input injected to an inclusive edge of the View bounds was not getting dispatched to the View due to precision losses. Bug: 264978231 Bug: 260965930 Test: atest libinput_tests Test: atest inputflinger_tests Test: atest HoverTest (with screen size override) Change-Id: I81062597058361a1218e6873d34b9b0d2fbfad96 Merged-In: I81062597058361a1218e6873d34b9b0d2fbfad96 --- include/input/Input.h | 2 + libs/input/Input.cpp | 21 +- libs/input/tests/InputEvent_test.cpp | 318 +++++++++++++-------- .../input/tests/InputPublisherAndConsumer_test.cpp | 20 +- .../inputflinger/tests/InputDispatcher_test.cpp | 8 +- 5 files changed, 227 insertions(+), 142 deletions(-) diff --git a/include/input/Input.h b/include/input/Input.h index e7d68fc349..db984e07f0 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -816,6 +816,8 @@ public: const PointerCoords&); static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&, const PointerCoords&); + // The rounding precision for transformed motion events. + static constexpr float ROUNDING_PRECISION = 0.001f; protected: int32_t mAction; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 4127f7ce10..6f065670b8 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -117,10 +117,23 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- +// Due to precision limitations when working with floating points, transforming - namely +// scaling - floating points can lead to minute errors. We round transformed values to approximately +// three decimal places so that values like 0.99997 show up as 1.0. +inline float roundTransformedCoords(float val) { + // Use a power to two to approximate three decimal places to potentially reduce some cycles. + // This should be at least as precise as MotionEvent::ROUNDING_PRECISION. + return std::round(val * 1024.f) / 1024.f; +} + +inline vec2 roundTransformedCoords(vec2 p) { + return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)}; +} + vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { const vec2 transformedXy = transform.transform(xy); const vec2 transformedOrigin = transform.transform(0, 0); - return transformedXy - transformedOrigin; + return roundTransformedCoords(transformedXy - transformedOrigin); } const char* inputEventTypeToString(int32_t type) { @@ -528,12 +541,12 @@ int MotionEvent::getSurfaceRotation() const { float MotionEvent::getXCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); - return vals.x; + return roundTransformedCoords(vals.x); } float MotionEvent::getYCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); - return vals.y; + return roundTransformedCoords(vals.y); } void MotionEvent::setCursorPosition(float x, float y) { @@ -855,7 +868,7 @@ std::string MotionEvent::actionToString(int32_t action) { static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, const vec2& xy) { return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy) - : transform.transform(xy); + : roundTransformedCoords(transform.transform(xy)); } vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index a92016ba3b..c59519c491 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -29,6 +29,8 @@ namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; + class BaseTest : public testing::Test { protected: static constexpr std::array HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -231,95 +233,107 @@ protected: static constexpr float RAW_X_OFFSET = 12; static constexpr float RAW_Y_OFFSET = -41.1; + void SetUp() override; + int32_t mId; ui::Transform mTransform; ui::Transform mRawTransform; + PointerProperties mPointerProperties[2]; + struct Sample { + PointerCoords pointerCoords[2]; + }; + std::array mSamples{}; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { +void MotionEventTest::SetUp() { mId = InputEvent::nextId(); mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1}); - PointerProperties pointerProperties[2]; - pointerProperties[0].clear(); - pointerProperties[0].id = 1; - pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - pointerProperties[1].clear(); - pointerProperties[1].id = 2; - pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - - PointerCoords pointerCoords[2]; - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); - pointerCoords[1].clear(); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + mPointerProperties[0].clear(); + mPointerProperties[0].id = 1; + mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerProperties[1].clear(); + mPointerProperties[1].id = 2; + mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + + mSamples[0].pointerCoords[0].clear(); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + mSamples[0].pointerCoords[1].clear(); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + + mSamples[1].pointerCoords[0].clear(); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + mSamples[1].pointerCoords[1].clear(); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + + mSamples[2].pointerCoords[0].clear(); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + mSamples[2].pointerCoords[1].clear(); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); +} + +void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, - pointerProperties, pointerCoords); - - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); - event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); - - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); - event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); + mPointerProperties, mSamples[0].pointerCoords); + event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords); + event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords); } void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { @@ -356,51 +370,65 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); - ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - - ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); - - ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0)); - ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0)); - ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1)); - ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1)); - ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0)); - ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1)); - - ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0)); - ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1)); - - ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); - ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); - ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1)); - ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1)); - ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0)); - ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1)); - - ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0)); - ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0)); - ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1)); - ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1)); - ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0)); - ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1)); + // Ensure the underlying PointerCoords are identical. + for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) { + for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) { + ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx], + event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]); + } + } + + ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON); + ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON); + + ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0), + EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1), + EPSILON); + + ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON); + + ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON); + + ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON); + ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON); + ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON); + ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON); + ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON); + ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON); + + ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON); + ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON); + ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON); ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); @@ -532,10 +560,10 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); - ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0)); - ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0)); - ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); - ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); + ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON); + ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON); + ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON); + ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON); ASSERT_EQ(212, event.getPressure(0)); ASSERT_EQ(213, event.getSize(0)); ASSERT_EQ(214 * 2, event.getTouchMajor(0)); @@ -773,18 +801,18 @@ TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) { // The x and y axes should have the window transform applied. const auto newPoint = transform.transform(60, 100); - ASSERT_EQ(newPoint.x, event.getX(0)); - ASSERT_EQ(newPoint.y, event.getY(0)); + ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON); + ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON); // The raw values should have the display transform applied. const auto raw = rawTransform.transform(60, 100); - ASSERT_EQ(raw.x, event.getRawX(0)); - ASSERT_EQ(raw.y, event.getRawY(0)); + ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON); + ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON); // Relative values should have the window transform applied without any translation. const auto rel = transformWithoutTranslation(transform, 42, 96); - ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); + ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON); + ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON); } TEST_F(MotionEventTest, Initialize_SetsClassification) { @@ -851,4 +879,42 @@ TEST_F(MotionEventTest, SetCursorPosition) { ASSERT_EQ(4, event.getYCursorPosition()); } +TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) { + // These are specifically integral values, since we are testing for rounding. + const vec2 EXPECTED{400.f, 700.f}; + + // Pick a transform such that transforming the point with its inverse and bringing that + // back to the original coordinate space results in a non-zero error amount due to the + // nature of floating point arithmetics. This can happen when the display is scaled. + // For example, the 'adb shell wm size' command can be used to set an override for the + // logical display size, which could result in the display being scaled. + constexpr float scale = 720.f / 1080.f; + ui::Transform transform; + transform.set(scale, 0, 0, scale); + ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED))); + + // Store the inverse-transformed values in the motion event. + const vec2 rawCoords = transform.inverse().transform(EXPECTED); + PointerCoords pc{}; + pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y); + PointerProperties pp{}; + MotionEvent event; + event.initialize(InputEvent::nextId(), 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + MotionClassification::NONE, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y, + transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc); + + // When using the getters from the MotionEvent to obtain the coordinates, the transformed + // values should be rounded by an appropriate amount so that they now precisely equal the + // original coordinates. + ASSERT_EQ(EXPECTED.x, event.getX(0)); + ASSERT_EQ(EXPECTED.y, event.getY(0)); + ASSERT_EQ(EXPECTED.x, event.getRawX(0)); + ASSERT_EQ(EXPECTED.y, event.getRawY(0)); + ASSERT_EQ(EXPECTED.x, event.getXCursorPosition()); + ASSERT_EQ(EXPECTED.y, event.getYCursorPosition()); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 05bc0bcbe8..8393e99d65 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -32,6 +32,8 @@ using android::base::Result; namespace android { +constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION; + class InputPublisherAndConsumerTest : public testing::Test { protected: std::shared_ptr mServerChannel, mClientChannel; @@ -233,10 +235,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); - EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); - EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); - EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); + EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON); + EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON); EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); @@ -249,10 +251,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); const auto& pc = pointerCoords[i]; - EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i)); - EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i)); - EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i)); - EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i)); + EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index bb8e5661b6..a423c3fd0d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -128,10 +128,10 @@ public: const auto& motionEvent = static_cast(event); EXPECT_EQ(motionEvent.getEventTime(), args.eventTime); EXPECT_EQ(motionEvent.getAction(), args.action); - EXPECT_EQ(motionEvent.getX(0), point.x); - EXPECT_EQ(motionEvent.getY(0), point.y); - EXPECT_EQ(motionEvent.getRawX(0), point.x); - EXPECT_EQ(motionEvent.getRawY(0), point.y); + EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION); }); } -- cgit v1.2.3-59-g8ed1b