[PATCH libinput] fallback: suspend internal keyboards and trackpoints on tablet-mode switch

Peter Hutterer peter.hutterer at who-t.net
Thu Sep 21 01:01:28 UTC 2017


Because on some devices the keyboard is where the fingers are holding the
device when in tablet mode.

https://bugs.freedesktop.org/show_bug.cgi?id=102729

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Requires the series from
https://lists.freedesktop.org/archives/wayland-devel/2017-September/035094.html
Branch for testing available here:
https://github.com/whot/libinput/tree/wip/disable-keyboard-in-tablet-mode

 src/evdev-fallback.c | 121 ++++++++++++++++++++++++++++++++++++---
 test/test-switch.c   | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 271 insertions(+), 9 deletions(-)

diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
index 47c05e4f..3eb71704 100644
--- a/src/evdev-fallback.c
+++ b/src/evdev-fallback.c
@@ -66,7 +66,16 @@ struct fallback_dispatch {
 	struct device_coords rel;
 
 	struct {
-		int state;
+		/* The struct for the tablet mode switch device itself */
+		struct {
+			int state;
+		} sw;
+		/* The struct for other devices listening to the tablet mode
+		   switch */
+		struct {
+			struct evdev_device *sw_device;
+			struct libinput_event_listener listener;
+		} other;
 	} tablet_mode;
 
 	/* Bitmask of pressed keys used to ignore initial release events from
@@ -171,7 +180,7 @@ fallback_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch,
 		abort();
 	}
 
-	return dispatch->tablet_mode.state ?
+	return dispatch->tablet_mode.sw.state ?
 			LIBINPUT_SWITCH_STATE_ON :
 			LIBINPUT_SWITCH_STATE_OFF;
 }
@@ -965,10 +974,10 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
 		fallback_lid_notify_toggle(dispatch, device, time);
 		break;
 	case SW_TABLET_MODE:
-		if (dispatch->tablet_mode.state == e->value)
+		if (dispatch->tablet_mode.sw.state == e->value)
 			return;
 
-		dispatch->tablet_mode.state = e->value;
+		dispatch->tablet_mode.sw.state = e->value;
 		if (e->value)
 			state = LIBINPUT_SWITCH_STATE_ON;
 		else
@@ -1235,10 +1244,10 @@ fallback_interface_remove(struct evdev_dispatch *evdev_dispatch)
 {
 	struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
 
-	if (!dispatch->lid.keyboard)
-		return;
+	libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener);
 
-	libinput_device_remove_event_listener(&dispatch->lid.listener);
+	if (dispatch->lid.keyboard)
+		libinput_device_remove_event_listener(&dispatch->lid.listener);
 }
 
 static void
@@ -1269,7 +1278,7 @@ fallback_interface_sync_initial_state(struct evdev_device *device,
 		}
 	}
 
-	if (dispatch->tablet_mode.state) {
+	if (dispatch->tablet_mode.sw.state) {
 		switch_notify_toggle(&device->base,
 				     time,
 				     LIBINPUT_SWITCH_TABLET_MODE,
@@ -1335,10 +1344,93 @@ fallback_lid_pair_keyboard(struct evdev_device *lid_switch,
 }
 
 static void
+fallback_resume(struct fallback_dispatch *dispatch,
+		struct evdev_device *device)
+{
+	if (dispatch->base.sendevents.current_mode ==
+	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED)
+		return;
+
+	evdev_device_resume(device);
+}
+
+static void
+fallback_suspend(struct fallback_dispatch *dispatch,
+		 struct evdev_device *device)
+{
+	evdev_device_suspend(device);
+}
+
+static void
+fallback_tablet_mode_switch_event(uint64_t time,
+				  struct libinput_event *event,
+				  void *data)
+{
+	struct fallback_dispatch *dispatch = data;
+	struct evdev_device *device = dispatch->device;
+	struct libinput_event_switch *swev;
+
+	if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
+		return;
+
+	swev = libinput_event_get_switch_event(event);
+	if (libinput_event_switch_get_switch(swev) !=
+	    LIBINPUT_SWITCH_TABLET_MODE)
+		return;
+
+
+	switch (libinput_event_switch_get_switch_state(swev)) {
+	case LIBINPUT_SWITCH_STATE_OFF:
+		fallback_resume(dispatch, device);
+		evdev_log_debug(device, "tablet-mode: resuming device\n");
+		break;
+	case LIBINPUT_SWITCH_STATE_ON:
+		fallback_suspend(dispatch, device);
+		evdev_log_debug(device, "tablet-mode: suspending device\n");
+		break;
+	}
+}
+
+static void
+fallback_keyboard_pair_tablet_mode(struct evdev_device *keyboard,
+				   struct evdev_device *tablet_mode_switch)
+{
+	struct fallback_dispatch *dispatch =
+		fallback_dispatch(keyboard->dispatch);
+
+	if ((keyboard->tags &
+	     (EVDEV_TAG_TRACKPOINT|EVDEV_TAG_INTERNAL_KEYBOARD)) == 0)
+		return;
+
+	if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
+		return;
+
+	if (dispatch->tablet_mode.other.sw_device)
+		return;
+
+	evdev_log_debug(keyboard,
+			"tablet_mode_switch: activated for %s<->%s\n",
+			keyboard->devname,
+			tablet_mode_switch->devname);
+
+	libinput_device_add_event_listener(&tablet_mode_switch->base,
+				&dispatch->tablet_mode.other.listener,
+				fallback_tablet_mode_switch_event,
+				dispatch);
+	dispatch->tablet_mode.other.sw_device = tablet_mode_switch;
+
+	if (evdev_device_switch_get_state(tablet_mode_switch,
+					  LIBINPUT_SWITCH_TABLET_MODE)
+		    == LIBINPUT_SWITCH_STATE_ON)
+		fallback_suspend(dispatch, keyboard);
+}
+
+static void
 fallback_interface_device_added(struct evdev_device *device,
 				struct evdev_device *added_device)
 {
 	fallback_lid_pair_keyboard(device, added_device);
+	fallback_keyboard_pair_tablet_mode(device, added_device);
 }
 
 static void
@@ -1353,8 +1445,17 @@ fallback_interface_device_removed(struct evdev_device *device,
 					&dispatch->lid.listener);
 		libinput_device_init_event_listener(
 					&dispatch->lid.listener);
+
 		dispatch->lid.keyboard = NULL;
 	}
+
+	if (removed_device == dispatch->tablet_mode.other.sw_device) {
+		libinput_device_remove_event_listener(
+				&dispatch->tablet_mode.other.listener);
+		libinput_device_init_event_listener(
+				&dispatch->tablet_mode.other.listener);
+		dispatch->tablet_mode.other.sw_device = NULL;
+	}
 }
 
 struct evdev_dispatch_interface fallback_interface = {
@@ -1550,8 +1651,10 @@ fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
 		val = libevdev_get_event_value(device->evdev,
 					       EV_SW,
 					       SW_TABLET_MODE);
-		dispatch->tablet_mode.state = val;
+		dispatch->tablet_mode.sw.state = val;
 	}
+
+	libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener);
 }
 
 struct evdev_dispatch *
diff --git a/test/test-switch.c b/test/test-switch.c
index 7cdcf440..fae4f56c 100644
--- a/test/test-switch.c
+++ b/test/test-switch.c
@@ -808,6 +808,161 @@ START_TEST(tablet_mode_disable_touchpad_on_init)
 }
 END_TEST
 
+START_TEST(tablet_mode_disable_keyboard)
+{
+	struct litest_device *sw = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = sw->libinput;
+
+	if (!switch_has_tablet_mode(sw))
+		return;
+
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_ON);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_empty_queue(li);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_delete_device(keyboard);
+}
+END_TEST
+
+START_TEST(tablet_mode_disable_keyboard_on_init)
+{
+	struct litest_device *sw = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = sw->libinput;
+
+	if (!switch_has_tablet_mode(sw))
+		return;
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_ON);
+	litest_drain_events(li);
+
+	/* keyboard comes with switch already on - no events */
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_empty_queue(li);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_KEYBOARD_KEY);
+
+	litest_delete_device(keyboard);
+}
+END_TEST
+
+START_TEST(tablet_mode_disable_trackpoint)
+{
+	struct litest_device *sw = litest_current_device();
+	struct litest_device *trackpoint;
+	struct libinput *li = sw->libinput;
+
+	if (!switch_has_tablet_mode(sw))
+		return;
+
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
+	litest_drain_events(li);
+
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_ON);
+	litest_drain_events(li);
+
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_assert_empty_queue(li);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
+
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_delete_device(trackpoint);
+}
+END_TEST
+START_TEST(tablet_mode_disable_trackpoint_on_init)
+{
+	struct litest_device *sw = litest_current_device();
+	struct litest_device *trackpoint;
+	struct libinput *li = sw->libinput;
+
+	if (!switch_has_tablet_mode(sw))
+		return;
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_ON);
+	litest_drain_events(li);
+
+	/* trackpoint comes with switch already on - no events */
+	trackpoint = litest_add_device(li, LITEST_TRACKPOINT);
+	litest_drain_events(li);
+
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_assert_empty_queue(li);
+
+	litest_switch_action(sw,
+			     LIBINPUT_SWITCH_TABLET_MODE,
+			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
+
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_event(trackpoint, EV_REL, REL_Y, -1);
+	litest_event(trackpoint, EV_SYN, SYN_REPORT, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_delete_device(trackpoint);
+}
+END_TEST
+
 void
 litest_setup_tests_lid(void)
 {
@@ -839,4 +994,8 @@ litest_setup_tests_lid(void)
 	litest_add_for_device("lid:keypress", lid_key_press, LITEST_GPIO_KEYS);
 
 	litest_add("tablet-mode:touchpad", tablet_mode_disable_touchpad_on_init, LITEST_SWITCH, LITEST_ANY);
+	litest_add("tablet-mode:keyboard", tablet_mode_disable_keyboard, LITEST_SWITCH, LITEST_ANY);
+	litest_add("tablet-mode:keyboard", tablet_mode_disable_keyboard_on_init, LITEST_SWITCH, LITEST_ANY);
+	litest_add("tablet-mode:trackpoint", tablet_mode_disable_trackpoint, LITEST_SWITCH, LITEST_ANY);
+	litest_add("tablet-mode:trackpoint", tablet_mode_disable_trackpoint_on_init, LITEST_SWITCH, LITEST_ANY);
 }
-- 
2.13.5



More information about the wayland-devel mailing list