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

Commit ce8d4855 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Resolve associated display and pointer display in CursorInputMapper"

parents d8649e7e c13ff081
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#pragma once

#include <map>
#include <optional>
#include <set>
#include <string>

@@ -27,6 +28,15 @@ std::string constToString(const T& v) {
    return std::to_string(v);
}

/**
 * Convert an optional type to string.
 */
template <typename T>
std::string toString(const std::optional<T>& optional,
                     std::string (*toString)(const T&) = constToString) {
    return optional ? toString(*optional) : "<not set>";
}

/**
 * Convert a set of integral types to string.
 */
+35 −25
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
#include "PointerControllerInterface.h"
#include "TouchCursorInputMapperCommon.h"

#include "input/PrintTools.h"

namespace android {

// The default velocity control parameters that has no effect.
@@ -117,6 +119,7 @@ void CursorInputMapper::dump(std::string& dump) {
                         toString(mCursorScrollAccumulator.haveRelativeHWheel()));
    dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
    dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
    dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
    dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
    dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
    dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -205,21 +208,34 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration*

    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
        configurePointerCapture) {
        const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;

        mDisplayId = ADISPLAY_ID_NONE;
        if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
            // This InputDevice is associated with a viewport.
            // Only generate events for the associated display.
            const bool mismatchedPointerDisplay =
                    isPointer && (viewport->displayId != mPointerController->getDisplayId());
            mDisplayId = mismatchedPointerDisplay ? std::nullopt
                                                  : std::make_optional(viewport->displayId);
        } else if (isPointer) {
            // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
            mDisplayId = mPointerController->getDisplayId();
        }

        mOrientation = DISPLAY_ORIENTATION_0;
        const bool isOrientedDevice =
                (mParameters.orientationAware && mParameters.hasAssociatedDisplay);

        // 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
        // orientation-aware, then we need to apply the inverse rotation of the display so that
        // when the display rotation is applied later as a part of the per-window transform, we
        // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
        // rotations and report values directly from the input device.
        if (!isOrientedDevice && mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
            std::optional<DisplayViewport> internalViewport =
                    config->getDisplayViewportByType(ViewportType::INTERNAL);
            if (internalViewport) {
                mOrientation = getInverseRotation(internalViewport->orientation);
        if (!isOrientedDevice && mDisplayId &&
            mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
            if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
                mOrientation = getInverseRotation(viewport->orientation);
            }
        }

@@ -282,6 +298,11 @@ void CursorInputMapper::process(const RawEvent* rawEvent) {
}

void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
    if (!mDisplayId) {
        // Ignore events when there is no target display configured.
        return;
    }

    int32_t lastButtonState = mButtonState;
    int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
    mButtonState = currentButtonState;
@@ -327,7 +348,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {

    mPointerVelocityControl.move(when, &deltaX, &deltaY);

    int32_t displayId = ADISPLAY_ID_NONE;
    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
    if (mSource == AINPUT_SOURCE_MOUSE) {
@@ -351,7 +371,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
        displayId = mPointerController->getDisplayId();
    } else {
        // Pointer capture and navigation modes
        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
@@ -373,7 +392,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {

    // Synthesize key down from buttons if needed.
    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
                         mSource, displayId, policyFlags, lastButtonState, currentButtonState);
                         mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);

    // Send motion event.
    if (downChanged || moved || scrolled || buttonsChanged) {
@@ -394,7 +413,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
                int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                buttonState &= ~actionButton;
                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
                                             getDeviceId(), mSource, displayId, policyFlags,
                                             getDeviceId(), mSource, *mDisplayId, policyFlags,
                                             AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                             metaState, buttonState, MotionClassification::NONE,
                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -406,7 +425,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
        }

        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
                              *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
                              currentButtonState, MotionClassification::NONE,
                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
                              mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
@@ -419,7 +438,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
                int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                buttonState |= actionButton;
                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                           mSource, displayId, policyFlags,
                                           mSource, *mDisplayId, policyFlags,
                                           AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                           metaState, buttonState, MotionClassification::NONE,
                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -435,7 +454,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
        // Send hover move after UP to tell the application that the mouse is hovering now.
        if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                       mSource, displayId, policyFlags,
                                       mSource, *mDisplayId, policyFlags,
                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
                                       currentButtonState, MotionClassification::NONE,
                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -450,7 +469,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
            pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);

            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                        mSource, displayId, policyFlags,
                                        mSource, *mDisplayId, policyFlags,
                                        AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                                        currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -462,7 +481,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {

    // Synthesize key up from buttons if needed.
    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
                         displayId, policyFlags, lastButtonState, currentButtonState);
                         *mDisplayId, policyFlags, lastButtonState, currentButtonState);

    mCursorMotionAccumulator.finishSync();
    mCursorScrollAccumulator.finishSync();
@@ -477,16 +496,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod
}

std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
    if (mParameters.hasAssociatedDisplay) {
        if (mParameters.mode == Parameters::Mode::POINTER) {
            return std::make_optional(mPointerController->getDisplayId());
        } else {
            // If the device is orientationAware and not a mouse,
            // it expects to dispatch events to any display
            return std::make_optional(ADISPLAY_ID_NONE);
        }
    }
    return std::nullopt;
    return mDisplayId;
}

} // namespace android
+4 −0
Original line number Diff line number Diff line
@@ -111,6 +111,10 @@ private:
    VelocityControl mWheelXVelocityControl;
    VelocityControl mWheelYVelocityControl;

    // The display that events generated by this mapper should target. This can be set to
    // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
    // std::nullopt), all events will be ignored.
    std::optional<int32_t> mDisplayId;
    int32_t mOrientation;

    std::shared_ptr<PointerControllerInterface> mPointerController;
+93 −22
Original line number Diff line number Diff line
@@ -59,7 +59,9 @@ static constexpr nsecs_t READ_TIME = 4321;

// Arbitrary display properties.
static constexpr int32_t DISPLAY_ID = 0;
static const std::string DISPLAY_UNIQUE_ID = "local:1";
static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
static constexpr int32_t DISPLAY_WIDTH = 480;
static constexpr int32_t DISPLAY_HEIGHT = 800;
static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
@@ -94,6 +96,24 @@ static constexpr int32_t ACTION_POINTER_1_UP =
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;

using ::testing::AllOf;

MATCHER_P(WithAction, action, "InputEvent with specified action") {
    return arg.action == action;
}

MATCHER_P(WithSource, source, "InputEvent with specified source") {
    return arg.source == source;
}

MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") {
    return arg.displayId == displayId;
}

MATCHER_P2(WithCoords, x, y, "MotionEvent with specified action") {
    return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY();
}

template<typename T>
static inline T min(T a, T b) {
    return a < b ? a : b;
@@ -2909,7 +2929,6 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) {

    // Device should be disabled because it is associated with a specific display, but the
    // corresponding display is not found.
    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -2940,7 +2959,6 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) {
    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);

    const std::string DISPLAY_UNIQUE_ID = "displayUniqueId";
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                    DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID,
@@ -4255,10 +4273,14 @@ protected:
                            int32_t rotatedX, int32_t rotatedY);

    void prepareDisplay(int32_t orientation) {
        const std::string uniqueId = "local:0";
        const ViewportType viewportType = ViewportType::INTERNAL;
        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                orientation, uniqueId, NO_PORT, viewportType);
        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation,
                                     DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
    }

    void prepareSecondaryDisplay() {
        setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
                                     DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT,
                                     ViewportType::EXTERNAL);
    }

    static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y,
@@ -4535,6 +4557,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
}

TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) {
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
    addConfigurationProperty("cursor.mode", "navigation");
    // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
    // need to be rotated.
@@ -4553,11 +4576,13 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotion
}

TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
    addConfigurationProperty("cursor.mode", "navigation");
    // Since InputReader works in the un-rotated coordinate space, only devices that are not
    // orientation-aware are affected by display rotation.
    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();

    clearViewports();
    prepareDisplay(DISPLAY_ORIENTATION_0);
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1,  1));
@@ -4568,6 +4593,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0, -1,  0));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));

    clearViewports();
    prepareDisplay(DISPLAY_ORIENTATION_90);
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1, -1,  0));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1,  1));
@@ -4578,6 +4604,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  0, -1));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1, -1));

    clearViewports();
    prepareDisplay(DISPLAY_ORIENTATION_180);
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0, -1));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1, -1, -1));
@@ -4588,6 +4615,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  0,  1,  0));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1,  1, -1));

    clearViewports();
    prepareDisplay(DISPLAY_ORIENTATION_270);
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  1,  0));
    ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  1,  1,  1, -1));
@@ -5084,33 +5112,76 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
    ASSERT_EQ(20, args.pointerCoords[0].getY());
}

TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();

    // Setup for second display.
    constexpr int32_t SECOND_DISPLAY_ID = 1;
    const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
    mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
                                    true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
                                    ViewportType::EXTERNAL);
    mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
    // Set up the default display.
    prepareDisplay(DISPLAY_ORIENTATION_90);

    // Set up the secondary display as the display on which the pointer should be shown.
    // The InputDevice is not associated with any display.
    prepareSecondaryDisplay();
    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);

    mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
    mFakePointerController->setPosition(100, 200);
    mFakePointerController->setButtonState(0);

    NotifyMotionArgs args;
    // Ensure input events are generated for the secondary display.
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
    ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
    ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
            110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
                  WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
}

TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();

    // Set up the default display.
    prepareDisplay(DISPLAY_ORIENTATION_90);

    // Set up the secondary display as the display on which the pointer should be shown,
    // and associate the InputDevice with the secondary display.
    prepareSecondaryDisplay();
    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);

    mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
    mFakePointerController->setPosition(100, 200);
    mFakePointerController->setButtonState(0);

    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
            AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
                  WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))));
    ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f));
    ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
}

TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();

    // Set up the default display as the display on which the pointer should be shown.
    prepareDisplay(DISPLAY_ORIENTATION_90);
    mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);

    // Associate the InputDevice with the secondary display.
    prepareSecondaryDisplay();
    mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);

    // The mapper should not generate any events because it is associated with a display that is
    // different from the pointer display.
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}

// --- TouchInputMapperTest ---