From 4eb29a8cc5eb36962a6cb14be032a4ca09296ffb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 25 Jan 2017 15:21:52 +1000 Subject: [PATCH] switch: hook up to keyboard events to fix the lid switch state Extra insurance against broken lid switches. Listen to events from the (internal) keyboard when we are logically closed. If any, assume we're open after all and update accordingly. Signed-off-by: Peter Hutterer --- src/evdev-lid.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-- test/test-lid.c | 68 ++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 4 deletions(-) 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); }