Merge changes from topic "move-input-rotation-to-native"
* changes:
InputDispatcherPolicy: Transform incoming/outgoing events
TouchInputMapper: Block touches outside logical display
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 30e5d5b..a1542c8 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,10 +20,8 @@
#include <attestation/HmacKeyManager.h>
#include <cutils/compiler.h>
#include <inttypes.h>
-#include <limits.h>
#include <string.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <gui/constants.h>
#include <input/Input.h>
@@ -43,15 +41,6 @@
namespace {
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- base::GetBoolProperty("persist.debug.per_window_input_rotation", false);
-
- return PER_WINDOW_INPUT_ROTATION;
-}
-
float transformAngle(const ui::Transform& transform, float angleRadians) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
@@ -511,8 +500,6 @@
size_t historicalIndex) const {
const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis);
-
if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
// For compatibility, convert raw coordinates into logical display space.
const vec2 xy = shouldDisregardTranslation(mSource)
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 7e7dfd5..caf3a61 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -231,36 +231,14 @@
static constexpr float RAW_X_OFFSET = 12;
static constexpr float RAW_Y_OFFSET = -41.1;
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
-
int32_t mId;
ui::Transform mTransform;
ui::Transform mRawTransform;
- void SetUp() override;
- void TearDown() override;
-
void initializeEventWithHistory(MotionEvent* event);
void assertEqualsEventWithHistory(const MotionEvent* event);
};
-const std::optional<bool> MotionEventTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- !base::GetProperty("persist.debug.per_window_input_rotation", "").empty()
- ? std::optional(base::GetBoolProperty("persist.debug.per_window_input_rotation", false))
- : std::nullopt;
-
-void MotionEventTest::SetUp() {
- // Ensure per_window_input_rotation is enabled.
- base::SetProperty("persist.debug.per_window_input_rotation", "true");
-}
-
-void MotionEventTest::TearDown() {
- const auto val = INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE.has_value()
- ? (*INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE ? "true" : "false")
- : "";
- base::SetProperty("persist.debug.per_window_input_rotation", val);
-}
-
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
mId = InputEvent::nextId();
mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1});
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5f48c1d..f094fee 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -524,6 +524,16 @@
return true;
}
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
+vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
+ const vec2 transformedXy = transform.transform(x, y);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -3962,15 +3972,19 @@
mLock.lock();
if (shouldSendMotionToInputFilterLocked(args)) {
+ ui::Transform displayTransform;
+ if (const auto it = mDisplayInfos.find(args->displayId); it != mDisplayInfos.end()) {
+ displayTransform = it->second.transform;
+ }
+
mLock.unlock();
MotionEvent event;
- ui::Transform identityTransform;
event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
args->action, args->actionButton, args->flags, args->edgeFlags,
args->metaState, args->buttonState, args->classification,
- identityTransform, args->xPrecision, args->yPrecision,
- args->xCursorPosition, args->yCursorPosition, identityTransform,
+ displayTransform, args->xPrecision, args->yPrecision,
+ args->xCursorPosition, args->yCursorPosition, displayTransform,
args->downTime, args->eventTime, args->pointerCount,
args->pointerProperties, args->pointerCoords);
@@ -4220,6 +4234,7 @@
pointerProperties, samplePointerCoords,
motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*injectedEntry);
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -4241,6 +4256,7 @@
uint32_t(pointerCount), pointerProperties,
samplePointerCoords, motionEvent.getXOffset(),
motionEvent.getYOffset());
+ transformMotionEntryForInjectionLocked(*nextInjectedEntry);
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -4404,6 +4420,38 @@
}
}
+void InputDispatcher::transformMotionEntryForInjectionLocked(MotionEntry& entry) const {
+ const bool isRelativeMouseEvent = isFromSource(entry.source, AINPUT_SOURCE_MOUSE_RELATIVE);
+ if (!isRelativeMouseEvent && !isFromSource(entry.source, AINPUT_SOURCE_CLASS_POINTER)) {
+ return;
+ }
+
+ // Input injection works in the logical display coordinate space, but the input pipeline works
+ // display space, so we need to transform the injected events accordingly.
+ const auto it = mDisplayInfos.find(entry.displayId);
+ if (it == mDisplayInfos.end()) return;
+ const auto& transformToDisplay = it->second.transform.inverse();
+
+ for (uint32_t i = 0; i < entry.pointerCount; i++) {
+ PointerCoords& pc = entry.pointerCoords[i];
+ const auto xy = isRelativeMouseEvent
+ ? transformWithoutTranslation(transformToDisplay, pc.getX(), pc.getY())
+ : transformToDisplay.transform(pc.getXYValue());
+ pc.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+
+ // Axes with relative values never represent points on a screen, so they should never have
+ // translation applied. If a device does not report relative values, these values are always
+ // 0, and will remain unaffected by the following operation.
+ const auto rel =
+ transformWithoutTranslation(transformToDisplay,
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
+ pc.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, rel.x);
+ pc.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, rel.y);
+ }
+}
+
void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) {
InjectionState* injectionState = entry.injectionState;
if (injectionState) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 51ec551..2282d91 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -280,6 +280,7 @@
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
void setInjectionResult(EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
+ void transformMotionEntryForInjectionLocked(MotionEntry&) const REQUIRES(mLock);
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry& entry);
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 419b0d0..22a0b57 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -749,16 +749,17 @@
mPhysicalLeft = naturalPhysicalLeft;
mPhysicalTop = naturalPhysicalTop;
- const int32_t oldSurfaceWidth = mRawSurfaceWidth;
- const int32_t oldSurfaceHeight = mRawSurfaceHeight;
- mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
- mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
- mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
-
if (isPerWindowInputRotationEnabled()) {
+ // When per-window input rotation is enabled, InputReader works in the display
+ // space, so the surface bounds are the bounds of the display device.
+ const int32_t oldSurfaceWidth = mRawSurfaceWidth;
+ const int32_t oldSurfaceHeight = mRawSurfaceHeight;
+ mRawSurfaceWidth = naturalDeviceWidth;
+ mRawSurfaceHeight = naturalDeviceHeight;
+ mSurfaceLeft = 0;
+ mSurfaceTop = 0;
+ mSurfaceRight = mRawSurfaceWidth;
+ mSurfaceBottom = mRawSurfaceHeight;
// When per-window input rotation is enabled, InputReader works in the un-rotated
// coordinate space, so we don't need to do anything if the device is already
// orientation-aware. If the device is not orientation-aware, then we need to apply
@@ -774,6 +775,14 @@
mRawSurfaceWidth == oldSurfaceWidth &&
mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
} else {
+ mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+ mRawSurfaceHeight =
+ naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+ mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
+ mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+ mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
+ mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
+
mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
: DISPLAY_ORIENTATION_0;
}
@@ -3772,6 +3781,12 @@
const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
+ if (isPerWindowInputRotationEnabled()) {
+ return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
+ xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
+ y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
+ yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
+ }
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index a56468f..3340672 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -435,6 +435,7 @@
// The surface origin specifies how the surface coordinates should be translated
// to align with the logical display coordinate space.
+ // TODO(b/188939842): Remove surface coordinates when Per-Window Input Rotation is enabled.
int32_t mSurfaceLeft;
int32_t mSurfaceTop;
int32_t mSurfaceRight;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e9d45b2..7fb2ccf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -92,13 +92,29 @@
FakeInputDispatcherPolicy() {}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
- args.displayId);
+ assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_KEY);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& keyEvent = static_cast<const KeyEvent&>(event);
+ EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(keyEvent.getAction(), args.action);
+ });
}
- void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
- assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
- args.displayId);
+ void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
+ assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), AINPUT_EVENT_TYPE_MOTION);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& motionEvent = static_cast<const MotionEvent&>(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);
+ });
}
void assertFilterInputEventWasNotCalled() {
@@ -425,26 +441,11 @@
mDropTargetWindowToken = token;
}
- void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
- int32_t displayId) {
+ void assertFilterInputEventWasCalledInternal(
+ const std::function<void(const InputEvent&)>& verify) {
std::scoped_lock lock(mLock);
ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), type);
-
- if (type == AINPUT_EVENT_TYPE_KEY) {
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
- EXPECT_EQ(keyEvent.getEventTime(), eventTime);
- EXPECT_EQ(keyEvent.getAction(), action);
- EXPECT_EQ(keyEvent.getDisplayId(), displayId);
- } else if (type == AINPUT_EVENT_TYPE_MOTION) {
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
- EXPECT_EQ(motionEvent.getEventTime(), eventTime);
- EXPECT_EQ(motionEvent.getAction(), action);
- EXPECT_EQ(motionEvent.getDisplayId(), displayId);
- } else {
- FAIL() << "Unknown type: " << type;
- }
-
+ verify(*mFilteredEvent);
mFilteredEvent = nullptr;
}
};
@@ -3481,7 +3482,8 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- void testNotifyMotion(int32_t displayId, bool expectToBeFiltered) {
+ void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
+ const ui::Transform& transform = ui::Transform()) {
NotifyMotionArgs motionArgs;
motionArgs =
@@ -3492,7 +3494,8 @@
mDispatcher->notifyMotion(&motionArgs);
ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
- mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
+ const auto xy = transform.transform(motionArgs.pointerCoords->getXYValue());
+ mFakePolicy->assertFilterInputEventWasCalled(motionArgs, xy);
} else {
mFakePolicy->assertFilterInputEventWasNotCalled();
}
@@ -3550,6 +3553,30 @@
testNotifyKey(/*expectToBeFiltered*/ false);
}
+// Ensure that MotionEvents sent to the InputFilter through InputListener are converted to the
+// logical display coordinate space.
+TEST_F(InputFilterTest, MotionEvent_UsesLogicalDisplayCoordinates_notifyMotion) {
+ ui::Transform firstDisplayTransform;
+ firstDisplayTransform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
+ ui::Transform secondDisplayTransform;
+ secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});
+
+ std::vector<gui::DisplayInfo> displayInfos(2);
+ displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
+ displayInfos[0].transform = firstDisplayTransform;
+ displayInfos[1].displayId = SECOND_DISPLAY_ID;
+ displayInfos[1].transform = secondDisplayTransform;
+
+ mDispatcher->onWindowInfosChanged({}, displayInfos);
+
+ // Enable InputFilter
+ mDispatcher->setInputFilterEnabled(true);
+
+ // Ensure the correct transforms are used for the displays.
+ testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered*/ true, firstDisplayTransform);
+ testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered*/ true, secondDisplayTransform);
+}
+
class InputFilterInjectionPolicyTest : public InputDispatcherTest {
protected:
virtual void SetUp() override {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 5c430f5..b28c1e2 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -250,14 +250,35 @@
return mConfig.getDisplayViewportByPort(displayPort);
}
+ void addDisplayViewport(DisplayViewport viewport) {
+ mViewports.push_back(std::move(viewport));
+ mConfig.setDisplayViewports(mViewports);
+ }
+
void addDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation,
bool isActive, const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType viewportType) {
- const DisplayViewport viewport =
- createDisplayViewport(displayId, width, height, orientation, isActive, uniqueId,
- physicalPort, viewportType);
- mViewports.push_back(viewport);
- mConfig.setDisplayViewports(mViewports);
+ std::optional<uint8_t> physicalPort, ViewportType type) {
+ const bool isRotated =
+ (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270);
+ DisplayViewport v;
+ v.displayId = displayId;
+ v.orientation = orientation;
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = isRotated ? height : width;
+ v.logicalBottom = isRotated ? width : height;
+ v.physicalLeft = 0;
+ v.physicalTop = 0;
+ v.physicalRight = isRotated ? height : width;
+ v.physicalBottom = isRotated ? width : height;
+ v.deviceWidth = isRotated ? height : width;
+ v.deviceHeight = isRotated ? width : height;
+ v.isActive = isActive;
+ v.uniqueId = uniqueId;
+ v.physicalPort = physicalPort;
+ v.type = type;
+
+ addDisplayViewport(v);
}
bool updateViewport(const DisplayViewport& viewport) {
@@ -330,32 +351,6 @@
private:
uint32_t mNextPointerCaptureSequenceNumber = 0;
- DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- int32_t orientation, bool isActive,
- const std::string& uniqueId,
- std::optional<uint8_t> physicalPort, ViewportType type) {
- bool isRotated = (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270);
- DisplayViewport v;
- v.displayId = displayId;
- v.orientation = orientation;
- v.logicalLeft = 0;
- v.logicalTop = 0;
- v.logicalRight = isRotated ? height : width;
- v.logicalBottom = isRotated ? width : height;
- v.physicalLeft = 0;
- v.physicalTop = 0;
- v.physicalRight = isRotated ? height : width;
- v.physicalBottom = isRotated ? width : height;
- v.deviceWidth = isRotated ? height : width;
- v.deviceHeight = isRotated ? width : height;
- v.isActive = isActive;
- v.uniqueId = uniqueId;
- v.physicalPort = physicalPort;
- v.type = type;
- return v;
- }
-
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
*outConfig = mConfig;
}
@@ -6296,6 +6291,172 @@
toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
}
+// --- TouchDisplayProjectionTest ---
+
+class TouchDisplayProjectionTest : public SingleTouchInputMapperTest {
+public:
+ // The values inside DisplayViewport are expected to be pre-rotated. This updates the current
+ // DisplayViewport to pre-rotate the values. The viewport's physical display will be set to the
+ // rotated equivalent of the given un-rotated physical display bounds.
+ void configurePhysicalDisplay(int32_t orientation, Rect naturalPhysicalDisplay) {
+ uint32_t inverseRotationFlags;
+ auto width = DISPLAY_WIDTH;
+ auto height = DISPLAY_HEIGHT;
+ switch (orientation) {
+ case DISPLAY_ORIENTATION_90:
+ inverseRotationFlags = ui::Transform::ROT_270;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_180:
+ inverseRotationFlags = ui::Transform::ROT_180;
+ break;
+ case DISPLAY_ORIENTATION_270:
+ inverseRotationFlags = ui::Transform::ROT_90;
+ std::swap(width, height);
+ break;
+ case DISPLAY_ORIENTATION_0:
+ inverseRotationFlags = ui::Transform::ROT_0;
+ break;
+ default:
+ FAIL() << "Invalid orientation: " << orientation;
+ }
+
+ const ui::Transform rotation(inverseRotationFlags, width, height);
+ const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay);
+
+ std::optional<DisplayViewport> internalViewport =
+ *mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
+ DisplayViewport& v = *internalViewport;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+
+ v.logicalLeft = 0;
+ v.logicalTop = 0;
+ v.logicalRight = 100;
+ v.logicalBottom = 100;
+
+ v.physicalLeft = rotatedPhysicalDisplay.left;
+ v.physicalTop = rotatedPhysicalDisplay.top;
+ v.physicalRight = rotatedPhysicalDisplay.right;
+ v.physicalBottom = rotatedPhysicalDisplay.bottom;
+
+ v.deviceWidth = width;
+ v.deviceHeight = height;
+
+ v.isActive = true;
+ v.uniqueId = UNIQUE_ID;
+ v.type = ViewportType::INTERNAL;
+ mFakePolicy->updateViewport(v);
+ configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ void assertReceivedMove(const Point& point) {
+ NotifyMotionArgs motionArgs;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], point.x, point.y,
+ 1, 0, 0, 0, 0, 0, 0, 0));
+ }
+};
+
+TEST_F(TouchDisplayProjectionTest, IgnoresTouchesOutsidePhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+ static const std::array<Point, 6> kPointsOutsidePhysicalDisplay{
+ {{-10, -10}, {0, 0}, {5, 100}, {50, 15}, {75, 100}, {50, 165}}};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches outside the physical display should be ignored, and should not generate any
+ // events. Ensure touches at the following points that lie outside of the physical display
+ // area do not generate any events.
+ for (const auto& point : kPointsOutsidePhysicalDisplay) {
+ processDown(mapper, toRawX(point.x), toRawY(point.y));
+ processSync(mapper);
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled())
+ << "Unexpected event generated for touch outside physical display at point: "
+ << point.x << ", " << point.y;
+ }
+ }
+}
+
+TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(DISPLAY_ORIENTATION_0);
+
+ prepareButtons();
+ prepareAxes(POSITION);
+ SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
+
+ NotifyMotionArgs motionArgs;
+
+ // Configure the DisplayViewport such that the logical display maps to a subsection of
+ // the display panel called the physical display. Here, the physical display is bounded by the
+ // points (10, 20) and (70, 160) inside the display space, which is of the size 400 x 800.
+ static const Rect kPhysicalDisplay{10, 20, 70, 160};
+
+ for (auto orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
+ DISPLAY_ORIENTATION_270}) {
+ configurePhysicalDisplay(orientation, kPhysicalDisplay);
+
+ // Touches that start outside the physical display should be ignored until it enters the
+ // physical display bounds, at which point it should generate a down event. Start a touch at
+ // the point (5, 100), which is outside the physical display bounds.
+ static const Point kOutsidePoint{5, 100};
+ processDown(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Move the touch into the physical display area. This should generate a pointer down.
+ processMove(mapper, toRawX(11), toRawY(21));
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+ ASSERT_EQ(size_t(1), motionArgs.pointerCount);
+ ASSERT_NO_FATAL_FAILURE(
+ assertPointerCoords(motionArgs.pointerCoords[0], 11, 21, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Move the touch inside the physical display area. This should generate a pointer move.
+ processMove(mapper, toRawX(69), toRawY(159));
+ processSync(mapper);
+ assertReceivedMove({69, 159});
+
+ // Move outside the physical display area. Since the pointer is already down, this should
+ // now continue generating events.
+ processMove(mapper, toRawX(kOutsidePoint.x), toRawY(kOutsidePoint.y));
+ processSync(mapper);
+ assertReceivedMove(kOutsidePoint);
+
+ // Release. This should generate a pointer up.
+ processUp(mapper);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+ ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], kOutsidePoint.x,
+ kOutsidePoint.y, 1, 0, 0, 0, 0, 0, 0, 0));
+
+ // Ensure no more events were generated.
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasNotCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+ }
+}
+
// --- MultiTouchInputMapperTest ---
class MultiTouchInputMapperTest : public TouchInputMapperTest {
@@ -8629,173 +8790,6 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-/**
- * Test touch should not work if outside of surface.
- */
-class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
-protected:
- void halfDisplayToCenterHorizontal(int32_t orientation) {
- std::optional<DisplayViewport> internalViewport =
- mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
-
- // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
- internalViewport->orientation = orientation;
- if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_HEIGHT;
- internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
-
- internalViewport->physicalLeft = 0;
- internalViewport->physicalTop = DISPLAY_WIDTH / 4;
- internalViewport->physicalRight = DISPLAY_HEIGHT;
- internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
-
- internalViewport->deviceWidth = DISPLAY_HEIGHT;
- internalViewport->deviceHeight = DISPLAY_WIDTH;
- } else {
- internalViewport->logicalLeft = 0;
- internalViewport->logicalTop = 0;
- internalViewport->logicalRight = DISPLAY_WIDTH / 2;
- internalViewport->logicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
- internalViewport->physicalTop = 0;
- internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
- internalViewport->physicalBottom = DISPLAY_HEIGHT;
-
- internalViewport->deviceWidth = DISPLAY_WIDTH;
- internalViewport->deviceHeight = DISPLAY_HEIGHT;
- }
-
- mFakePolicy->updateViewport(internalViewport.value());
- configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
- }
-
- void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xOutside, int32_t yOutside,
- int32_t xInside, int32_t yInside, int32_t xExpected,
- int32_t yExpected) {
- // touch on outside area should not work.
- processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-
- // touch on inside area should receive the event.
- NotifyMotionArgs args;
- processPosition(mapper, toRawX(xInside), toRawY(yInside));
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
- ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
-
- // Reset.
- mapper.reset(ARBITRARY_TIME);
- }
-};
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- prepareAxes(POSITION);
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Touch on center of normal display should work.
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
- processPosition(mapper, toRawX(x), toRawY(y));
- processSync(mapper);
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 0.0f));
- // Reset.
- mapper.reset(ARBITRARY_TIME);
-
- // Let physical display be different to device, and make surface and physical could be 1:1 in
- // all four orientations.
- for (int orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, DISPLAY_ORIENTATION_180,
- DISPLAY_ORIENTATION_270}) {
- halfDisplayToCenterHorizontal(orientation);
-
- const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
- }
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- 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");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse x.
- constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
- constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- 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");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
- halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
-
- const int32_t x = DISPLAY_WIDTH / 4;
- const int32_t y = DISPLAY_HEIGHT / 2;
-
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected = y;
- const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
- processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
-}
-
-TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner_NotOrientationAware) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(DISPLAY_ORIENTATION_0);
- 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");
- MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
-
- const int32_t x = 0;
- const int32_t y = 0;
-
- const int32_t xExpected = x;
- const int32_t yExpected = y;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_90);
- // expect x/y = swap x/y then reverse x.
- const int32_t xExpected90 = DISPLAY_HEIGHT - 1;
- const int32_t yExpected90 = x;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90);
-
- clearViewports();
- prepareDisplay(DISPLAY_ORIENTATION_270);
- // expect x/y = swap x/y then reverse y.
- const int32_t xExpected270 = y;
- const int32_t yExpected270 = DISPLAY_WIDTH - 1;
- processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270);
-}
-
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 =