diff --git a/src/evdev-lid.c b/src/evdev-lid.c index 3cc287ca..9029838e 100644 --- a/src/evdev-lid.c +++ b/src/evdev-lid.c @@ -30,10 +30,56 @@ struct lid_switch_dispatch { struct evdev_dispatch base; + struct evdev_device *device; bool lid_is_closed; + + struct { + struct evdev_device *keyboard; + struct libinput_event_listener listener; + } keyboard; }; +static void +lid_switch_keyboard_event(uint64_t time, + struct libinput_event *event, + void *data) +{ + struct lid_switch_dispatch *dispatch = + (struct lid_switch_dispatch*)data; + + if (!dispatch->lid_is_closed) + return; + + if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY) + return; + + dispatch->lid_is_closed = false; + switch_notify_toggle(&dispatch->device->base, + time, + LIBINPUT_SWITCH_LID, + dispatch->lid_is_closed); +} + +static void +lid_switch_toggle_keyboard_listener(struct lid_switch_dispatch *dispatch, + bool is_closed) +{ + if (!dispatch->keyboard.keyboard) + return; + + if (is_closed) { + libinput_device_add_event_listener( + &dispatch->keyboard.keyboard->base, + &dispatch->keyboard.listener, + lid_switch_keyboard_event, + dispatch); + } else { + libinput_device_remove_event_listener( + &dispatch->keyboard.listener); + } +} + static void lid_switch_process_switch(struct lid_switch_dispatch *dispatch, struct evdev_device *device, @@ -49,7 +95,11 @@ 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, @@ -107,6 +157,56 @@ lid_switch_destroy(struct evdev_dispatch *evdev_dispatch) free(dispatch); } +static void +lid_switch_pair_keyboard(struct evdev_device *lid_switch, + struct evdev_device *keyboard) +{ + struct lid_switch_dispatch *dispatch = + (struct lid_switch_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; + libinput_device_remove_event_listener(&dispatch->keyboard.listener); + } + + dispatch->keyboard.keyboard = keyboard; + log_debug(evdev_libinput_context(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 +lid_switch_interface_device_added(struct evdev_device *device, + struct evdev_device *added_device) +{ + lid_switch_pair_keyboard(device, added_device); +} + +static void +lid_switch_interface_device_removed(struct evdev_device *device, + struct evdev_device *removed_device) +{ + struct lid_switch_dispatch *dispatch = + (struct lid_switch_dispatch*)device->dispatch; + + if (removed_device == dispatch->keyboard.keyboard) { + libinput_device_remove_event_listener( + &dispatch->keyboard.listener); + dispatch->keyboard.keyboard = NULL; + } +} + static void lid_switch_sync_initial_state(struct evdev_device *device, struct evdev_dispatch *evdev_dispatch) @@ -148,10 +248,10 @@ struct evdev_dispatch_interface lid_switch_interface = { NULL, /* suspend */ NULL, /* remove */ lid_switch_destroy, - NULL, /* device_added */ - NULL, /* device_removed */ - NULL, /* device_suspended */ - NULL, /* device_resumed */ + lid_switch_interface_device_added, + lid_switch_interface_device_removed, + lid_switch_interface_device_removed, /* device_suspended, treat as remove */ + lid_switch_interface_device_added, /* device_resumed, treat as add */ lid_switch_sync_initial_state, NULL, /* toggle_touch */ }; @@ -165,6 +265,8 @@ evdev_lid_switch_dispatch_create(struct evdev_device *lid_device) return NULL; dispatch->base.interface = &lid_switch_interface; + dispatch->device = lid_device; + libinput_device_init_event_listener(&dispatch->keyboard.listener); evdev_init_sendevents(lid_device, &dispatch->base); diff --git a/test/test-lid.c b/test/test-lid.c index 59c461dd..6577d23f 100644 --- a/test/test-lid.c +++ b/test/test-lid.c @@ -293,6 +293,71 @@ START_TEST(lid_disable_touchpad_already_open) } END_TEST +START_TEST(lid_open_on_key) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = sw->libinput; + struct libinput_event *event; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_drain_events(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); + libinput_dispatch(li); + + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_SWITCH_TOGGLE, -1); + event = libinput_get_event(li); + litest_is_switch_event(event, + LIBINPUT_SWITCH_LID, + LIBINPUT_SWITCH_STATE_OFF); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_OFF); + litest_assert_empty_queue(li); + + libinput_event_destroy(event); + litest_delete_device(keyboard); +} +END_TEST + +START_TEST(lid_open_on_key_touchpad_enabled) +{ + struct litest_device *sw = litest_current_device(); + struct litest_device *keyboard, *touchpad; + struct libinput *li = sw->libinput; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C); + + litest_lid_action(sw, LIBINPUT_SWITCH_STATE_ON); + litest_drain_events(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); + litest_drain_events(li); + litest_timeout_dwt_long(); + + litest_touch_down(touchpad, 0, 50, 50); + litest_touch_move_to(touchpad, 0, 50, 50, 70, 70, 10, 1); + litest_touch_up(touchpad, 0); + libinput_dispatch(li); + + litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION); + + litest_delete_device(keyboard); + litest_delete_device(touchpad); +} +END_TEST + void litest_setup_tests_lid(void) { @@ -304,4 +369,7 @@ litest_setup_tests_lid(void) litest_add("lid:disable_touchpad", lid_disable_touchpad_edge_scroll, LITEST_SWITCH, LITEST_ANY); litest_add("lid:disable_touchpad", lid_disable_touchpad_edge_scroll_interrupt, LITEST_SWITCH, LITEST_ANY); litest_add("lid:disable_touchpad", lid_disable_touchpad_already_open, LITEST_SWITCH, LITEST_ANY); + + litest_add("lid:keyboard", lid_open_on_key, LITEST_SWITCH, LITEST_ANY); + litest_add("lid:keyboard", lid_open_on_key_touchpad_enabled, LITEST_SWITCH, LITEST_ANY); }