diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 3d8363cc..f99fd12c 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -85,6 +85,21 @@ tablet_mark_all_axes_changed(struct tablet_dispatch *tablet, tablet_set_status(tablet, TABLET_AXES_UPDATED); } +static void +tablet_change_to_left_handed(struct evdev_device *device) +{ + struct tablet_dispatch *tablet = + (struct tablet_dispatch*)device->dispatch; + + if (device->left_handed.enabled == device->left_handed.want_enabled) + return; + + if (!tablet_has_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY)) + return; + + device->left_handed.enabled = device->left_handed.want_enabled; +} + static void tablet_update_tool(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -120,6 +135,12 @@ normalize_tilt(const struct input_absinfo * absinfo) { return (value * 2) - 1; } +static inline int32_t +invert_axis(const struct input_absinfo *absinfo) +{ + return absinfo->maximum - (absinfo->value - absinfo->minimum); +} + static void tablet_check_notify_axes(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -142,7 +163,10 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, switch (a) { case LIBINPUT_TABLET_AXIS_X: case LIBINPUT_TABLET_AXIS_Y: - tablet->axes[a] = absinfo->value; + if (device->left_handed.enabled) + tablet->axes[a] = invert_axis(absinfo); + else + tablet->axes[a] = absinfo->value; break; case LIBINPUT_TABLET_AXIS_DISTANCE: case LIBINPUT_TABLET_AXIS_PRESSURE: @@ -481,6 +505,8 @@ tablet_flush(struct tablet_dispatch *tablet, tablet->axes); tablet_set_status(tablet, TABLET_TOOL_OUT_OF_PROXIMITY); tablet_unset_status(tablet, TABLET_TOOL_LEAVING_PROXIMITY); + + tablet_change_to_left_handed(device); } /* Update state */ @@ -585,5 +611,7 @@ evdev_tablet_create(struct evdev_device *device) return NULL; } + evdev_init_left_handed(device, tablet_change_to_left_handed); + return &tablet->base; } diff --git a/test/tablet.c b/test/tablet.c index c38302af..922915fc 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -243,6 +243,119 @@ START_TEST(motion) } END_TEST +START_TEST(left_handed) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tablet_event; + double libinput_max_x, libinput_max_y; + double last_x, last_y; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { -1, -1 } + }; + + ck_assert(libinput_device_config_left_handed_is_available(dev->libinput_device)); + + libinput_device_get_size (dev->libinput_device, + &libinput_max_x, + &libinput_max_y); + + /* Test that left-handed mode doesn't go into effect until the tool has + * left proximity of the tablet. In order to test this, we have to bring + * the tool into proximity and make sure libinput processes the + * proximity events so that it updates it's internal tablet state, and + * then try setting it to left-handed mode. */ + litest_tablet_proximity_in(dev, 0, 100, axes); + libinput_dispatch(li); + libinput_device_config_left_handed_set(dev->libinput_device, 1); + + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_AXIS, -1); + + while ((event = libinput_get_event(li))) { + tablet_event = libinput_event_get_tablet_event(event); + + last_x = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_X); + last_y = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_Y); + + litest_assert_double_eq(last_x, 0); + litest_assert_double_eq(last_y, libinput_max_y); + + libinput_event_destroy(event); + } + + litest_tablet_motion(dev, 100, 0, axes); + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_AXIS, -1); + + while ((event = libinput_get_event(li))) { + double x, y; + tablet_event = libinput_event_get_tablet_event(event); + + x = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_X); + y = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_Y); + + litest_assert_double_eq(x, libinput_max_x); + litest_assert_double_eq(y, 0); + + litest_assert_double_gt(x, last_x); + litest_assert_double_lt(y, last_y); + + libinput_event_destroy(event); + } + + litest_tablet_proximity_out(dev); + litest_drain_events(li); + + /* Since we've drained the events and libinput's aware the tool is out + * of proximity, it should have finally transitioned into left-handed + * mode, so the axes should be inverted once we bring it back into + * proximity */ + litest_tablet_proximity_in(dev, 0, 100, axes); + + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_AXIS, -1); + + while ((event = libinput_get_event(li))) { + tablet_event = libinput_event_get_tablet_event(event); + + last_x = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_X); + last_y = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_Y); + + litest_assert_double_eq(last_x, libinput_max_x); + litest_assert_double_eq(last_y, 0); + + libinput_event_destroy(event); + } + + litest_tablet_motion(dev, 100, 0, axes); + litest_wait_for_event_of_type(li, LIBINPUT_EVENT_TABLET_AXIS, -1); + + while ((event = libinput_get_event(li))) { + double x, y; + tablet_event = libinput_event_get_tablet_event(event); + + x = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_X); + y = libinput_event_tablet_get_axis_value( + tablet_event, LIBINPUT_TABLET_AXIS_Y); + + litest_assert_double_eq(x, 0); + litest_assert_double_eq(y, libinput_max_y); + + litest_assert_double_lt(x, last_x); + litest_assert_double_gt(y, last_y); + + libinput_event_destroy(event); + } +} +END_TEST + START_TEST(motion_event_state) { struct litest_device *dev = litest_current_device(); @@ -873,6 +986,7 @@ main(int argc, char **argv) litest_add("tablet:proximity", bad_distance_events, LITEST_TABLET | LITEST_DISTANCE, LITEST_ANY); litest_add("tablet:motion", motion, LITEST_TABLET, LITEST_ANY); litest_add("tablet:motion", motion_event_state, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:left_handed", left_handed, LITEST_TABLET, LITEST_ANY); litest_add("tablet:normalization", normalization, LITEST_TABLET, LITEST_ANY); litest_add("tablet:pad", pad_buttons_ignored, LITEST_TABLET, LITEST_ANY);