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

Commit 935fb2d6 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Android (Google) Code Review
Browse files

Merge changes Ief22aac9,I92d4a60f,Ia57d1c88,I8745de89

* changes:
  Add additional ways to recognize an external stylus
  Generate key events for stylus button presses
  KeyboardInputMapper: Miscelaneous code cleanup
  KeyboardInputMapper: Remove unused "stem" key mapping
parents 15c01e27 a3621859
Loading
Loading
Loading
Loading
+27 −9
Original line number Diff line number Diff line
@@ -149,6 +149,14 @@ static std::string sha1(const std::string& in) {
    return out;
}

/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = {
        AKEYCODE_STYLUS_BUTTON_PRIMARY,
        AKEYCODE_STYLUS_BUTTON_SECONDARY,
        AKEYCODE_STYLUS_BUTTON_TERTIARY,
        AKEYCODE_STYLUS_BUTTON_TAIL,
};

/**
 * Return true if name matches "v4l-touch*"
 */
@@ -2171,13 +2179,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;
    }

@@ -2187,11 +2197,13 @@ void EventHub::openDeviceLocked(const std::string& devicePath) {
        device->classes |= InputDeviceClass::CURSOR;
    }

    // See if this is a rotary encoder type device.
    // See if the device is specially configured to be of a certain type.
    std::string deviceType;
    if (device->configuration && device->configuration->tryGetProperty("device.type", deviceType)) {
        if (deviceType == "rotaryEncoder") {
            device->classes |= InputDeviceClass::ROTARY_ENCODER;
        } else if (deviceType == "externalStylus") {
            device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
        }
    }

@@ -2208,14 +2220,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.
@@ -2300,6 +2308,16 @@ void EventHub::openDeviceLocked(const std::string& devicePath) {
                break;
            }
        }

        // See if this device has any stylus buttons that we would want to fuse with touch data.
        if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) {
            for (int32_t keycode : STYLUS_BUTTON_KEYCODES) {
                if (device->hasKeycodeLocked(keycode)) {
                    device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
                    break;
                }
            }
        }
    }

    // If the device isn't recognized as something we handle, don't monitor it.
+81 −135
Original line number Diff line number Diff line
@@ -24,19 +24,8 @@ namespace android {

// --- Static Definitions ---

static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
                                           const int32_t map[][4], size_t mapSize) {
    if (orientation != DISPLAY_ORIENTATION_0) {
        for (size_t i = 0; i < mapSize; i++) {
            if (value == map[i][0]) {
                return map[i][orientation];
            }
        }
    }
    return value;
}

static const int32_t keyCodeRotationMap[][4] = {
static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
    static constexpr int32_t KEYCODE_ROTATION_MAP[][4] = {
            // key codes enumerated counter-clockwise with the original (unrotated) key first
            // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
            {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT},
@@ -53,38 +42,52 @@ static const int32_t keyCodeRotationMap[][4] = {
             AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP},
    };

static const size_t keyCodeRotationMapSize =
        sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);

static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2],
                             size_t mapSize) {
    if (orientation == DISPLAY_ORIENTATION_180) {
        for (size_t i = 0; i < mapSize; i++) {
            if (value == map[i][0]) {
                return map[i][1];
    LOG_ALWAYS_FATAL_IF(orientation < 0 || orientation > 3, "Invalid orientation: %d", orientation);
    if (orientation != DISPLAY_ORIENTATION_0) {
        for (const auto& rotation : KEYCODE_ROTATION_MAP) {
            if (rotation[DISPLAY_ORIENTATION_0] == keyCode) {
                return rotation[orientation];
            }
        }
    }
    return value;
    return keyCode;
}

// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X
static int32_t stemKeyRotationMap[][2] = {
        // key codes enumerated with the original (unrotated) key first
        // no rotation,           180 degree rotation
        {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY},
        {AKEYCODE_STEM_1, AKEYCODE_STEM_1},
        {AKEYCODE_STEM_2, AKEYCODE_STEM_2},
        {AKEYCODE_STEM_3, AKEYCODE_STEM_3},
};

static const size_t stemKeyRotationMapSize =
        sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]);
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 int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
    keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize);
    return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap,
                                       keyCodeRotationMapSize);
static bool isMediaKey(int32_t keyCode) {
    switch (keyCode) {
        case AKEYCODE_MEDIA_PLAY:
        case AKEYCODE_MEDIA_PAUSE:
        case AKEYCODE_MEDIA_PLAY_PAUSE:
        case AKEYCODE_MUTE:
        case AKEYCODE_HEADSETHOOK:
        case AKEYCODE_MEDIA_STOP:
        case AKEYCODE_MEDIA_NEXT:
        case AKEYCODE_MEDIA_PREVIOUS:
        case AKEYCODE_MEDIA_REWIND:
        case AKEYCODE_MEDIA_RECORD:
        case AKEYCODE_MEDIA_FAST_FORWARD:
        case AKEYCODE_MEDIA_SKIP_FORWARD:
        case AKEYCODE_MEDIA_SKIP_BACKWARD:
        case AKEYCODE_MEDIA_STEP_FORWARD:
        case AKEYCODE_MEDIA_STEP_BACKWARD:
        case AKEYCODE_MEDIA_AUDIO_TRACK:
        case AKEYCODE_VOLUME_UP:
        case AKEYCODE_VOLUME_DOWN:
        case AKEYCODE_VOLUME_MUTE:
        case AKEYCODE_TV_AUDIO_DESCRIPTION:
        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
            return true;
        default:
            return false;
    }
}

// --- KeyboardInputMapper ---
@@ -93,8 +96,6 @@ KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint
                                         int32_t keyboardType)
      : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}

KeyboardInputMapper::~KeyboardInputMapper() {}

uint32_t KeyboardInputMapper::getSources() const {
    return mSource;
}
@@ -130,7 +131,7 @@ void KeyboardInputMapper::dump(std::string& dump) {
}

std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
        nsecs_t when, const InputReaderConfiguration* config) {
        const InputReaderConfiguration* config) {
    if (getDeviceContext().getAssociatedViewport()) {
        return getDeviceContext().getAssociatedViewport();
    }
@@ -154,35 +155,16 @@ std::list<NotifyArgs> KeyboardInputMapper::configure(nsecs_t when,
    }

    if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
        mViewport = findViewport(when, config);
        mViewport = findViewport(config);
    }
    return out;
}

static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) {
    int32_t mapped = 0;
    if (config.tryGetProperty(property, mapped) && mapped > 0) {
        for (size_t i = 0; i < stemKeyRotationMapSize; i++) {
            if (stemKeyRotationMap[i][0] == keyCode) {
                stemKeyRotationMap[i][1] = mapped;
                return;
            }
        }
    }
}

void KeyboardInputMapper::configureParameters() {
    mParameters.orientationAware = false;
    const PropertyMap& config = getDeviceContext().getConfiguration();
    config.tryGetProperty("keyboard.orientationAware", mParameters.orientationAware);

    if (mParameters.orientationAware) {
        mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary");
        mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1");
        mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2");
        mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3");
    }

    mParameters.handlesKeyRepeat = false;
    config.tryGetProperty("keyboard.handlesKeyRepeat", mParameters.handlesKeyRepeat);

@@ -190,7 +172,7 @@ void KeyboardInputMapper::configureParameters() {
    config.tryGetProperty("keyboard.doNotWakeByDefault", mParameters.doNotWakeByDefault);
}

void KeyboardInputMapper::dumpParameters(std::string& dump) {
void KeyboardInputMapper::dumpParameters(std::string& dump) const {
    dump += INDENT3 "Parameters:\n";
    dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
    dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat));
@@ -214,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);
            }
@@ -235,41 +217,6 @@ std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
    return out;
}

bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
    return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
            (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
            (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
}

bool KeyboardInputMapper::isMediaKey(int32_t keyCode) {
    switch (keyCode) {
        case AKEYCODE_MEDIA_PLAY:
        case AKEYCODE_MEDIA_PAUSE:
        case AKEYCODE_MEDIA_PLAY_PAUSE:
        case AKEYCODE_MUTE:
        case AKEYCODE_HEADSETHOOK:
        case AKEYCODE_MEDIA_STOP:
        case AKEYCODE_MEDIA_NEXT:
        case AKEYCODE_MEDIA_PREVIOUS:
        case AKEYCODE_MEDIA_REWIND:
        case AKEYCODE_MEDIA_RECORD:
        case AKEYCODE_MEDIA_FAST_FORWARD:
        case AKEYCODE_MEDIA_SKIP_FORWARD:
        case AKEYCODE_MEDIA_SKIP_BACKWARD:
        case AKEYCODE_MEDIA_STEP_FORWARD:
        case AKEYCODE_MEDIA_STEP_BACKWARD:
        case AKEYCODE_MEDIA_AUDIO_TRACK:
        case AKEYCODE_VOLUME_UP:
        case AKEYCODE_VOLUME_DOWN:
        case AKEYCODE_VOLUME_MUTE:
        case AKEYCODE_TV_AUDIO_DESCRIPTION:
        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
        case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
            return true;
    }
    return false;
}

std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down,
                                                      int32_t scanCode, int32_t usageCode) {
    std::list<NotifyArgs> out;
@@ -285,6 +232,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read
    }

    nsecs_t downTime = when;
    std::optional<size_t> keyDownIndex = findKeyDownIndex(scanCode);
    if (down) {
        // Rotate key codes according to orientation if needed.
        if (mParameters.orientationAware) {
@@ -292,11 +240,10 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read
        }

        // Add key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
        if (keyDownIndex) {
            // key repeat, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            downTime = mKeyDowns[keyDownIndex].downTime;
            keyCode = mKeyDowns[*keyDownIndex].keyCode;
            downTime = mKeyDowns[*keyDownIndex].downTime;
        } else {
            // key down
            if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
@@ -315,12 +262,11 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read
        }
    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
        if (keyDownIndex) {
            // key up, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            downTime = mKeyDowns[keyDownIndex].downTime;
            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
            keyCode = mKeyDowns[*keyDownIndex].keyCode;
            downTime = mKeyDowns[*keyDownIndex].downTime;
            mKeyDowns.erase(mKeyDowns.begin() + *keyDownIndex);
        } else {
            // key was not actually down
            ALOGI("Dropping key up from device %s because the key was not down.  "
@@ -353,22 +299,22 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read
        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
    }

    out.push_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                                getDisplayId(), policyFlags,
    out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
                                   mSource, getDisplayId(), policyFlags,
                                   down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                                   AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState,
                                   downTime));
    return out;
}

ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) {
std::optional<size_t> KeyboardInputMapper::findKeyDownIndex(int32_t scanCode) {
    size_t n = mKeyDowns.size();
    for (size_t i = 0; i < n; i++) {
        if (mKeyDowns[i].scanCode == scanCode) {
            return i;
        }
    }
    return -1;
    return {};
}

int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -481,7 +427,7 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) {
    std::list<NotifyArgs> out;
    size_t n = mKeyDowns.size();
    for (size_t i = 0; i < n; i++) {
        out.push_back(NotifyKeyArgs(getContext()->getNextId(), when,
        out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
                                       systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
                                       getDisplayId(), 0 /*policyFlags*/, AKEY_EVENT_ACTION_UP,
                                       AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED,
+22 −26
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ namespace android {
class KeyboardInputMapper : public InputMapper {
public:
    KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
    virtual ~KeyboardInputMapper();
    ~KeyboardInputMapper() override = default;

    uint32_t getSources() const override;
    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
@@ -47,58 +47,54 @@ public:

private:
    // The current viewport.
    std::optional<DisplayViewport> mViewport;
    std::optional<DisplayViewport> mViewport{};

    struct KeyDown {
        nsecs_t downTime;
        int32_t keyCode;
        int32_t scanCode;
        nsecs_t downTime{};
        int32_t keyCode{};
        int32_t scanCode{};
    };

    uint32_t mSource;
    int32_t mKeyboardType;
    uint32_t mSource{};
    int32_t mKeyboardType{};

    std::vector<KeyDown> mKeyDowns; // keys that are down
    int32_t mMetaState;
    std::vector<KeyDown> mKeyDowns{}; // keys that are down
    int32_t mMetaState{};

    int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none
    int32_t mCurrentHidUsage{}; // most recent HID usage seen this packet, or 0 if none

    struct LedState {
        bool avail; // led is available
        bool on;    // we think the led is currently on
        bool avail{}; // led is available
        bool on{};    // we think the led is currently on
    };
    LedState mCapsLockLedState;
    LedState mNumLockLedState;
    LedState mScrollLockLedState;
    LedState mCapsLockLedState{};
    LedState mNumLockLedState{};
    LedState mScrollLockLedState{};

    // Immutable configuration parameters.
    struct Parameters {
        bool orientationAware;
        bool handlesKeyRepeat;
        bool doNotWakeByDefault;
    } mParameters;
        bool orientationAware{};
        bool handlesKeyRepeat{};
        bool doNotWakeByDefault{};
    } mParameters{};

    void configureParameters();
    void dumpParameters(std::string& dump);
    void dumpParameters(std::string& dump) const;

    int32_t getOrientation();
    int32_t getDisplayId();

    bool isKeyboardOrGamepadKey(int32_t scanCode);
    bool isMediaKey(int32_t keyCode);

    [[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
                                                   int32_t scanCode, int32_t usageCode);

    bool updateMetaStateIfNeeded(int32_t keyCode, bool down);

    ssize_t findKeyDown(int32_t scanCode);
    std::optional<size_t> findKeyDownIndex(int32_t scanCode);

    void resetLedState();
    void initializeLedState(LedState& ledState, int32_t led);
    void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
    std::optional<DisplayViewport> findViewport(nsecs_t when,
                                                const InputReaderConfiguration* config);
    std::optional<DisplayViewport> findViewport(const InputReaderConfiguration* config);
    [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
};

+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 also be recognized as a keyboard.
    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_STYLUS, 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."));
}
Loading