[PATCH libinput 2/3] Add support for SW_TABLET_MODE

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


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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 meson.build                                |  1 +
 src/evdev.c                                | 57 +++++++++++++----
 src/evdev.h                                |  5 ++
 src/libinput.h                             | 16 +++++
 test/litest-device-thinkpad-extrabuttons.c | 90 +++++++++++++++++++++++++++
 test/litest.c                              |  5 ++
 test/litest.h                              |  1 +
 test/test-switch.c                         | 98 +++++++++++++++++++++++++-----
 8 files changed, 246 insertions(+), 27 deletions(-)
 create mode 100644 test/litest-device-thinkpad-extrabuttons.c

diff --git a/meson.build b/meson.build
index a20018ff..41381553 100644
--- a/meson.build
+++ b/meson.build
@@ -565,6 +565,7 @@ if get_option('tests')
 		'test/litest-device-synaptics-st.c',
 		'test/litest-device-synaptics-t440.c',
 		'test/litest-device-synaptics-x1-carbon-3rd.c',
+		'test/litest-device-thinkpad-extrabuttons.c',
 		'test/litest-device-trackpoint.c',
 		'test/litest-device-touch-screen.c',
 		'test/litest-device-touchscreen-fuzz.c',
diff --git a/src/evdev.c b/src/evdev.c
index 9dec2c4f..f9d8d34a 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1161,6 +1161,7 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
 			struct input_event *e,
 			uint64_t time)
 {
+	enum libinput_switch_state state;
 	bool is_closed;
 
 	switch (e->code) {
@@ -1174,6 +1175,20 @@ fallback_process_switch(struct fallback_dispatch *dispatch,
 		dispatch->lid.is_closed = is_closed;
 		fallback_lid_notify_toggle(dispatch, device, time);
 		break;
+	case SW_TABLET_MODE:
+		if (dispatch->tablet_mode.state == e->value)
+			return;
+
+		dispatch->tablet_mode.state = e->value;
+		if (e->value)
+			state = LIBINPUT_SWITCH_STATE_ON;
+		else
+			state = LIBINPUT_SWITCH_STATE_OFF;
+		switch_notify_toggle(&device->base,
+				     time,
+				     LIBINPUT_SWITCH_TABLET_MODE,
+				     state);
+		break;
 	}
 }
 
@@ -1381,12 +1396,6 @@ evdev_tag_keyboard(struct evdev_device *device,
 }
 
 static void
-evdev_tag_lid_switch(struct evdev_device *device)
-{
-	device->tags |= EVDEV_TAG_LID_SWITCH;
-}
-
-static void
 fallback_process(struct evdev_dispatch *evdev_dispatch,
 		 struct evdev_device *device,
 		 struct input_event *event,
@@ -1566,6 +1575,13 @@ fallback_sync_initial_state(struct evdev_device *device,
 			fallback_lid_notify_toggle(dispatch, device, time);
 		}
 	}
+
+	if (dispatch->tablet_mode.state) {
+		switch_notify_toggle(&device->base,
+				     time,
+				     LIBINPUT_SWITCH_TABLET_MODE,
+				     LIBINPUT_SWITCH_STATE_ON);
+	}
 }
 
 static void
@@ -2197,11 +2213,20 @@ static inline void
 fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
 			      struct evdev_device *device)
 {
+	int val;
+
 	if (device->tags & EVDEV_TAG_LID_SWITCH) {
 		libinput_device_init_event_listener(&dispatch->lid.listener);
 		dispatch->lid.reliability = evdev_read_switch_reliability_prop(device);
 		dispatch->lid.is_closed = false;
 	}
+
+	if (device->tags & EVDEV_TAG_TABLET_MODE_SWITCH) {
+		val = libevdev_get_event_value(device->evdev,
+					       EV_SW,
+					       SW_TABLET_MODE);
+		dispatch->tablet_mode.state = val;
+	}
 }
 
 static struct evdev_dispatch *
@@ -3115,11 +3140,18 @@ evdev_configure_device(struct evdev_device *device)
 		evdev_log_info(device, "device is a touch device\n");
 	}
 
-	if (udev_tags & EVDEV_UDEV_TAG_SWITCH &&
-	    libevdev_has_event_code(evdev, EV_SW, SW_LID)) {
-		device->seat_caps |= EVDEV_DEVICE_SWITCH;
-		evdev_tag_lid_switch(device);
-		evdev_log_info(device, "device is a switch device\n");
+	if (udev_tags & EVDEV_UDEV_TAG_SWITCH) {
+		if (libevdev_has_event_code(evdev, EV_SW, SW_LID)) {
+			device->seat_caps |= EVDEV_DEVICE_SWITCH;
+			device->tags |= EVDEV_TAG_LID_SWITCH;
+			evdev_log_info(device, "device is a switch device\n");
+		}
+
+		if (libevdev_has_event_code(evdev, EV_SW, SW_TABLET_MODE)) {
+			device->seat_caps |= EVDEV_DEVICE_SWITCH;
+			device->tags |= EVDEV_TAG_TABLET_MODE_SWITCH;
+			evdev_log_info(device, "device is a switch device\n");
+		}
 	}
 
 	if (device->seat_caps & EVDEV_DEVICE_POINTER &&
@@ -3651,6 +3683,9 @@ evdev_device_has_switch(struct evdev_device *device,
 	case LIBINPUT_SWITCH_LID:
 		code = SW_LID;
 		break;
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		code = SW_TABLET_MODE;
+		break;
 	default:
 		return -1;
 	}
diff --git a/src/evdev.h b/src/evdev.h
index 0b8b2791..4895c2ec 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -75,6 +75,7 @@ enum evdev_device_tags {
 	EVDEV_TAG_LID_SWITCH = (1 << 5),
 	EVDEV_TAG_INTERNAL_KEYBOARD = (1 << 6),
 	EVDEV_TAG_EXTERNAL_KEYBOARD = (1 << 7),
+	EVDEV_TAG_TABLET_MODE_SWITCH = (1 << 8),
 };
 
 enum evdev_middlebutton_state {
@@ -376,6 +377,10 @@ struct fallback_dispatch {
 
 	struct device_coords rel;
 
+	struct {
+		int state;
+	} tablet_mode;
+
 	/* Bitmask of pressed keys used to ignore initial release events from
 	 * the kernel. */
 	unsigned long hw_key_mask[NLONGS(KEY_CNT)];
diff --git a/src/libinput.h b/src/libinput.h
index 0a9c9d87..1a06bc7c 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -624,6 +624,22 @@ enum libinput_switch {
 	 * LIBINPUT_SWITCH_STATE_OFF.
 	 */
 	LIBINPUT_SWITCH_LID = 1,
+
+	/**
+	 * This switch indicates whether the device is in normal laptop mode
+	 * or behaves like a tablet-like device where the primary
+	 * interaction is usually a touch screen. When in tablet mode, the
+	 * keyboard and touchpad are usually inaccessible.
+	 *
+	 * If the switch is in state @ref LIBINPUT_SWITCH_STATE_OFF, the
+	 * device is in laptop mode. If the switch is in state @ref
+	 * LIBINPUT_SWITCH_STATE_ON, the device is in tablet mode and the
+	 * keyboard or touchpad may not be  accessible.
+	 *
+	 * It is up to the caller to identify which devices are inaccessible
+	 * in tablet mode.
+	 */
+	LIBINPUT_SWITCH_TABLET_MODE,
 };
 
 /**
diff --git a/test/litest-device-thinkpad-extrabuttons.c b/test/litest-device-thinkpad-extrabuttons.c
new file mode 100644
index 00000000..84541493
--- /dev/null
+++ b/test/litest-device-thinkpad-extrabuttons.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2017 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "litest.h"
+#include "litest-int.h"
+
+static void litest_extrabuttons_setup(void)
+{
+	struct litest_device *d = litest_create_device(LITEST_THINKPAD_EXTRABUTTONS);
+	litest_set_current_device(d);
+}
+
+static struct input_id input_id = {
+	.bustype = 0x19,
+	.vendor = 0x17aa,
+	.product = 0x5054,
+};
+
+static int events[] = {
+	EV_KEY, KEY_MUTE,
+	EV_KEY, KEY_VOLUMEUP,
+	EV_KEY, KEY_VOLUMEDOWN,
+	EV_KEY, KEY_SCALE,
+	EV_KEY, KEY_SLEEP,
+	EV_KEY, KEY_FILE,
+	EV_KEY, KEY_PROG1,
+	EV_KEY, KEY_COFFEE,
+	EV_KEY, KEY_BACK,
+	EV_KEY, KEY_CONFIG,
+	EV_KEY, KEY_REFRESH,
+	EV_KEY, KEY_F20,
+	EV_KEY, KEY_F21,
+	EV_KEY, KEY_F24,
+	EV_KEY, KEY_SUSPEND,
+	EV_KEY, KEY_CAMERA,
+	EV_KEY, KEY_SEARCH,
+	EV_KEY, KEY_BRIGHTNESSDOWN,
+	EV_KEY, KEY_BRIGHTNESSUP,
+	EV_KEY, KEY_SWITCHVIDEOMODE,
+	EV_KEY, KEY_KBDILLUMTOGGLE,
+	EV_KEY, KEY_BATTERY,
+	EV_KEY, KEY_WLAN,
+	EV_KEY, KEY_UNKNOWN,
+	EV_KEY, KEY_ZOOM,
+	EV_KEY, KEY_FN_F1,
+	EV_KEY, KEY_FN_F10,
+	EV_KEY, KEY_FN_F11,
+	EV_KEY, KEY_VOICECOMMAND,
+	EV_KEY, KEY_BRIGHTNESS_MIN,
+
+	EV_SW, SW_RFKILL_ALL,
+	EV_SW, SW_TABLET_MODE,
+
+	-1, -1,
+};
+
+struct litest_test_device litest_thinkpad_extrabuttons_device = {
+	.type = LITEST_THINKPAD_EXTRABUTTONS,
+	.features = LITEST_KEYS | LITEST_SWITCH,
+	.shortname = "thinkpad-extrabuttons",
+	.setup = litest_extrabuttons_setup,
+	.interface = NULL,
+
+	.name = "ThinkPad Extra Buttons",
+	.id = &input_id,
+	.events = events,
+	.absinfo = NULL,
+};
diff --git a/test/litest.c b/test/litest.c
index 82ca14db..1f238431 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -417,6 +417,7 @@ extern struct litest_test_device litest_appletouch_device;
 extern struct litest_test_device litest_gpio_keys_device;
 extern struct litest_test_device litest_ignored_mouse_device;
 extern struct litest_test_device litest_wacom_mobilestudio_13hdt_pad_device;
+extern struct litest_test_device litest_thinkpad_extrabuttons_device;
 
 struct litest_test_device* devices[] = {
 	&litest_synaptics_clickpad_device,
@@ -485,6 +486,7 @@ struct litest_test_device* devices[] = {
 	&litest_gpio_keys_device,
 	&litest_ignored_mouse_device,
 	&litest_wacom_mobilestudio_13hdt_pad_device,
+	&litest_thinkpad_extrabuttons_device,
 	NULL,
 };
 
@@ -2189,6 +2191,9 @@ litest_switch_action(struct litest_device *dev,
 	case LIBINPUT_SWITCH_LID:
 		code = SW_LID;
 		break;
+	case LIBINPUT_SWITCH_TABLET_MODE:
+		code = SW_TABLET_MODE;
+		break;
 	default:
 		litest_abort_msg("Invalid switch %d", sw);
 		break;
diff --git a/test/litest.h b/test/litest.h
index 39aa882b..8643f32d 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -237,6 +237,7 @@ enum litest_device_type {
 	LITEST_GPIO_KEYS,
 	LITEST_IGNORED_MOUSE,
 	LITEST_WACOM_MOBILESTUDIO_PRO_16_PAD,
+	LITEST_THINKPAD_EXTRABUTTONS,
 };
 
 enum litest_device_feature {
diff --git a/test/test-switch.c b/test/test-switch.c
index 045f226e..59809798 100644
--- a/test/test-switch.c
+++ b/test/test-switch.c
@@ -29,6 +29,13 @@
 #include "libinput-util.h"
 #include "litest.h"
 
+static inline bool
+switch_has_lid(struct litest_device *dev)
+{
+	return libinput_device_switch_has_switch(dev->libinput_device,
+						 LIBINPUT_SWITCH_LID);
+}
+
 START_TEST(switch_has_cap)
 {
 	struct litest_device *dev = litest_current_device();
@@ -39,38 +46,60 @@ START_TEST(switch_has_cap)
 }
 END_TEST
 
-START_TEST(switch_has)
+START_TEST(switch_has_lid_switch)
 {
 	struct litest_device *dev = litest_current_device();
 
+	if (!libevdev_has_event_code(dev->evdev, EV_SW, SW_LID))
+		return;
+
 	ck_assert_int_eq(libinput_device_switch_has_switch(dev->libinput_device,
 							   LIBINPUT_SWITCH_LID),
 			 1);
 }
 END_TEST
 
+START_TEST(switch_has_tablet_mode_switch)
+{
+	struct litest_device *dev = litest_current_device();
+
+	if (!libevdev_has_event_code(dev->evdev, EV_SW, SW_TABLET_MODE))
+		return;
+
+	ck_assert_int_eq(libinput_device_switch_has_switch(dev->libinput_device,
+							   LIBINPUT_SWITCH_TABLET_MODE),
+			 1);
+}
+END_TEST
+
 START_TEST(switch_toggle)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
-	enum libinput_switch sw = LIBINPUT_SWITCH_LID;
+	enum libinput_switch sw = _i; /* ranged test */
 
 	litest_drain_events(li);
 
 	litest_switch_action(dev, sw, LIBINPUT_SWITCH_STATE_ON);
 	libinput_dispatch(li);
 
-	event = libinput_get_event(li);
-	litest_is_switch_event(event, sw, LIBINPUT_SWITCH_STATE_ON);
-	libinput_event_destroy(event);
+	if (libinput_device_switch_has_switch(dev->libinput_device, sw)) {
+		event = libinput_get_event(li);
+		litest_is_switch_event(event, sw, LIBINPUT_SWITCH_STATE_ON);
+		libinput_event_destroy(event);
+	} else {
+		litest_assert_empty_queue(li);
+	}
 
 	litest_switch_action(dev, sw, LIBINPUT_SWITCH_STATE_OFF);
 	libinput_dispatch(li);
 
-	event = libinput_get_event(li);
-	litest_is_switch_event(event, sw, LIBINPUT_SWITCH_STATE_OFF);
-	libinput_event_destroy(event);
+	if (libinput_device_switch_has_switch(dev->libinput_device, sw)) {
+		event = libinput_get_event(li);
+		litest_is_switch_event(event, sw, LIBINPUT_SWITCH_STATE_OFF);
+		libinput_event_destroy(event);
+	}
 
 	litest_assert_empty_queue(li);
 }
@@ -81,7 +110,10 @@ START_TEST(switch_toggle_double)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
 	struct libinput_event *event;
-	enum libinput_switch sw = LIBINPUT_SWITCH_LID;
+	enum libinput_switch sw = _i; /* ranged test */
+
+	if (!libinput_device_switch_has_switch(dev->libinput_device, sw))
+		return;
 
 	litest_drain_events(li);
 
@@ -123,9 +155,12 @@ START_TEST(switch_down_on_init)
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li;
 	struct libinput_event *event;
-	enum libinput_switch sw = LIBINPUT_SWITCH_LID;
+	enum libinput_switch sw = _i; /* ranged test */
 
-	if (!lid_switch_is_reliable(dev))
+	if (!libinput_device_switch_has_switch(dev->libinput_device, sw))
+		return;
+
+	if (sw == LIBINPUT_SWITCH_LID && !lid_switch_is_reliable(dev))
 		return;
 
 	litest_switch_action(dev, sw, LIBINPUT_SWITCH_STATE_ON);
@@ -166,7 +201,10 @@ START_TEST(switch_not_down_on_init)
 	struct libinput_event *event;
 	enum libinput_switch sw = LIBINPUT_SWITCH_LID;
 
-	if (lid_switch_is_reliable(dev))
+	if (!libinput_device_switch_has_switch(dev->libinput_device, sw))
+		return;
+
+	if (sw == LIBINPUT_SWITCH_LID && lid_switch_is_reliable(dev))
 		return;
 
 	litest_switch_action(dev, sw, LIBINPUT_SWITCH_STATE_ON);
@@ -203,6 +241,9 @@ START_TEST(lid_disable_touchpad)
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	touchpad = lid_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
@@ -239,6 +280,9 @@ START_TEST(lid_disable_touchpad_during_touch)
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	touchpad = lid_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
@@ -266,6 +310,9 @@ START_TEST(lid_disable_touchpad_edge_scroll)
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	touchpad = lid_init_paired_touchpad(li);
 	litest_enable_edge_scroll(touchpad);
 
@@ -302,6 +349,9 @@ START_TEST(lid_disable_touchpad_edge_scroll_interrupt)
 	struct libinput *li = sw->libinput;
 	struct libinput_event *event;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	touchpad = lid_init_paired_touchpad(li);
 	litest_enable_edge_scroll(touchpad);
 
@@ -341,6 +391,9 @@ START_TEST(lid_disable_touchpad_already_open)
 	struct litest_device *touchpad;
 	struct libinput *li = sw->libinput;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	touchpad = lid_init_paired_touchpad(li);
 	litest_disable_tap(touchpad->libinput_device);
 	litest_drain_events(li);
@@ -373,6 +426,9 @@ START_TEST(lid_open_on_key)
 	struct libinput *li = sw->libinput;
 	struct libinput_event *event;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 
 	litest_switch_action(sw,
@@ -409,6 +465,9 @@ START_TEST(lid_open_on_key_touchpad_enabled)
 	struct litest_device *keyboard, *touchpad;
 	struct libinput *li = sw->libinput;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 	touchpad = litest_add_device(li, LITEST_SYNAPTICS_I2C);
 
@@ -501,6 +560,9 @@ START_TEST(lid_update_hw_on_key)
 	struct litest_device *keyboard;
 	struct libinput_event *event;
 
+	if (!switch_has_lid(sw))
+		return;
+
 	sleep(5);
 	keyboard = litest_add_device(li, LITEST_KEYBOARD);
 
@@ -612,11 +674,15 @@ END_TEST
 void
 litest_setup_tests_lid(void)
 {
+	struct range switches = { LIBINPUT_SWITCH_LID,
+				  LIBINPUT_SWITCH_TABLET_MODE + 1};
+
 	litest_add("switch:has", switch_has_cap, LITEST_SWITCH, LITEST_ANY);
-	litest_add("switch:has", switch_has, LITEST_SWITCH, LITEST_ANY);
-	litest_add("switch:toggle", switch_toggle, LITEST_SWITCH, LITEST_ANY);
-	litest_add("switch:toggle", switch_toggle_double, LITEST_SWITCH, LITEST_ANY);
-	litest_add("switch:toggle", switch_down_on_init, LITEST_SWITCH, LITEST_ANY);
+	litest_add("switch:has", switch_has_lid_switch, LITEST_SWITCH, LITEST_ANY);
+	litest_add("switch:has", switch_has_tablet_mode_switch, LITEST_SWITCH, LITEST_ANY);
+	litest_add_ranged("switch:toggle", switch_toggle, LITEST_SWITCH, LITEST_ANY, &switches);
+	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);
-- 
2.13.5



More information about the wayland-devel mailing list