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 <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2017-01-25 15:21:52 +10:00
parent d17e84fc0f
commit 4eb29a8cc5
2 changed files with 174 additions and 4 deletions

View file

@ -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);

View file

@ -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);
}