diff --git a/src/evdev-lid.c b/src/evdev-lid.c index 9e694ba1..fe98e6e1 100644 --- a/src/evdev-lid.c +++ b/src/evdev-lid.c @@ -34,6 +34,7 @@ struct lid_switch_dispatch { enum switch_reliability reliability; bool lid_is_closed; + bool lid_is_closed_client_state; struct { struct evdev_device *keyboard; @@ -49,6 +50,20 @@ lid_dispatch(struct evdev_dispatch *dispatch) return container_of(dispatch, struct lid_switch_dispatch, base); } +static void +lid_switch_notify_toggle(struct lid_switch_dispatch *dispatch, + struct evdev_device *device, + uint64_t time) +{ + if (dispatch->lid_is_closed ^ dispatch->lid_is_closed_client_state) { + switch_notify_toggle(&device->base, + time, + LIBINPUT_SWITCH_LID, + dispatch->lid_is_closed); + dispatch->lid_is_closed_client_state = dispatch->lid_is_closed; + } +} + static void lid_switch_keyboard_event(uint64_t time, struct libinput_event *event, @@ -74,11 +89,12 @@ lid_switch_keyboard_event(uint64_t time, * regardless. */ } + /* Posting the event here means we preempt the keyboard events that + * caused us to wake up, so the lid event is always passed on before + * the key event. + */ dispatch->lid_is_closed = false; - switch_notify_toggle(&dispatch->device->base, - time, - LIBINPUT_SWITCH_LID, - dispatch->lid_is_closed); + lid_switch_notify_toggle(dispatch, dispatch->device, time); } static void @@ -116,16 +132,12 @@ lid_switch_process_switch(struct lid_switch_dispatch *dispatch, if (dispatch->lid_is_closed == is_closed) return; - lid_switch_toggle_keyboard_listener(dispatch, is_closed); dispatch->lid_is_closed = is_closed; - switch_notify_toggle(&device->base, - time, - LIBINPUT_SWITCH_LID, - dispatch->lid_is_closed); + lid_switch_notify_toggle(dispatch, device, time); break; } } @@ -198,29 +210,26 @@ lid_switch_pair_keyboard(struct evdev_device *lid_switch, { struct lid_switch_dispatch *dispatch = lid_dispatch(lid_switch->dispatch); - unsigned int bus_kbd = libevdev_get_id_bustype(keyboard->evdev); if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0) return; - /* If we already have a keyboard paired, override it if the new one - * is a serio device. Otherwise keep the current one */ - if (dispatch->keyboard.keyboard) { - if (bus_kbd != BUS_I8042) - return; + if (dispatch->keyboard.keyboard) + return; - libinput_device_remove_event_listener(&dispatch->keyboard.listener); - libinput_device_init_event_listener(&dispatch->keyboard.listener); + if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) { + dispatch->keyboard.keyboard = keyboard; + evdev_log_debug(lid_switch, + "lid: keyboard paired with %s<->%s\n", + lid_switch->devname, + keyboard->devname); + + /* We need to init the event listener now only if the reported state + * is closed. */ + if (dispatch->lid_is_closed) + lid_switch_toggle_keyboard_listener(dispatch, + dispatch->lid_is_closed); } - - dispatch->keyboard.keyboard = keyboard; - evdev_log_debug(lid_switch, - "lid: keyboard paired with %s<->%s\n", - lid_switch->devname, - keyboard->devname); - - /* We don't init the event listener yet - we don't care about - * keyboard events until the lid is closed */ } static void @@ -251,10 +260,12 @@ lid_switch_sync_initial_state(struct evdev_device *device, { struct lid_switch_dispatch *dispatch = lid_dispatch(device->dispatch); struct libevdev *evdev = device->evdev; - bool is_closed = false; dispatch->reliability = evdev_read_switch_reliability_prop(device); + dispatch->lid_is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID); + dispatch->lid_is_closed_client_state = false; + /* For the initial state sync, we depend on whether the lid switch * is reliable. If we know it's reliable, we sync as expected. * If we're not sure, we ignore the initial state and only sync on @@ -262,24 +273,11 @@ lid_switch_sync_initial_state(struct evdev_device *device, * that always have the switch in 'on' state thus don't mess up our * touchpad. */ - switch(dispatch->reliability) { - case RELIABILITY_UNKNOWN: - case RELIABILITY_WRITE_OPEN: - is_closed = false; - break; - case RELIABILITY_RELIABLE: - is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID); - break; - } - - dispatch->lid_is_closed = is_closed; - if (dispatch->lid_is_closed) { + if (dispatch->lid_is_closed && + dispatch->reliability == RELIABILITY_RELIABLE) { uint64_t time; time = libinput_now(evdev_libinput_context(device)); - switch_notify_toggle(&device->base, - time, - LIBINPUT_SWITCH_LID, - LIBINPUT_SWITCH_STATE_ON); + lid_switch_notify_toggle(dispatch, device, time); } } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 17b14bc8..e5f2a5ce 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1601,8 +1601,7 @@ tp_want_dwt(struct evdev_device *touchpad, /* For Apple touchpads, always use its internal keyboard */ if (vendor_tp == VENDOR_ID_APPLE) { return vendor_kbd == vendor_tp && - keyboard->model_flags & - EVDEV_MODEL_APPLE_INTERNAL_KEYBOARD; + keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD; } /* everything else we don't really know, so we have to assume diff --git a/src/evdev.c b/src/evdev.c index f7a019c6..02ed4f1a 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -1089,10 +1089,25 @@ evdev_tag_trackpoint(struct evdev_device *device, device->tags |= EVDEV_TAG_TRACKPOINT; } +static inline void +evdev_tag_keyboard_internal(struct evdev_device *device) +{ + device->tags |= EVDEV_TAG_INTERNAL_KEYBOARD; + device->tags &= ~EVDEV_TAG_EXTERNAL_KEYBOARD; +} + +static inline void +evdev_tag_keyboard_external(struct evdev_device *device) +{ + device->tags |= EVDEV_TAG_EXTERNAL_KEYBOARD; + device->tags &= ~EVDEV_TAG_INTERNAL_KEYBOARD; +} + static void evdev_tag_keyboard(struct evdev_device *device, struct udev_device *udev_device) { + const char *prop; int code; if (!libevdev_has_event_type(device->evdev, EV_KEY)) @@ -1105,6 +1120,21 @@ evdev_tag_keyboard(struct evdev_device *device, return; } + /* This should eventually become ID_INPUT_KEYBOARD_INTEGRATION */ + prop = udev_device_get_property_value(udev_device, + "LIBINPUT_ATTR_KEYBOARD_INTEGRATION"); + if (prop) { + if (streq(prop, "internal")) { + evdev_tag_keyboard_internal(device); + } else if (streq(prop, "external")) { + evdev_tag_keyboard_external(device); + } else { + evdev_log_info(device, + "tagged with unknown value %s\n", + prop); + } + } + device->tags |= EVDEV_TAG_KEYBOARD; } @@ -2251,7 +2281,6 @@ evdev_read_model_flags(struct evdev_device *device) MODEL(ALPS_TOUCHPAD), MODEL(SYNAPTICS_SERIAL_TOUCHPAD), MODEL(JUMPING_SEMI_MT), - MODEL(APPLE_INTERNAL_KEYBOARD), MODEL(CYBORG_RAT), MODEL(HP_STREAM11_TOUCHPAD), MODEL(LENOVO_T450_TOUCHPAD), diff --git a/src/evdev.h b/src/evdev.h index a5c11fc3..b891f906 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -73,6 +73,8 @@ enum evdev_device_tags { EVDEV_TAG_TRACKPOINT = (1 << 3), EVDEV_TAG_KEYBOARD = (1 << 4), EVDEV_TAG_LID_SWITCH = (1 << 5), + EVDEV_TAG_INTERNAL_KEYBOARD = (1 << 6), + EVDEV_TAG_EXTERNAL_KEYBOARD = (1 << 7), }; enum evdev_middlebutton_state { @@ -112,7 +114,6 @@ enum evdev_device_model { EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD = (1 << 9), EVDEV_MODEL_JUMPING_SEMI_MT = (1 << 10), EVDEV_MODEL_LENOVO_X220_TOUCHPAD_FW81 = (1 << 12), - EVDEV_MODEL_APPLE_INTERNAL_KEYBOARD = (1 << 13), EVDEV_MODEL_CYBORG_RAT = (1 << 14), EVDEV_MODEL_HP_STREAM11_TOUCHPAD = (1 << 16), EVDEV_MODEL_LENOVO_T450_TOUCHPAD= (1 << 17), diff --git a/test/test-lid.c b/test/test-lid.c index 1bd75cb6..4bf4c059 100644 --- a/test/test-lid.c +++ b/test/test-lid.c @@ -505,6 +505,56 @@ START_TEST(lid_update_hw_on_key) } END_TEST +START_TEST(lid_update_hw_on_key_closed_on_init) +{ + struct litest_device *sw = litest_current_device(); + struct libinput *li; + struct litest_device *keyboard; + struct libevdev *evdev = sw->evdev; + struct input_event ev; + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + + /* Make sure kernel state is right */ + libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_SYNC, &ev) >= 0) + ; + ck_assert(libevdev_get_event_value(evdev, EV_SW, SW_LID)); + + keyboard = litest_add_device(sw->libinput, LITEST_KEYBOARD); + + /* separate context for the right state on init */ + li = litest_create_context(); + libinput_path_add_device(li, + libevdev_uinput_get_devnode(sw->uinput)); + libinput_path_add_device(li, + libevdev_uinput_get_devnode(keyboard->uinput)); + + /* don't expect a switch waiting for us */ + while (libinput_next_event_type(li) != LIBINPUT_EVENT_NONE) { + ck_assert_int_ne(libinput_next_event_type(li), + LIBINPUT_EVENT_SWITCH_TOGGLE); + libinput_event_destroy(libinput_get_event(li)); + } + + litest_event(keyboard, EV_KEY, KEY_A, 1); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + litest_event(keyboard, EV_KEY, KEY_A, 0); + litest_event(keyboard, EV_SYN, SYN_REPORT, 0); + /* No switch event, we're still in vanilla (open) state */ + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + /* Make sure kernel state has updated */ + libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + while (libevdev_next_event(evdev, LIBEVDEV_READ_FLAG_SYNC, &ev) >= 0) + ; + ck_assert(!libevdev_get_event_value(evdev, EV_SW, SW_LID)); + + libinput_unref(li); + litest_delete_device(keyboard); +} +END_TEST + void litest_setup_tests_lid(void) { @@ -525,4 +575,5 @@ litest_setup_tests_lid(void) litest_add_no_device("lid:disable_touchpad", lid_suspend_with_touchpad); litest_add_for_device("lid:buggy", lid_update_hw_on_key, LITEST_LID_SWITCH_SURFACE3); + litest_add_for_device("lid:buggy", lid_update_hw_on_key_closed_on_init, LITEST_LID_SWITCH_SURFACE3); } diff --git a/udev/90-libinput-model-quirks.hwdb b/udev/90-libinput-model-quirks.hwdb index 3f048092..229fb1de 100644 --- a/udev/90-libinput-model-quirks.hwdb +++ b/udev/90-libinput-model-quirks.hwdb @@ -25,6 +25,12 @@ libinput:name:*Lid Switch*:dmi:*:ct10:* libinput:name:*Lid Switch*:dmi:*:ct9:* LIBINPUT_ATTR_LID_SWITCH_RELIABILITY=reliable +########################################## +# Serial keyboards are internal +########################################## +libinput:keyboard:input:b0011v* + LIBINPUT_ATTR_KEYBOARD_INTEGRATION=internal + ########################################## # ALPS ########################################## @@ -45,7 +51,7 @@ libinput:touchpad:input:b0005v05ACp* LIBINPUT_ATTR_SIZE_HINT=104x75 libinput:name:*Apple Inc. Apple Internal Keyboard*:dmi:* - LIBINPUT_MODEL_APPLE_INTERNAL_KEYBOARD=1 + LIBINPUT_ATTR_KEYBOARD_INTEGRATION=internal libinput:mouse:input:b0005v05ACp030D* LIBINPUT_MODEL_APPLE_MAGICMOUSE=1 @@ -186,6 +192,10 @@ libinput:mouse:input:b0003v046DpC408* libinput:name:*Lid Switch*:dmi:*svnMicrosoftCorporation:pnSurface3:* LIBINPUT_ATTR_LID_SWITCH_RELIABILITY=write_open +# Surface 3 Type Cover keyboard +libinput:name:*Microsoft Surface Type Cover Keyboard*:dmi:*svnMicrosoftCorporation:pnSurface3:* + LIBINPUT_ATTR_KEYBOARD_INTEGRATION=internal + ########################################## # Synaptics ########################################## diff --git a/udev/90-libinput-model-quirks.rules.in b/udev/90-libinput-model-quirks.rules.in index 8bff192d..ace2bf07 100644 --- a/udev/90-libinput-model-quirks.rules.in +++ b/udev/90-libinput-model-quirks.rules.in @@ -33,6 +33,10 @@ ENV{ID_INPUT_TOUCHPAD}=="1", \ ENV{ID_INPUT_MOUSE}=="1", \ IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:mouse:" +# libinput:touchpad: +ENV{ID_INPUT_KEYBOARD}=="1", \ + IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=libinput:keyboard:" + # libinput:name::dmi: KERNELS=="input*", \ IMPORT{builtin}="hwdb 'libinput:name:$attr{name}:$attr{[dmi/id]modalias}'" diff --git a/udev/parse_hwdb.py b/udev/parse_hwdb.py index b4f0b1bf..8ac64010 100755 --- a/udev/parse_hwdb.py +++ b/udev/parse_hwdb.py @@ -60,7 +60,7 @@ REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER)) UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_') TYPES = { - 'libinput': ('name', 'touchpad', 'mouse'), + 'libinput': ('name', 'touchpad', 'mouse', 'keyboard'), } @functools.lru_cache() @@ -117,8 +117,13 @@ def property_grammar(): Suppress('=') - Group(pressure_range('SETTINGS*')) ] + kbintegration_tags = Or(('internal', 'external')) + kbintegration = [Literal('LIBINPUT_ATTR_KEYBOARD_INTEGRATION')('NAME') - + Suppress('=') - + kbintegration_tags('VALUE')] + grammar = Or(model_props + size_props + reliability + tpkbcombo + - pressure_prop) + pressure_prop + kbintegration) return grammar