Add orientation configuration for touchscreen devices
There are many instances where devices have display panels installed in
a different orientation than the device's default orientation. In these
cases, there is an SF flag to change the device's default orientation,
using ro.surface_flinger.primary_display_orientation. However, when that
is used, the touchscreens don't work in the expected orientation.
This CL adds the ability to configure the orientation of a touchscreen
device using an Input Device Configuration (IDC) file.
Bug: 196357204
Test: atest inputflinger_tests
Test: manual: ensure touch works as expected with
ro.sf.primary_display_orientation and touch.orientation set to 90
Merged-In: I1267f2b3fdb6faaef2923789bcaf5a6141971431
Change-Id: I1267f2b3fdb6faaef2923789bcaf5a6141971431
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index af02844..f6fa7a1 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -470,6 +470,23 @@
getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
mParameters.orientationAware);
+ mParameters.orientation = Parameters::Orientation::ORIENTATION_0;
+ String8 orientationString;
+ if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientation"),
+ orientationString)) {
+ if (mParameters.deviceType != Parameters::DeviceType::TOUCH_SCREEN) {
+ ALOGW("The configuration 'touch.orientation' is only supported for touchscreens.");
+ } else if (orientationString == "ORIENTATION_90") {
+ mParameters.orientation = Parameters::Orientation::ORIENTATION_90;
+ } else if (orientationString == "ORIENTATION_180") {
+ mParameters.orientation = Parameters::Orientation::ORIENTATION_180;
+ } else if (orientationString == "ORIENTATION_270") {
+ mParameters.orientation = Parameters::Orientation::ORIENTATION_270;
+ } else if (orientationString != "ORIENTATION_0") {
+ ALOGW("Invalid value for touch.orientation: '%s'", orientationString.string());
+ }
+ }
+
mParameters.hasAssociatedDisplay = false;
mParameters.associatedDisplayIsExternal = false;
if (mParameters.orientationAware ||
@@ -508,6 +525,7 @@
toString(mParameters.associatedDisplayIsExternal),
mParameters.uniqueDisplayId.c_str());
dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
+ dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n";
}
void TouchInputMapper::configureRawPointerAxes() {
@@ -669,7 +687,13 @@
int32_t naturalPhysicalWidth, naturalPhysicalHeight;
int32_t naturalPhysicalLeft, naturalPhysicalTop;
int32_t naturalDeviceWidth, naturalDeviceHeight;
- switch (mViewport.orientation) {
+
+ // Apply the inverse of the input device orientation so that the surface is configured
+ // in the same orientation as the device. The input device orientation will be
+ // re-applied to mSurfaceOrientation.
+ const int32_t naturalSurfaceOrientation =
+ (mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
+ switch (naturalSurfaceOrientation) {
case DISPLAY_ORIENTATION_90:
naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
@@ -752,6 +776,10 @@
mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
: DISPLAY_ORIENTATION_0;
}
+
+ // Apply the input device orientation for the device.
+ mSurfaceOrientation =
+ (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
} else {
mPhysicalWidth = rawWidth;
mPhysicalHeight = rawHeight;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 920f842..e104220 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -204,6 +204,15 @@
bool hasAssociatedDisplay;
bool associatedDisplayIsExternal;
bool orientationAware;
+
+ enum class Orientation : int32_t {
+ ORIENTATION_0 = DISPLAY_ORIENTATION_0,
+ ORIENTATION_90 = DISPLAY_ORIENTATION_90,
+ ORIENTATION_180 = DISPLAY_ORIENTATION_180,
+ ORIENTATION_270 = DISPLAY_ORIENTATION_270,
+ };
+ Orientation orientation;
+
bool hasButtonUnderPad;
std::string uniqueDisplayId;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 43f14bd..91da0d5 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -4659,6 +4659,8 @@
void prepareLocationCalibration();
int32_t toRawX(float displayX);
int32_t toRawY(float displayY);
+ int32_t toRotatedRawX(float displayX);
+ int32_t toRotatedRawY(float displayY);
float toCookedX(float rawX, float rawY);
float toCookedY(float rawX, float rawY);
float toDisplayX(int32_t rawX);
@@ -4741,6 +4743,14 @@
return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
}
+int32_t TouchInputMapperTest::toRotatedRawX(float displayX) {
+ return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_HEIGHT + RAW_X_MIN);
+}
+
+int32_t TouchInputMapperTest::toRotatedRawY(float displayY) {
+ return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_WIDTH + RAW_Y_MIN);
+}
+
float TouchInputMapperTest::toCookedX(float rawX, float rawY) {
AFFINE_TRANSFORM.applyTo(rawX, rawY);
return rawX;
@@ -5487,6 +5497,172 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
}
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation0_RotatesMotions) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareButtons();
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.orientationAware", "1");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_0");
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs args;
+
+ // Orientation 0.
+ processDown(mapper, toRawX(50), toRawY(75));
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation90_RotatesMotions) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareButtons();
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.orientationAware", "1");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs args;
+
+ // Orientation 90.
+ processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation180_RotatesMotions) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareButtons();
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.orientationAware", "1");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_180");
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs args;
+
+ // Orientation 180.
+ processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientation270_RotatesMotions) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareButtons();
+ prepareAxes(POSITION);
+ addConfigurationProperty("touch.orientationAware", "1");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_270");
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+ NotifyMotionArgs args;
+
+ // Orientation 270.
+ processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
+TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotionWithDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareButtons();
+ prepareAxes(POSITION);
+ // Since InputReader works in the un-rotated coordinate space, only devices that are not
+ // orientation-aware are affected by display rotation.
+ addConfigurationProperty("touch.orientationAware", "0");
+ addConfigurationProperty("touch.orientation", "ORIENTATION_90");
+ auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs args;
+
+ // Orientation 90, Rotation 0.
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+ processDown(mapper, RAW_X_MAX - toRotatedRawX(75) + RAW_X_MIN, toRotatedRawY(50));
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+ // Orientation 90, Rotation 90.
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_90);
+ processDown(mapper, toRotatedRawX(50), toRotatedRawY(75));
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+ // Orientation 90, Rotation 180.
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_180);
+ processDown(mapper, toRotatedRawX(75), RAW_Y_MAX - toRotatedRawY(50) + RAW_Y_MIN);
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+
+ // Orientation 90, Rotation 270.
+ clearViewports();
+ prepareDisplay(DISPLAY_ORIENTATION_270);
+ processDown(mapper, RAW_X_MAX - toRotatedRawX(50) + RAW_X_MIN,
+ RAW_Y_MAX - toRotatedRawY(75) + RAW_Y_MIN);
+ processSync(mapper);
+
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+ EXPECT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+ EXPECT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+ processUp(mapper);
+ processSync(mapper);
+ EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
+}
+
TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(DISPLAY_ORIENTATION_0);