From: Peter Hutterer Date: Wed, 20 Sep 2017 04:40:59 +0000 (+1000) Subject: fallback: suspend internal keyboards and trackpoints on tablet-mode switch X-Git-Tag: 1.8.901~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=84d49ee49cc30927debb4104af42a92738837ada;p=platform%2Fupstream%2Flibinput.git fallback: suspend internal keyboards and trackpoints on tablet-mode switch 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 --- diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index adbb091b..13e130ac 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 @@ -181,7 +190,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; } @@ -992,10 +1001,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 @@ -1263,6 +1272,8 @@ fallback_interface_remove(struct evdev_dispatch *evdev_dispatch) struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); struct paired_keyboard *kbd; + libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener); + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { if (!kbd->device) continue; @@ -1299,7 +1310,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, @@ -1377,11 +1388,94 @@ fallback_lid_pair_keyboard(struct evdev_device *lid_switch, "lid: too many internal keyboards\n"); } +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 @@ -1403,6 +1497,14 @@ fallback_interface_device_removed(struct evdev_device *device, libinput_device_init_event_listener(&kbd->listener); kbd->device = 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 = { @@ -1602,8 +1704,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 c306305b..3a499bf7 100644 --- a/test/test-switch.c +++ b/test/test-switch.c @@ -864,6 +864,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) { @@ -896,4 +1051,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); }