[PATCH libinput 3/3] touchpad: if a device has a tablet mode switch, disable the touchpad

Peter Hutterer peter.hutterer at who-t.net
Tue Sep 5 03:11:11 UTC 2017


On some devices with a tablet mode switch, the touchpad is inacessible when
in tablet mode and we don't really need this except to avoid possible ghost
touches (none have been mentioned so far). On other devices like the Lenovo
Yoga, the touchpad points to the back of the device and it's hard to use the
device without accidentally using the touchpad. For those, disabling the
touchpad is the best solution.

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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-mt-touchpad.c |  58 ++++++++++++++--
 src/evdev-mt-touchpad.h |   5 ++
 src/evdev.c             |  19 ++++++
 src/evdev.h             |   4 ++
 test/test-switch.c      | 177 +++++++++++++++++++++++++++++++-----------------
 5 files changed, 198 insertions(+), 65 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 58595785..eb3dde3c 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -1479,6 +1479,10 @@ tp_remove_sendevents(struct tp_dispatch *tp)
 	if (tp->lid_switch.lid_switch)
 		libinput_device_remove_event_listener(
 					&tp->lid_switch.listener);
+
+	if (tp->tablet_mode_switch.tablet_mode_switch)
+		libinput_device_remove_event_listener(
+					&tp->tablet_mode_switch.listener);
 }
 
 static void
@@ -1861,23 +1865,34 @@ tp_pair_trackpoint(struct evdev_device *touchpad,
 }
 
 static void
-tp_lid_switch_event(uint64_t time, struct libinput_event *event, void *data)
+tp_switch_event(uint64_t time, struct libinput_event *event, void *data)
 {
 	struct tp_dispatch *tp = data;
 	struct libinput_event_switch *swev;
+	const char *which = NULL;
 
 	if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
 		return;
 
 	swev = libinput_event_get_switch_event(event);
+
+	switch (libinput_event_switch_get_switch(swev)) {
+	case LIBINPUT_SWITCH_LID:
+		which = "lid";
+		break;
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		which = "tablet-mode";
+		break;
+	}
+
 	switch (libinput_event_switch_get_switch_state(swev)) {
 	case LIBINPUT_SWITCH_STATE_OFF:
 		tp_resume(tp, tp->device);
-		evdev_log_debug(tp->device, "lid: resume touchpad\n");
+		evdev_log_debug(tp->device, "%s: resume touchpad\n", which);
 		break;
 	case LIBINPUT_SWITCH_STATE_ON:
 		tp_suspend(tp, tp->device);
-		evdev_log_debug(tp->device, "lid: suspend touchpad\n");
+		evdev_log_debug(tp->device, "%s: suspend touchpad\n", which);
 		break;
 	}
 }
@@ -1899,12 +1914,40 @@ tp_pair_lid_switch(struct evdev_device *touchpad,
 
 		libinput_device_add_event_listener(&lid_switch->base,
 						   &tp->lid_switch.listener,
-						   tp_lid_switch_event, tp);
+						   tp_switch_event, tp);
 		tp->lid_switch.lid_switch = lid_switch;
 	}
 }
 
 static void
+tp_pair_tablet_mode_switch(struct evdev_device *touchpad,
+			   struct evdev_device *tablet_mode_switch)
+{
+	struct tp_dispatch *tp = (struct tp_dispatch*)touchpad->dispatch;
+
+	if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
+		return;
+
+	if (tp->tablet_mode_switch.tablet_mode_switch == NULL) {
+		evdev_log_debug(touchpad,
+				"tablet_mode_switch: activated for %s<->%s\n",
+				touchpad->devname,
+				tablet_mode_switch->devname);
+
+		libinput_device_add_event_listener(&tablet_mode_switch->base,
+					&tp->tablet_mode_switch.listener,
+					tp_switch_event, tp);
+		tp->tablet_mode_switch.tablet_mode_switch = tablet_mode_switch;
+
+		if (evdev_device_switch_get_state(tablet_mode_switch,
+						  LIBINPUT_SWITCH_TABLET_MODE)
+			    == LIBINPUT_SWITCH_STATE_ON) {
+			tp_suspend(tp, touchpad);
+		}
+	}
+}
+
+static void
 tp_interface_device_added(struct evdev_device *device,
 			  struct evdev_device *added_device)
 {
@@ -1913,6 +1956,7 @@ tp_interface_device_added(struct evdev_device *device,
 	tp_pair_trackpoint(device, added_device);
 	tp_dwt_pair_keyboard(device, added_device);
 	tp_pair_lid_switch(device, added_device);
+	tp_pair_tablet_mode_switch(device, added_device);
 
 	if (tp->sendevents.current_mode !=
 	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
@@ -1954,6 +1998,12 @@ tp_interface_device_removed(struct evdev_device *device,
 		tp->lid_switch.lid_switch = NULL;
 	}
 
+	if (removed_device == tp->tablet_mode_switch.tablet_mode_switch) {
+		libinput_device_remove_event_listener(
+					&tp->tablet_mode_switch.listener);
+		tp->tablet_mode_switch.tablet_mode_switch = NULL;
+	}
+
 	if (tp->sendevents.current_mode !=
 	    LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
 		return;
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 4c667eff..efbdb3bf 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -407,6 +407,11 @@ struct tp_dispatch {
 		struct libinput_event_listener listener;
 		struct evdev_device *lid_switch;
 	} lid_switch;
+
+	struct {
+		struct libinput_event_listener listener;
+		struct evdev_device *tablet_mode_switch;
+	} tablet_mode_switch;
 };
 
 static inline struct tp_dispatch*
diff --git a/src/evdev.c b/src/evdev.c
index f9d8d34a..46f8ad57 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -185,6 +185,25 @@ fallback_lid_notify_toggle(struct fallback_dispatch *dispatch,
 	}
 }
 
+enum libinput_switch_state
+evdev_device_switch_get_state(struct evdev_device *device,
+			      enum libinput_switch sw)
+{
+	struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
+
+	switch (sw) {
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		break;
+	default:
+		/* Internal function only, so we can abort here */
+		abort();
+	}
+
+	return dispatch->tablet_mode.state ?
+			LIBINPUT_SWITCH_STATE_ON :
+			LIBINPUT_SWITCH_STATE_OFF;
+}
+
 void
 evdev_pointer_notify_physical_button(struct evdev_device *device,
 				     uint64_t time,
diff --git a/src/evdev.h b/src/evdev.h
index 4895c2ec..5192927c 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -524,6 +524,10 @@ struct libinput_tablet_pad_mode_group *
 evdev_device_tablet_pad_get_mode_group(struct evdev_device *device,
 				       unsigned int index);
 
+enum libinput_switch_state
+evdev_device_switch_get_state(struct evdev_device *device,
+			      enum libinput_switch sw);
+
 double
 evdev_device_transform_x(struct evdev_device *device,
 			 double x,
diff --git a/test/test-switch.c b/test/test-switch.c
index 59809798..77ba5593 100644
--- a/test/test-switch.c
+++ b/test/test-switch.c
@@ -36,6 +36,13 @@ switch_has_lid(struct litest_device *dev)
 						 LIBINPUT_SWITCH_LID);
 }
 
+static inline bool
+switch_has_tablet_mode(struct litest_device *dev)
+{
+	return libinput_device_switch_has_switch(dev->libinput_device,
+						 LIBINPUT_SWITCH_TABLET_MODE);
+}
+
 START_TEST(switch_has_cap)
 {
 	struct litest_device *dev = litest_current_device();
@@ -228,30 +235,29 @@ START_TEST(switch_not_down_on_init)
 END_TEST
 
 static inline struct litest_device *
-lid_init_paired_touchpad(struct libinput *li)
+switch_init_paired_touchpad(struct libinput *li)
 {
 	enum litest_device_type which = LITEST_SYNAPTICS_I2C;
 
 	return litest_add_device(li, which);
 }
 
-START_TEST(lid_disable_touchpad)
+START_TEST(switch_disable_touchpad)
 {
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
+	enum libinput_switch which = _i; /* ranged test */
 
-	if (!switch_has_lid(sw))
+	if (!libinput_device_switch_has_switch(sw->libinput_device, which))
 		return;
 
-	touchpad = lid_init_paired_touchpad(li);
+	touchpad = switch_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
 
-	/* lid is down - no events */
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	/* switch is on - no events */
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_ON);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
 
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -259,10 +265,8 @@ START_TEST(lid_disable_touchpad)
 	litest_touch_up(touchpad, 0);
 	litest_assert_empty_queue(li);
 
-	/* lid is up - motion events */
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_OFF);
+	/* switch is off - motion events */
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_OFF);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
 
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -274,16 +278,17 @@ START_TEST(lid_disable_touchpad)
 }
 END_TEST
 
-START_TEST(lid_disable_touchpad_during_touch)
+START_TEST(switch_disable_touchpad_during_touch)
 {
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
+	enum libinput_switch which = _i; /* ranged test */
 
-	if (!switch_has_lid(sw))
+	if (!libinput_device_switch_has_switch(sw->libinput_device, which))
 		return;
 
-	touchpad = lid_init_paired_touchpad(li);
+	touchpad = switch_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
 
@@ -291,9 +296,7 @@ START_TEST(lid_disable_touchpad_during_touch)
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 5, 1);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_ON);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
 
 	litest_touch_move_to(touchpad, 0, 70, 50, 50, 50, 5, 1);
@@ -304,23 +307,22 @@ START_TEST(lid_disable_touchpad_during_touch)
 }
 END_TEST
 
-START_TEST(lid_disable_touchpad_edge_scroll)
+START_TEST(switch_disable_touchpad_edge_scroll)
 {
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
+	enum libinput_switch which = _i; /* ranged test */
 
-	if (!switch_has_lid(sw))
+	if (!libinput_device_switch_has_switch(sw->libinput_device, which))
 		return;
 
-	touchpad = lid_init_paired_touchpad(li);
+	touchpad = switch_init_paired_touchpad(li);
 	litest_enable_edge_scroll(touchpad);
 
 	litest_drain_events(li);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_ON);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_SWITCH_TOGGLE);
 
 	litest_touch_down(touchpad, 0, 99, 20);
@@ -342,17 +344,18 @@ START_TEST(lid_disable_touchpad_edge_scroll)
 }
 END_TEST
 
-START_TEST(lid_disable_touchpad_edge_scroll_interrupt)
+START_TEST(switch_disable_touchpad_edge_scroll_interrupt)
 {
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 	struct libinput_event *event;
+	enum libinput_switch which = _i; /* ranged test */
 
-	if (!switch_has_lid(sw))
+	if (!libinput_device_switch_has_switch(sw->libinput_device, which))
 		return;
 
-	touchpad = lid_init_paired_touchpad(li);
+	touchpad = switch_init_paired_touchpad(li);
 	litest_enable_edge_scroll(touchpad);
 
 	litest_drain_events(li);
@@ -364,9 +367,7 @@ START_TEST(lid_disable_touchpad_edge_scroll_interrupt)
 	libinput_dispatch(li);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_ON);
 	libinput_dispatch(li);
 
 	event = libinput_get_event(li);
@@ -376,38 +377,36 @@ START_TEST(lid_disable_touchpad_edge_scroll_interrupt)
 	libinput_event_destroy(event);
 
 	event = libinput_get_event(li);
-	litest_is_switch_event(event,
-			       LIBINPUT_SWITCH_LID,
-			       LIBINPUT_SWITCH_STATE_ON);
+	litest_is_switch_event(event, which, LIBINPUT_SWITCH_STATE_ON);
 	libinput_event_destroy(event);
 
 	litest_delete_device(touchpad);
 }
 END_TEST
 
-START_TEST(lid_disable_touchpad_already_open)
+START_TEST(switch_disable_touchpad_already_open)
 {
 	struct litest_device *sw = litest_current_device();
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
+	enum libinput_switch which = _i; /* ranged test */
 
-	if (!switch_has_lid(sw))
+	if (!libinput_device_switch_has_switch(sw->libinput_device, which))
 		return;
 
-	touchpad = lid_init_paired_touchpad(li);
+	touchpad = switch_init_paired_touchpad(li);
+
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
 
-	/* default: lid is up - motion events */
+	/* default: switch is off - motion events */
 	litest_touch_down(touchpad, 0, 50, 50);
 	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
 	litest_touch_up(touchpad, 0);
 	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 
-	/* open lid - motion events */
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_OFF);
+	/* disable switch - motion events */
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_OFF);
 	litest_assert_empty_queue(li);
 
 	litest_touch_down(touchpad, 0, 50, 50);
@@ -495,27 +494,34 @@ START_TEST(lid_open_on_key_touchpad_enabled)
 }
 END_TEST
 
-START_TEST(lid_suspend_with_keyboard)
+START_TEST(switch_suspend_with_keyboard)
 {
 	struct libinput *li;
 	struct litest_device *keyboard;
 	struct litest_device *sw;
+	enum libinput_switch which = _i; /* ranged test */
 
 	li = litest_create_context();
 
-	sw = litest_add_device(li, LITEST_LID_SWITCH);
+	switch(which) {
+	case LIBINPUT_SWITCH_LID:
+		sw = litest_add_device(li, LITEST_LID_SWITCH);
+		break;
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		sw = litest_add_device(li, LITEST_THINKPAD_EXTRABUTTONS);
+		break;
+	default:
+		abort();
+	}
+
 	libinput_dispatch(li);
 
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 	libinput_dispatch(li);
 
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_ON);
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_ON);
 	litest_drain_events(li);
-	litest_switch_action(sw,
-			     LIBINPUT_SWITCH_LID,
-			     LIBINPUT_SWITCH_STATE_OFF);
+	litest_switch_action(sw, which, LIBINPUT_SWITCH_STATE_OFF);
 	litest_drain_events(li);
 
 	litest_delete_device(keyboard);
@@ -528,14 +534,25 @@ START_TEST(lid_suspend_with_keyboard)
 }
 END_TEST
 
-START_TEST(lid_suspend_with_touchpad)
+START_TEST(switch_suspend_with_touchpad)
 {
 	struct libinput *li;
 	struct litest_device *touchpad, *sw;
+	enum libinput_switch which = _i; /* ranged test */
 
 	li = litest_create_context();
 
-	sw = litest_add_device(li, LITEST_LID_SWITCH);
+	switch(which) {
+	case LIBINPUT_SWITCH_LID:
+		sw = litest_add_device(li, LITEST_LID_SWITCH);
+		break;
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		sw = litest_add_device(li, LITEST_THINKPAD_EXTRABUTTONS);
+		break;
+	default:
+		abort();
+	}
+
 	litest_drain_events(li);
 
 	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
@@ -671,6 +688,44 @@ START_TEST(lid_key_press)
 }
 END_TEST
 
+START_TEST(tablet_mode_disable_touchpad_on_init)
+{
+	struct litest_device *sw = litest_current_device();
+	struct litest_device *touchpad;
+	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);
+
+	/* touchpad comes with switch already on - no events */
+	touchpad = switch_init_paired_touchpad(li);
+	litest_disable_tap(touchpad->libinput_device);
+	litest_drain_events(li);
+
+	litest_touch_down(touchpad, 0, 50, 50);
+	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
+	litest_touch_up(touchpad, 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_touch_down(touchpad, 0, 50, 50);
+	litest_touch_move_to(touchpad, 0, 50, 50, 70, 50, 10, 1);
+	litest_touch_up(touchpad, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
+
+	litest_delete_device(touchpad);
+}
+END_TEST
+
 void
 litest_setup_tests_lid(void)
 {
@@ -684,20 +739,20 @@ litest_setup_tests_lid(void)
 	litest_add_ranged("switch:toggle", switch_toggle_double, LITEST_SWITCH, LITEST_ANY, &switches);
 	litest_add_ranged("switch:toggle", switch_down_on_init, LITEST_SWITCH, LITEST_ANY, &switches);
 	litest_add("switch:toggle", switch_not_down_on_init, LITEST_SWITCH, LITEST_ANY);
-	litest_add("lid:disable_touchpad", lid_disable_touchpad, LITEST_SWITCH, LITEST_ANY);
-	litest_add("lid:disable_touchpad", lid_disable_touchpad_during_touch, LITEST_SWITCH, LITEST_ANY);
-	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_ranged("switch:touchpad", switch_disable_touchpad, LITEST_SWITCH, LITEST_ANY, &switches);
+	litest_add_ranged("switch:touchpad", switch_disable_touchpad_during_touch, LITEST_SWITCH, LITEST_ANY, &switches);
+	litest_add_ranged("switch:touchpad", switch_disable_touchpad_edge_scroll, LITEST_SWITCH, LITEST_ANY, &switches);
+	litest_add_ranged("switch:touchpad", switch_disable_touchpad_edge_scroll_interrupt, LITEST_SWITCH, LITEST_ANY, &switches);
+	litest_add_ranged("switch:touchpad", switch_disable_touchpad_already_open, LITEST_SWITCH, LITEST_ANY, &switches);
+
+	litest_add_ranged_no_device("switch:keyboard", switch_suspend_with_keyboard, &switches);
+	litest_add_ranged_no_device("switch:touchpad", switch_suspend_with_touchpad, &switches);
 
 	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);
-
-	litest_add_no_device("lid:keyboard", lid_suspend_with_keyboard);
-	litest_add_no_device("lid:disable_touchpad", lid_suspend_with_touchpad);
-
 	litest_add_for_device("lid:buggy", lid_update_hw_on_key, LITEST_LID_SWITCH_SURFACE3);
 	litest_add_for_device("lid:buggy", lid_update_hw_on_key_closed_on_init, LITEST_LID_SWITCH_SURFACE3);
-
 	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);
 }
-- 
2.13.5



More information about the wayland-devel mailing list