MotionEvent: Differentiate directional support for AXIS_ORIENTATION
We have three cases for handling AXIS_ORIENTATION:
1. Orientation is not supported by the input device, so the value for
AXIS_ORIENTATION should always be 0, regardless of display rotation.
2. Orientation is supported, but a "direction" is not specified, like
for touchscreens and touchpads. The orientation must be in the range
[-pi/2, pi/2] for all display rotations.
3. Orientation is fully supported, and the value is in the range [-pi,
pi] for all display rotations.
It is insufficient to rely on whether or not the PointerCoords has the
bit for AXIS_ORIENTATION set to determine whether the event has a valid
orientation. This is because we always skip setting values of 0 for any
axis in PointerCoords to save space during serialization.
To support these three cases, we introduce two new MotionEvent private
flags. These are flags that are not exposed to Java and to the public
APIs.
Bug: 263310669
Test: atest TouchScreenTest libinput_tests inputflinger_tests
Change-Id: Iaa38afe35b00de74fbc5eefce25191bea52c2ea6
diff --git a/include/input/Input.h b/include/input/Input.h
index 3ca9c19..a96dae2 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -99,6 +99,18 @@
/* Motion event is inconsistent with previously sent motion events. */
AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION,
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants::
+ MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+
+ /** Mask for all private flags that are not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
};
/**
@@ -209,8 +221,12 @@
* Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or
* pointing upwards in the negative Y direction, a positive angle points towards the right, and a
* negative angle points towards the left.
+ *
+ * If the angle represents a direction that needs to be preserved, set isDirectional to true to get
+ * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set
+ * isDirectional to false to get an output range of [-pi/2, pi/2].
*/
-float transformAngle(const ui::Transform& transform, float angleRadians);
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional);
/**
* The type of the InputEvent.
@@ -462,7 +478,7 @@
// axes, however the window scaling will not.
void scale(float globalScale, float windowXScale, float windowYScale);
- void transform(const ui::Transform& transform);
+ void transform(const ui::Transform& transform, int32_t motionEventFlags);
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
@@ -930,10 +946,10 @@
// relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
// are used to apply these transformations for different axes.
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
- static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
- const PointerCoords&);
- static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
- const PointerCoords&);
+ static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
+ static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
// The rounding precision for transformed motion events.
static constexpr float ROUNDING_PRECISION = 0.001f;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index ee121d5..0a3f1fd 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -96,6 +96,19 @@
return AMOTION_EVENT_ACTION_DOWN;
}
+float transformOrientation(const ui::Transform& transform, const PointerCoords& coords,
+ int32_t motionEventFlags) {
+ if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) == 0) {
+ return 0;
+ }
+
+ const bool isDirectionalAngle =
+ (motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION) != 0;
+
+ return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+ isDirectionalAngle);
+}
+
} // namespace
const char* motionClassificationToString(MotionClassification classification) {
@@ -187,7 +200,7 @@
return roundTransformedCoords(transformedXy - transformedOrigin);
}
-float transformAngle(const ui::Transform& transform, float angleRadians) {
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
float x = sinf(angleRadians);
@@ -201,6 +214,11 @@
transformedPoint.x -= origin.x;
transformedPoint.y -= origin.y;
+ if (!isDirectional && transformedPoint.y > 0) {
+ // Limit the range of atan2f to [-pi/2, pi/2] by reversing the direction of the vector.
+ transformedPoint *= -1;
+ }
+
// Derive the transformed vector's clockwise angle from vertical.
// The return value of atan2f is in range [-pi, pi] which conforms to the orientation API.
return atan2f(transformedPoint.x, -transformedPoint.y);
@@ -530,7 +548,7 @@
return true;
}
-void PointerCoords::transform(const ui::Transform& transform) {
+void PointerCoords::transform(const ui::Transform& transform, int32_t motionEventFlags) {
const vec2 xy = transform.transform(getXYValue());
setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
@@ -544,9 +562,9 @@
setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
}
- if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) {
- const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val));
+ if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) != 0) {
+ setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformOrientation(transform, *this, motionEventFlags));
}
}
@@ -723,13 +741,13 @@
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
+ return calculateTransformedAxisValue(axis, mSource, mFlags, mRawTransform, coords);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
+ return calculateTransformedAxisValue(axis, mSource, mFlags, mTransform, coords);
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -787,7 +805,7 @@
// Apply the transformation to all samples.
std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
- [&transform](PointerCoords& c) { c.transform(transform); });
+ [&](PointerCoords& c) { c.transform(transform, mFlags); });
if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
@@ -1059,7 +1077,7 @@
}
// Keep in sync with calculateTransformedCoords.
-float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
const ui::Transform& transform,
const PointerCoords& coords) {
if (shouldDisregardTransformation(source)) {
@@ -1081,7 +1099,7 @@
}
if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
- return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ return transformOrientation(transform, coords, flags);
}
return coords.getAxisValue(axis);
@@ -1089,7 +1107,7 @@
// Keep in sync with calculateTransformedAxisValue. This is an optimization of
// calculateTransformedAxisValue for all PointerCoords axes.
-PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
+PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, int32_t flags,
const ui::Transform& transform,
const PointerCoords& coords) {
if (shouldDisregardTransformation(source)) {
@@ -1109,8 +1127,7 @@
out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(transform,
- coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)));
+ transformOrientation(transform, coords, flags));
return out;
}
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 90ed2b7..650dc5c 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -116,6 +116,31 @@
const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40;
/**
+ * This flag indicates that the event has a valid value for AXIS_ORIENTATION.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80;
+
+ /**
+ * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine
+ * the direction in which the tool is pointing. The value of the orientation axis will be in
+ * the range [-pi, pi], which represents a full circle. This is usually supported by devices
+ * like styluses.
+ *
+ * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing
+ * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2],
+ * which represents half a circle. This is usually the case for devices like touchscreens and
+ * touchpads, for which it is difficult to tell which direction along the major axis of the
+ * touch ellipse the finger is pointing.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100;
+
+ /**
* The input event was generated or modified by accessibility service.
* Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
* set of flags, including in input/Input.h and in android/input.h.
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 476b5cf..3717f49 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,23 +26,39 @@
namespace android {
+namespace {
+
// Default display id.
-static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
-static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
-static constexpr auto POINTER_0_DOWN =
+constexpr auto POINTER_0_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-static constexpr auto POINTER_1_DOWN =
+constexpr auto POINTER_1_DOWN =
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-static constexpr auto POINTER_0_UP =
+constexpr auto POINTER_0_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-static constexpr auto POINTER_1_UP =
+constexpr auto POINTER_1_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+std::array<float, 9> asFloat9(const ui::Transform& t) {
+ std::array<float, 9> mat{};
+ mat[0] = t[0][0];
+ mat[1] = t[1][0];
+ mat[2] = t[2][0];
+ mat[3] = t[0][1];
+ mat[4] = t[1][1];
+ mat[5] = t[2][1];
+ mat[6] = t[0][2];
+ mat[7] = t[1][2];
+ mat[8] = t[2][2];
+ return mat;
+}
+
class BaseTest : public testing::Test {
protected:
static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
@@ -50,6 +66,8 @@
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
};
+} // namespace
+
// --- PointerCoordsTest ---
class PointerCoordsTest : public BaseTest {
@@ -344,13 +362,15 @@
}
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+ const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
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,
- mPointerProperties, mSamples[0].pointerCoords);
+ AMOTION_EVENT_ACTION_MOVE, 0, flags, 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, mPointerProperties, mSamples[0].pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
}
@@ -364,7 +384,10 @@
ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
EXPECT_EQ(HMAC, event->getHmac());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
- ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
+ ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+ event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
@@ -799,8 +822,10 @@
}
MotionEvent event;
ui::Transform identityTransform;
+ const int32_t flags = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
- INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
/*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2,
@@ -1087,4 +1112,90 @@
ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
}
+TEST_F(MotionEventTest, InvalidOrientationNotRotated) {
+ // This touch event does not have a value for AXIS_ORIENTATION, and the flags are implicitly
+ // set to 0. The transform is set to a 90-degree rotation.
+ MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .build();
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+}
+
+TEST_F(MotionEventTest, ValidZeroOrientationRotated) {
+ // This touch events will implicitly have a value of 0 for its AXIS_ORIENTATION.
+ auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+ MotionEvent nonDirectionalEvent = builder.build();
+ MotionEvent directionalEvent =
+ builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+ // The angle is rotated by the initial transform, a 90-degree rotation.
+ ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), M_PI_2, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+ ASSERT_NEAR(fabs(directionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+
+ nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), -M_PI_2, EPSILON);
+}
+
+TEST_F(MotionEventTest, ValidNonZeroOrientationRotated) {
+ const float initial = 1.f;
+ auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER)
+ .x(4)
+ .y(4)
+ .axis(AMOTION_EVENT_AXIS_ORIENTATION, initial))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+
+ MotionEvent nonDirectionalEvent = builder.build();
+ MotionEvent directionalEvent =
+ builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+ // The angle is rotated by the initial transform, a 90-degree rotation.
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial + M_PI_2, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+
+ nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 70529bb..f49469c 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -96,7 +96,9 @@
hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
- flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
if (action == AMOTION_EVENT_ACTION_CANCEL) {
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 48512f7..e65a919 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -89,7 +89,9 @@
hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
- flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
if (action == AMOTION_EVENT_ACTION_CANCEL) {
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 527edb6..8b38874 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -444,10 +444,10 @@
newCoords.copyFrom(motionEntry.pointerCoords[i]);
// First, apply the current pointer's transform to update the coordinates into
// window space.
- newCoords.transform(currTransform);
+ newCoords.transform(currTransform, motionEntry.flags);
// Next, apply the inverse transform of the normalized coordinates so the
// current coordinates are transformed into the normalized coordinate space.
- newCoords.transform(inverseTransform);
+ newCoords.transform(inverseTransform, motionEntry.flags);
}
}
@@ -5133,8 +5133,8 @@
}
for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
entry.pointerCoords[i] =
- MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
- entry.pointerCoords[i]);
+ MotionEvent::calculateTransformedCoords(entry.source, entry.flags,
+ transformToDisplay, entry.pointerCoords[i]);
}
}
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index 2d7554c..0b17507 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -123,7 +123,8 @@
const auto& coords = motion->pointerCoords[i];
const auto coordsInWindow =
- MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords);
+ MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
+ args.transform, coords);
auto bits = BitSet64(coords.bits);
for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
const uint32_t axis = bits.clearFirstMarkedBit();
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 9d049ae..a383490 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2343,20 +2343,23 @@
if (mHaveTilt) {
float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
- orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)));
+ orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)),
+ /*isDirectional=*/true);
tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
} else {
tilt = 0;
switch (mCalibration.orientationCalibration) {
case Calibration::OrientationCalibration::INTERPOLATED:
- orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale);
+ orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale,
+ /*isDirectional=*/true);
break;
case Calibration::OrientationCalibration::VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
- orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f);
+ orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f,
+ /*isDirectional=*/true);
float confidence = hypotf(c1, c2);
float scale = 1.0f + confidence / 16.0f;
touchMajor *= scale;
@@ -3672,6 +3675,14 @@
if (mCurrentStreamModifiedByExternalStylus) {
source |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
}
+ if (mOrientedRanges.orientation.has_value()) {
+ flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION;
+ if (mOrientedRanges.tilt.has_value()) {
+ // In the current implementation, only devices that report a value for tilt supports
+ // directional orientation.
+ flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
+ }
+ }
const ui::LogicalDisplayId displayId =
getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2e65d4a..804b4f7 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -5485,6 +5485,9 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, tool, tool, tool, tool, orientation, distance));
ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT));
+ ASSERT_EQ(args.flags,
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION);
}
TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
@@ -7927,6 +7930,7 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor,
orientation, distance));
+ ASSERT_EQ(args.flags, AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
}
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {