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
abort();
}
- return dispatch->tablet_mode.state ?
+ return dispatch->tablet_mode.sw.state ?
LIBINPUT_SWITCH_STATE_ON :
LIBINPUT_SWITCH_STATE_OFF;
}
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
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;
}
}
- if (dispatch->tablet_mode.state) {
+ if (dispatch->tablet_mode.sw.state) {
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_TABLET_MODE,
"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
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 = {
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 *
}
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)
{
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);
}