[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