[PATCH libinput 13/14] switch: hook up to keyboard events to fix the lid switch state

Peter Hutterer peter.hutterer at who-t.net
Wed Jan 25 23:44:23 UTC 2017


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 at who-t.net>
---
 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 3cc287c..9029838 100644
--- a/src/evdev-lid.c
+++ b/src/evdev-lid.c
@@ -30,11 +30,57 @@
 
 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,
 			  struct input_event *e,
@@ -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,
@@ -108,6 +158,56 @@ lid_switch_destroy(struct evdev_dispatch *evdev_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 59c461d..6577d23 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);
 }
-- 
2.9.3



More information about the wayland-devel mailing list