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

Commit e1a41a84 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Generate key events for stylus button presses

Ensure all devices that report stylus buttons are categorized as a
keyboard. This means that KeyboardInputMapper will get a chance to
handle the events from these devices.

Then, we allow KeyboardInputMapper to process stylus button events.
Since stylus button (e.g. BTN_STYLUS) are mapped to Android key codes in
the Generic.kl file, KeyEvents with the appropriate key codes will be
generated for these button presses.

Bug: 246394583
Test: atest inputflinger_tests
Change-Id: I92d4a60f23f98f9d239edf1f4dd400e6e528e350
parent 2197cb4c
Loading
Loading
Loading
Loading
+6 −8
Original line number Original line Diff line number Diff line
@@ -2171,13 +2171,15 @@ void EventHub::openDeviceLocked(const std::string& devicePath) {
    device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
    device->readDeviceBitMask(EVIOCGBIT(EV_MSC, 0), device->mscBitmask);
    device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);
    device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask);


    // See if this is a keyboard.  Ignore everything in the button range except for
    // See if this is a device with keys. This could be full keyboard, or other devices like
    // joystick and gamepad buttons which are handled like keyboards for the most part.
    // gamepads, joysticks, and styluses with buttons that should generate key presses.
    bool haveKeyboardKeys =
    bool haveKeyboardKeys =
            device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
            device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
    bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
    bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
            device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
            device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI);
    if (haveKeyboardKeys || haveGamepadButtons) {
    bool haveStylusButtons = device->keyBitmask.test(BTN_STYLUS) ||
            device->keyBitmask.test(BTN_STYLUS2) || device->keyBitmask.test(BTN_STYLUS3);
    if (haveKeyboardKeys || haveGamepadButtons || haveStylusButtons) {
        device->classes |= InputDeviceClass::KEYBOARD;
        device->classes |= InputDeviceClass::KEYBOARD;
    }
    }


@@ -2208,14 +2210,10 @@ void EventHub::openDeviceLocked(const std::string& devicePath) {
    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
               device->absBitmask.test(ABS_Y)) {
               device->absBitmask.test(ABS_Y)) {
        device->classes |= InputDeviceClass::TOUCH;
        device->classes |= InputDeviceClass::TOUCH;
        // Is this a BT stylus?
        // Is this a stylus that reports contact/pressure independently of touch coordinates?
    } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
    } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) &&
               !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
               !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
        device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
        device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
        // Keyboard will try to claim some of the buttons but we really want to reserve those so we
        // can fuse it with the touch screen data, so just take them back. Note this means an
        // external stylus cannot also be a keyboard device.
        device->classes &= ~InputDeviceClass::KEYBOARD;
    }
    }


    // See if this device is a joystick.
    // See if this device is a joystick.
+6 −5
Original line number Original line Diff line number Diff line
@@ -53,10 +53,11 @@ static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
    return keyCode;
    return keyCode;
}
}


static bool isKeyboardOrGamepadKey(int32_t scanCode) {
static bool isSupportedScanCode(int32_t scanCode) {
    return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
    // KeyboardInputMapper handles keys from keyboards, gamepads, and styluses.
            (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
    return scanCode < BTN_MOUSE || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI) ||
            (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
            scanCode == BTN_STYLUS || scanCode == BTN_STYLUS2 || scanCode == BTN_STYLUS3 ||
            scanCode >= BTN_WHEEL;
}
}


static bool isMediaKey(int32_t keyCode) {
static bool isMediaKey(int32_t keyCode) {
@@ -195,7 +196,7 @@ std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
            int32_t usageCode = mCurrentHidUsage;
            int32_t usageCode = mCurrentHidUsage;
            mCurrentHidUsage = 0;
            mCurrentHidUsage = 0;


            if (isKeyboardOrGamepadKey(scanCode)) {
            if (isSupportedScanCode(scanCode)) {
                out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
                out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
                                  scanCode, usageCode);
                                  scanCode, usageCode);
            }
            }
+54 −12
Original line number Original line Diff line number Diff line
@@ -2391,18 +2391,11 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) {
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
    ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
    ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());


    // Find the test device by its name.
    const auto device = findDeviceByName(keyboard->getName());
    const std::vector<InputDeviceInfo> inputDevices = mFakePolicy->getInputDevices();
    ASSERT_TRUE(device.has_value());
    const auto& it =
    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());
            std::find_if(inputDevices.begin(), inputDevices.end(),
    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources());
                         [&keyboard](const InputDeviceInfo& info) {
    ASSERT_EQ(0U, device->getMotionRanges().size());
                             return info.getIdentifier().name == keyboard->getName();
                         });

    ASSERT_NE(it, inputDevices.end());
    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType());
    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources());
    ASSERT_EQ(0U, it->getMotionRanges().size());


    keyboard.reset();
    keyboard.reset();
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2437,6 +2430,41 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) {
    ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
    ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
}
}


TEST_F(InputReaderIntegrationTest, ExternalStylusesButtons) {
    std::unique_ptr<UinputExternalStylus> stylus = createUinputDevice<UinputExternalStylus>();
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());

    const auto device = findDeviceByName(stylus->getName());
    ASSERT_TRUE(device.has_value());

    // An external stylus with buttons should be recognized as a keyboard.
    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, device->getSources())
            << "Unexpected source " << inputEventSourceToString(device->getSources()).c_str();
    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, device->getKeyboardType());

    const auto DOWN =
            AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD));
    const auto UP = AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD));

    stylus->pressAndReleaseKey(BTN_STYLUS);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));

    stylus->pressAndReleaseKey(BTN_STYLUS2);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_SECONDARY))));

    stylus->pressAndReleaseKey(BTN_STYLUS3);
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(DOWN, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(UP, WithKeyCode(AKEYCODE_STYLUS_BUTTON_TERTIARY))));
}

/**
/**
 * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
 * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
 * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
 * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
@@ -2772,6 +2800,20 @@ TEST_F(TouchIntegrationTest, NotifiesPolicyWhenStylusGestureStarted) {
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertStylusGestureNotified(mDeviceInfo.getId()));
}
}


TEST_F(TouchIntegrationTest, StylusButtonsGenerateKeyEvents) {
    mDevice->sendKey(BTN_STYLUS, 1);
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithSource(AINPUT_SOURCE_KEYBOARD),
                  WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));

    mDevice->sendKey(BTN_STYLUS, 0);
    mDevice->sendSync();
    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(
            AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithSource(AINPUT_SOURCE_KEYBOARD),
                  WithKeyCode(AKEYCODE_STYLUS_BUTTON_PRIMARY))));
}

// --- InputDeviceTest ---
// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
class InputDeviceTest : public testing::Test {
protected:
protected:
+6 −0
Original line number Original line Diff line number Diff line
@@ -59,6 +59,12 @@ void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
            assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
            assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
}
}


void TestInputListener::assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher) {
    NotifyKeyArgs outEventArgs;
    ASSERT_NO_FATAL_FAILURE(assertNotifyKeyWasCalled(&outEventArgs));
    ASSERT_THAT(outEventArgs, matcher);
}

void TestInputListener::assertNotifyKeyWasNotCalled() {
void TestInputListener::assertNotifyKeyWasNotCalled() {
    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -44,6 +44,8 @@ public:


    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);
    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);


    void assertNotifyKeyWasCalled(const ::testing::Matcher<NotifyKeyArgs>& matcher);

    void assertNotifyKeyWasNotCalled();
    void assertNotifyKeyWasNotCalled();


    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
Loading