Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 675f25ab authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

TouchInputMapper: Perform physical frame hit test in rotated display space

The physical frame is specified in DisplayViewport in the rotated
display space. The frame is not symmetric along the X or Y axes because
the right and bottom edges are outside of the frame. For example, for a
physical frame with bounds [left, top, right, bottom], any point with an
x value of `right` or y value of `bottom` is outside the frame, whereas
points in the frame could contain an x value of `left` and a y value of
`top`.

To address this asymmetry, we must perform any hit tests in the intended
coordinate space, which in this case is that of the rotated display.

This logic is tested again in following CLs.

Bug: 236798672
Bug: 257118693
Test: atest inputflinger_tests
Change-Id: I403a686c437aa53cb808910b296a7251e0e96321
parent ea31d4f8
Loading
Loading
Loading
Loading
+21 −32
Original line number Diff line number Diff line
@@ -51,9 +51,8 @@ static std::string toString(const ui::Size& size) {
    return base::StringPrintf("%dx%d", size.width, size.height);
}

static bool isPointInRect(const Rect& rect, int32_t x, int32_t y) {
    // Consider all four sides as "inclusive".
    return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
static bool isPointInRect(const Rect& rect, vec2 p) {
    return p.x >= rect.left && p.x < rect.right && p.y >= rect.top && p.y < rect.bottom;
}

template <typename T>
@@ -81,29 +80,12 @@ inline static int32_t signExtendNybble(int32_t value) {
    return value >= 8 ? value - 16 : value;
}

static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
        const DisplayViewport& viewport) {
static ui::Size getNaturalDisplaySize(const DisplayViewport& viewport) {
    ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
    if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
        std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
    }

    ui::Transform rotate(ui::Transform::toRotationFlags(viewport.orientation),
                         rotatedDisplaySize.width, rotatedDisplaySize.height);

    Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
                       viewport.physicalBottom};
    physicalFrame = rotate.transform(physicalFrame);

    LOG_ALWAYS_FATAL_IF(!physicalFrame.isValid());
    if (physicalFrame.isEmpty()) {
        ALOGE("Viewport is not set properly: %s", viewport.toString().c_str());
        physicalFrame.right =
                physicalFrame.left + (physicalFrame.width() == 0 ? 1 : physicalFrame.width());
        physicalFrame.bottom =
                physicalFrame.top + (physicalFrame.height() == 0 ? 1 : physicalFrame.height());
    }
    return {rotatedDisplaySize, physicalFrame};
    return rotatedDisplaySize;
}

// --- RawPointerData ---
@@ -856,7 +838,7 @@ void TouchInputMapper::initializeOrientedRanges() {
    }
}

ui::Transform TouchInputMapper::computeInputTransform() const {
void TouchInputMapper::computeInputTransforms() {
    const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};

    ui::Size rotatedRawSize = rawSize;
@@ -881,7 +863,13 @@ ui::Transform TouchInputMapper::computeInputTransform() const {
    const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height;
    scaleToDisplay.set(xScale, 0, 0, yScale);

    return (scaleToDisplay * (rotate * undoRawOffset));
    mRawToDisplay = (scaleToDisplay * (rotate * undoRawOffset));

    // Calculate the transform that takes raw coordinates to the rotated display space.
    ui::Transform displayToRotatedDisplay;
    displayToRotatedDisplay.set(ui::Transform::toRotationFlags(-mViewport.orientation),
                                mViewport.deviceWidth, mViewport.deviceHeight);
    mRawToRotatedDisplay = displayToRotatedDisplay * mRawToDisplay;
}

void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
@@ -955,7 +943,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)
        if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
            const auto oldDisplayBounds = mDisplayBounds;

            std::tie(mDisplayBounds, mPhysicalFrameInDisplay) = getNaturalDisplayInfo(mViewport);
            mDisplayBounds = getNaturalDisplaySize(mViewport);
            mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop,
                                              mViewport.physicalRight, mViewport.physicalBottom};

            // InputReader works in the un-rotated display coordinate space, so we don't need to do
            // anything if the device is already orientation-aware. If the device is not
@@ -972,13 +962,14 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)

            // Apply the input device orientation for the device.
            mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
            mRawToDisplay = computeInputTransform();
            computeInputTransforms();
        } else {
            mDisplayBounds = rawSize;
            mPhysicalFrameInDisplay = Rect{mDisplayBounds};
            mPhysicalFrameInRotatedDisplay = Rect{mDisplayBounds};
            mInputDeviceOrientation = ui::ROTATION_0;
            mRawToDisplay.reset();
            mRawToDisplay.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
            mRawToRotatedDisplay = mRawToDisplay;
        }
    }

@@ -1061,7 +1052,8 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded)
void TouchInputMapper::dumpDisplay(std::string& dump) {
    dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
    dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str());
    dump += StringPrintf(INDENT3 "PhysicalFrame: %s\n", toString(mPhysicalFrameInDisplay).c_str());
    dump += StringPrintf(INDENT3 "PhysicalFrameInRotatedDisplay: %s\n",
                         toString(mPhysicalFrameInRotatedDisplay).c_str());
    dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
}

@@ -3822,12 +3814,9 @@ std::list<NotifyArgs> TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTi
}

bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const {
    const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
    const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;

    return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
            y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
            isPointInRect(mPhysicalFrameInDisplay, xScaled, yScaled);
            isPointInRect(mPhysicalFrameInRotatedDisplay, mRawToRotatedDisplay.transform(x, y));
}

const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
+8 −3
Original line number Diff line number Diff line
@@ -410,9 +410,9 @@ private:
    // Always starts at (0, 0).
    ui::Size mDisplayBounds{ui::kInvalidSize};

    // The physical frame is the rectangle in the natural display's coordinate space that maps to
    // The physical frame is the rectangle in the rotated display's coordinate space that maps to
    // the logical display frame.
    Rect mPhysicalFrameInDisplay{Rect::INVALID_RECT};
    Rect mPhysicalFrameInRotatedDisplay{Rect::INVALID_RECT};

    // The orientation of the input device relative to that of the display panel. It specifies
    // the rotation of the input device coordinates required to produce the display panel
@@ -423,6 +423,11 @@ private:
    // coordinate space. InputReader generates events in the un-rotated display's coordinate space.
    ui::Transform mRawToDisplay;

    // The transform that maps the input device's raw coordinate space to the rotated display's
    // coordinate space. This used to perform hit-testing of raw events with the physical frame in
    // the rotated coordinate space. See mPhysicalFrameInRotatedDisplay.
    ui::Transform mRawToRotatedDisplay;

    // Translation and scaling factors, orientation-independent.
    float mXScale;
    float mXPrecision;
@@ -817,7 +822,7 @@ private:

    static void assignPointerIds(const RawState& last, RawState& current);

    ui::Transform computeInputTransform() const;
    void computeInputTransforms();

    void configureDeviceType();
};
+55 −0
Original line number Diff line number Diff line
@@ -5740,6 +5740,61 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationSpecified_RotatesMotio
    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled());
}

TEST_F(SingleTouchInputMapperTest, Process_IgnoresTouchesOutsidePhysicalFrame) {
    addConfigurationProperty("touch.deviceType", "touchScreen");
    prepareButtons();
    prepareAxes(POSITION);
    addConfigurationProperty("touch.orientationAware", "1");
    prepareDisplay(ui::ROTATION_0);
    auto& mapper = addMapperAndConfigure<SingleTouchInputMapper>();

    // Set a physical frame in the display viewport.
    auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
    viewport->physicalLeft = 20;
    viewport->physicalTop = 600;
    viewport->physicalRight = 30;
    viewport->physicalBottom = 610;
    mFakePolicy->updateViewport(*viewport);
    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);

    // Start the touch.
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
    processSync(mapper);

    // Expect all input starting outside the physical frame to be ignored.
    const std::array<Point, 6> outsidePoints = {
            {{0, 0}, {19, 605}, {31, 605}, {25, 599}, {25, 611}, {DISPLAY_WIDTH, DISPLAY_HEIGHT}}};
    for (const auto& p : outsidePoints) {
        processMove(mapper, toRawX(p.x), toRawY(p.y));
        processSync(mapper);
        EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
    }

    // Move the touch into the physical frame.
    processMove(mapper, toRawX(25), toRawY(605));
    processSync(mapper);
    NotifyMotionArgs args;
    EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
    EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
    EXPECT_NEAR(25, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
    EXPECT_NEAR(605, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);

    // Once the touch down is reported, continue reporting input, even if it is outside the frame.
    for (const auto& p : outsidePoints) {
        processMove(mapper, toRawX(p.x), toRawY(p.y));
        processSync(mapper);
        EXPECT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
        EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
        EXPECT_NEAR(p.x, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
        EXPECT_NEAR(p.y, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
    }

    processUp(mapper);
    processSync(mapper);
    EXPECT_NO_FATAL_FAILURE(
            mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
}

TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
    addConfigurationProperty("touch.deviceType", "touchScreen");
    prepareDisplay(ui::ROTATION_0);