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 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(EVIOCGPROP(0), device->propBitmask);

    // See if this is a keyboard.  Ignore everything in the button range except for
    // joystick and gamepad buttons which are handled like keyboards for the most part.
    // See if this is a device with keys. This could be full keyboard, or other devices like
    // gamepads, joysticks, and styluses with buttons that should generate key presses.
    bool haveKeyboardKeys =
            device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1);
    bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) ||
            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;
    }

@@ -2208,14 +2210,10 @@ void EventHub::openDeviceLocked(const std::string& devicePath) {
    } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) &&
               device->absBitmask.test(ABS_Y)) {
        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)) &&
               !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) {
        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.
+6 −5
Original line number Diff line number Diff line
@@ -53,10 +53,11 @@ static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
    return keyCode;
}

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

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

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

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

    keyboard.reset();
    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
@@ -2437,6 +2430,41 @@ TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) {
    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
 * 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()));
}

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 ---
class InputDeviceTest : public testing::Test {
protected:
+6 −0
Original line number Diff line number Diff line
@@ -59,6 +59,12 @@ void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
            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() {
    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}
+2 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ public:

    void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr);

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

    void assertNotifyKeyWasNotCalled();

    void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr);
Loading