From ec0c4a65e00302c193a36c56a7f2f021c43bf183 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Fri, 23 Oct 2020 09:14:33 +1000 Subject: [PATCH libinput 3/5] quirks: add AttrEventCodeEnable as counterpoint to the disable one Currently unused, but let's get this in because we may need this very soon for broken tablets. Enabling EV_ABS axes requires an absinfo struct - we default to a simple 0-1 axis range for those as the most generic option. Anything more custom will need more custom treatment when we need it. Signed-off-by: Peter Hutterer (cherry picked from commit e3c4ff38984dfb4b51c245032c4aff5169d15257) --- doc/user/device-quirks.rst | 4 + meson.build | 1 + src/evdev.c | 26 ++++ src/quirks.c | 10 +- src/quirks.h | 1 + src/util-strings.h | 12 ++ test/litest-device-keyboard-quirked.c | 216 ++++++++++++++++++++++++++ test/litest.h | 1 + test/test-device.c | 76 +++++++++ 9 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 test/litest-device-keyboard-quirked.c diff --git a/doc/user/device-quirks.rst b/doc/user/device-quirks.rst index 0a055a35..21c43e1c 100644 --- a/doc/user/device-quirks.rst +++ b/doc/user/device-quirks.rst @@ -177,6 +177,10 @@ AttrEventCodeDisable=EV_ABS;BTN_STYLUS;EV_KEY:0x123; Disables the evdev event type/code tuples on the device. Entries may be a named event type, or a named event code, or a named event type with a hexadecimal event code, separated by a single colon. +AttrEventCodeEnable=EV_ABS;BTN_STYLUS;EV_KEY:0x123; + Enables the evdev event type/code tuples on the device. Entries may be + a named event type, or a named event code, or a named event type with a + hexadecimal event code, separated by a single colon. AttrPointingStickIntegration=internal|external Indicates the integration of the pointing stick. This is a string enum. Only needed for external pointing sticks. These are rare. diff --git a/meson.build b/meson.build index c9b53a3b..0481fa10 100644 --- a/meson.build +++ b/meson.build @@ -780,6 +780,7 @@ if get_option('tests') 'test/litest-device-ignored-mouse.c', 'test/litest-device-keyboard.c', 'test/litest-device-keyboard-all-codes.c', + 'test/litest-device-keyboard-quirked.c', 'test/litest-device-keyboard-razer-blackwidow.c', 'test/litest-device-keyboard-razer-blade-stealth.c', 'test/litest-device-keyboard-razer-blade-stealth-videoswitch.c', diff --git a/src/evdev.c b/src/evdev.c index 2f6c7447..4bcd3066 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -2083,6 +2083,32 @@ evdev_pre_configure_model_quirks(struct evdev_device *device) libevdev_disable_event_code(device->evdev, EV_MSC, MSC_TIMESTAMP); } + if (quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE_ENABLE, &t)) { + for (size_t i = 0; i < t->ntuples; i++) { + const struct input_absinfo absinfo = { + .minimum = 0, + .maximum = 1, + }; + + int type = t->tuples[i].first; + int code = t->tuples[i].second; + + if (code == EVENT_CODE_UNDEFINED) + libevdev_enable_event_type(device->evdev, type); + else + libevdev_enable_event_code(device->evdev, + type, + code, + type == EV_ABS ? &absinfo : NULL); + evdev_log_debug(device, + "quirks: enabling %s %s (%#x %#x)\n", + libevdev_event_type_get_name(type), + libevdev_event_code_get_name(type, code), + type, + code); + } + } + if (quirks_get_tuples(q, QUIRK_ATTR_EVENT_CODE_DISABLE, &t)) { for (size_t i = 0; i < t->ntuples; i++) { int type = t->tuples[i].first; diff --git a/src/quirks.c b/src/quirks.c index 45d1f554..69f41fde 100644 --- a/src/quirks.c +++ b/src/quirks.c @@ -273,6 +273,7 @@ quirk_get_name(enum quirk q) case QUIRK_ATTR_THUMB_SIZE_THRESHOLD: return "AttrThumbSizeThreshold"; case QUIRK_ATTR_MSC_TIMESTAMP: return "AttrMscTimestamp"; case QUIRK_ATTR_EVENT_CODE_DISABLE: return "AttrEventCodeDisable"; + case QUIRK_ATTR_EVENT_CODE_ENABLE: return "AttrEventCodeEnable"; default: abort(); } @@ -737,10 +738,15 @@ parse_attr(struct quirks_context *ctx, p->type = PT_STRING; p->value.s = safe_strdup(value); rc = true; - } else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE))) { + } else if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE)) || + streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_ENABLE))) { struct input_event events[32]; size_t nevents = ARRAY_LENGTH(events); - p->id = QUIRK_ATTR_EVENT_CODE_DISABLE; + if (streq(key, quirk_get_name(QUIRK_ATTR_EVENT_CODE_DISABLE))) + p->id = QUIRK_ATTR_EVENT_CODE_DISABLE; + else + p->id = QUIRK_ATTR_EVENT_CODE_ENABLE; + if (!parse_evcode_property(value, events, &nevents) || nevents == 0) goto out; diff --git a/src/quirks.h b/src/quirks.h index ee85fe3b..0b2fe9f3 100644 --- a/src/quirks.h +++ b/src/quirks.h @@ -109,6 +109,7 @@ enum quirk { QUIRK_ATTR_THUMB_SIZE_THRESHOLD, QUIRK_ATTR_MSC_TIMESTAMP, QUIRK_ATTR_EVENT_CODE_DISABLE, + QUIRK_ATTR_EVENT_CODE_ENABLE, _QUIRK_LAST_ATTR_QUIRK_, /* Guard: do not modify */ }; diff --git a/src/util-strings.h b/src/util-strings.h index 2c31ff80..80e88aeb 100644 --- a/src/util-strings.h +++ b/src/util-strings.h @@ -111,6 +111,18 @@ xasprintf(char **strp, const char *fmt, ...) return rc; } +__attribute__ ((format (printf, 2, 0))) +static inline int +xvasprintf(char **strp, const char *fmt, va_list args) +{ + int rc = 0; + rc = vasprintf(strp, fmt, args); + if ((rc == -1) && strp) + *strp = NULL; + + return rc; +} + static inline bool safe_atoi_base(const char *str, int *val, int base) { diff --git a/test/litest-device-keyboard-quirked.c b/test/litest-device-keyboard-quirked.c new file mode 100644 index 00000000..748794b2 --- /dev/null +++ b/test/litest-device-keyboard-quirked.c @@ -0,0 +1,216 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include "litest.h" +#include "litest-int.h" + +static struct input_id input_id = { + .bustype = 0x11, + .vendor = 0x1, + .product = 0x1, +}; + +static int events[] = { + EV_REL, REL_X, + EV_REL, REL_Y, + + EV_KEY, KEY_ESC, + EV_KEY, KEY_1, + EV_KEY, KEY_2, + EV_KEY, KEY_3, + EV_KEY, KEY_4, + EV_KEY, KEY_5, + EV_KEY, KEY_6, + EV_KEY, KEY_7, + EV_KEY, KEY_8, + EV_KEY, KEY_9, + EV_KEY, KEY_0, + EV_KEY, KEY_MINUS, + EV_KEY, KEY_EQUAL, + EV_KEY, KEY_BACKSPACE, + EV_KEY, KEY_TAB, + EV_KEY, KEY_Q, + EV_KEY, KEY_W, + EV_KEY, KEY_E, + EV_KEY, KEY_R, + EV_KEY, KEY_T, + EV_KEY, KEY_Y, + EV_KEY, KEY_U, + EV_KEY, KEY_I, + EV_KEY, KEY_O, + EV_KEY, KEY_P, + EV_KEY, KEY_LEFTBRACE, + EV_KEY, KEY_RIGHTBRACE, + EV_KEY, KEY_ENTER, + EV_KEY, KEY_LEFTCTRL, + EV_KEY, KEY_A, + EV_KEY, KEY_S, + EV_KEY, KEY_D, + EV_KEY, KEY_F, + EV_KEY, KEY_G, + EV_KEY, KEY_H, + EV_KEY, KEY_J, + EV_KEY, KEY_K, + EV_KEY, KEY_L, + EV_KEY, KEY_SEMICOLON, + EV_KEY, KEY_APOSTROPHE, + EV_KEY, KEY_GRAVE, + EV_KEY, KEY_LEFTSHIFT, + EV_KEY, KEY_BACKSLASH, + EV_KEY, KEY_Z, + EV_KEY, KEY_X, + EV_KEY, KEY_C, + EV_KEY, KEY_V, + EV_KEY, KEY_B, + EV_KEY, KEY_N, + EV_KEY, KEY_M, + EV_KEY, KEY_COMMA, + EV_KEY, KEY_DOT, + EV_KEY, KEY_SLASH, + EV_KEY, KEY_RIGHTSHIFT, + EV_KEY, KEY_KPASTERISK, + EV_KEY, KEY_LEFTALT, + EV_KEY, KEY_SPACE, + EV_KEY, KEY_CAPSLOCK, + EV_KEY, KEY_F1, + EV_KEY, KEY_F2, + EV_KEY, KEY_F3, + EV_KEY, KEY_F4, + EV_KEY, KEY_F5, + EV_KEY, KEY_F6, + EV_KEY, KEY_F7, + EV_KEY, KEY_F8, + EV_KEY, KEY_F9, + EV_KEY, KEY_F10, + EV_KEY, KEY_NUMLOCK, + EV_KEY, KEY_SCROLLLOCK, + EV_KEY, KEY_KP7, + EV_KEY, KEY_KP8, + EV_KEY, KEY_KP9, + EV_KEY, KEY_KPMINUS, + EV_KEY, KEY_KP4, + EV_KEY, KEY_KP5, + EV_KEY, KEY_KP6, + EV_KEY, KEY_KPPLUS, + EV_KEY, KEY_KP1, + EV_KEY, KEY_KP2, + EV_KEY, KEY_KP3, + EV_KEY, KEY_KP0, + EV_KEY, KEY_KPDOT, + EV_KEY, KEY_ZENKAKUHANKAKU, + EV_KEY, KEY_102ND, + EV_KEY, KEY_F11, + EV_KEY, KEY_F12, + EV_KEY, KEY_RO, + EV_KEY, KEY_KATAKANA, + EV_KEY, KEY_HIRAGANA, + EV_KEY, KEY_HENKAN, + EV_KEY, KEY_KATAKANAHIRAGANA, + EV_KEY, KEY_MUHENKAN, + EV_KEY, KEY_KPJPCOMMA, + EV_KEY, KEY_KPENTER, + EV_KEY, KEY_RIGHTCTRL, + EV_KEY, KEY_KPSLASH, + EV_KEY, KEY_SYSRQ, + EV_KEY, KEY_RIGHTALT, + EV_KEY, KEY_LINEFEED, + EV_KEY, KEY_HOME, + EV_KEY, KEY_UP, + EV_KEY, KEY_PAGEUP, + EV_KEY, KEY_LEFT, + EV_KEY, KEY_RIGHT, + EV_KEY, KEY_END, + EV_KEY, KEY_DOWN, + EV_KEY, KEY_PAGEDOWN, + EV_KEY, KEY_INSERT, + EV_KEY, KEY_DELETE, + EV_KEY, KEY_MACRO, + EV_KEY, KEY_MUTE, + EV_KEY, KEY_VOLUMEDOWN, + EV_KEY, KEY_VOLUMEUP, + EV_KEY, KEY_POWER, + EV_KEY, KEY_KPEQUAL, + EV_KEY, KEY_KPPLUSMINUS, + EV_KEY, KEY_PAUSE, + /* EV_KEY, KEY_SCALE, */ + EV_KEY, KEY_KPCOMMA, + EV_KEY, KEY_HANGEUL, + EV_KEY, KEY_HANJA, + EV_KEY, KEY_YEN, + EV_KEY, KEY_LEFTMETA, + EV_KEY, KEY_RIGHTMETA, + EV_KEY, KEY_COMPOSE, + EV_KEY, KEY_STOP, + + EV_KEY, KEY_MENU, + EV_KEY, KEY_CALC, + EV_KEY, KEY_SETUP, + EV_KEY, KEY_SLEEP, + EV_KEY, KEY_WAKEUP, + EV_KEY, KEY_SCREENLOCK, + EV_KEY, KEY_DIRECTION, + EV_KEY, KEY_CYCLEWINDOWS, + EV_KEY, KEY_MAIL, + EV_KEY, KEY_BOOKMARKS, + EV_KEY, KEY_COMPUTER, + EV_KEY, KEY_BACK, + EV_KEY, KEY_FORWARD, + EV_KEY, KEY_NEXTSONG, + EV_KEY, KEY_PLAYPAUSE, + EV_KEY, KEY_PREVIOUSSONG, + EV_KEY, KEY_STOPCD, + EV_KEY, KEY_HOMEPAGE, + EV_KEY, KEY_REFRESH, + EV_KEY, KEY_F14, + EV_KEY, KEY_F15, + EV_KEY, KEY_SEARCH, + EV_KEY, KEY_MEDIA, + EV_KEY, KEY_FN, + EV_LED, LED_NUML, + EV_LED, LED_CAPSL, + EV_LED, LED_SCROLLL, + -1, -1, +}; + +static const char quirk_file[] = +"[litest Quirked Keyboard enable rel]\n" +"MatchName=litest Quirked Keyboard\n" +"AttrEventCodeEnable=BTN_RIGHT;EV_KEY:0x110\n" /* BTN_LEFT */ +"\n" +"[litest Quirked keyboard disable F1-F3]\n" +"MatchName=litest Quirked Keyboard\n" +"AttrEventCodeDisable=KEY_F1;EV_KEY:0x3c;KEY_F3\n"; + +TEST_DEVICE("keyboard-quirked", + .type = LITEST_KEYBOARD_QUIRKED, + .features = LITEST_KEYS | LITEST_IGNORED, /* Only use this keyboard in specific tests */ + .interface = NULL, + + .name = "Quirked Keyboard", + .id = &input_id, + .events = events, + .absinfo = NULL, + .quirk_file = quirk_file, +) diff --git a/test/litest.h b/test/litest.h index 1f4e609d..25dc9eed 100644 --- a/test/litest.h +++ b/test/litest.h @@ -306,6 +306,7 @@ enum litest_device_type { LITEST_TABLET_MODE_UNRELIABLE, LITEST_KEYBOARD_LOGITECH_MEDIA_KEYBOARD_ELITE, LITEST_SONY_VAIO_KEYS, + LITEST_KEYBOARD_QUIRKED, }; #define LITEST_DEVICELESS -2 diff --git a/test/test-device.c b/test/test-device.c index 3a4a6b57..a50372d4 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -1419,6 +1419,81 @@ START_TEST(device_quirks_logitech_marble_mouse) } END_TEST +char *debug_messages[64] = { NULL }; + +static void +debug_log_handler(struct libinput *libinput, + enum libinput_log_priority priority, + const char *format, + va_list args) +{ + char *message; + int n; + + if (priority != LIBINPUT_LOG_PRIORITY_DEBUG) + return; + + n = xvasprintf(&message, format, args); + litest_assert_int_gt(n, 0); + + for (size_t idx = 0; idx < ARRAY_LENGTH(debug_messages); idx++) { + if (debug_messages[idx] == NULL) { + debug_messages[idx] = message; + return; + } + } + + litest_abort_msg("Out of space for debug messages"); +} + +START_TEST(device_quirks) +{ + struct libinput *li; + struct litest_device *dev; + struct libinput_device *device; + char **message; + bool disable_key_f1 = false, + enable_btn_left = false; + + li = litest_create_context(); + libinput_log_set_priority(li, LIBINPUT_LOG_PRIORITY_DEBUG); + libinput_log_set_handler(li, debug_log_handler); + dev = litest_add_device(li, LITEST_KEYBOARD_QUIRKED); + device = dev->libinput_device; + + ck_assert(libinput_device_pointer_has_button(device, + BTN_LEFT)); + ck_assert(libinput_device_pointer_has_button(dev->libinput_device, + BTN_RIGHT)); + ck_assert(!libinput_device_keyboard_has_key(dev->libinput_device, + KEY_F1)); + ck_assert(!libinput_device_keyboard_has_key(dev->libinput_device, + KEY_F2)); + ck_assert(!libinput_device_keyboard_has_key(dev->libinput_device, + KEY_F3)); + + /* Scrape the debug messages for confirmation that our quirks are + * triggered, the above checks cannot work non-key codes */ + message = debug_messages; + while (*message) { + if (strstr(*message, "disabling EV_KEY KEY_F1")) + disable_key_f1 = true; + if (strstr(*message, "enabling EV_KEY BTN_LEFT")) + enable_btn_left = true; + + message++; + } + + ck_assert(disable_key_f1); + ck_assert(enable_btn_left); + + litest_disable_log_handler(li); + + litest_delete_device(dev); + litest_destroy_context(li); +} +END_TEST + START_TEST(device_capability_at_least_one) { struct litest_device *dev = litest_current_device(); @@ -1670,6 +1745,7 @@ TEST_COLLECTION(device) litest_add_for_device("device:quirks", device_quirks_cyborg_rat_mode_button, LITEST_CYBORG_RAT); litest_add_for_device("device:quirks", device_quirks_apple_magicmouse, LITEST_MAGICMOUSE); litest_add_for_device("device:quirks", device_quirks_logitech_marble_mouse, LITEST_LOGITECH_TRACKBALL); + litest_add_no_device("device:quirks", device_quirks); litest_add("device:capability", device_capability_at_least_one, LITEST_ANY, LITEST_ANY); litest_add("device:capability", device_capability_check_invalid, LITEST_ANY, LITEST_ANY); -- 2.31.1