From 6229df184e8a03e76ba99483e7f9ecdd9ef02f4a Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 30 Apr 2019 14:52:28 +1000 Subject: [PATCH] touchpad: rotate the touch part of tablets Tablets in left-handed mode are rotated, so we need to rotate the touchpad part of them too. This doesn't affect all tablets though, some of them are symmetrical and the left-handed mode merely changes the button order around (some of the earlier Bamboos). So we rely on libwacom to tell us which device must be rotated. The rotation itself is done on the input coordinate itself as we get it. This way any software buttons, palm zones, etc. are automatically handled by rest of the code. Fixes #274 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 112 ++++++++++++++++++++++++++++++++++++---- src/evdev-mt-touchpad.h | 5 ++ test/test-touchpad.c | 60 +++++++++++++++++++++ 3 files changed, 168 insertions(+), 9 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 2c79dbec..34693f31 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -28,6 +28,10 @@ #include #include +#if HAVE_LIBWACOM +#include +#endif + #include "quirks.h" #include "evdev-mt-touchpad.h" @@ -464,6 +468,30 @@ tp_get_delta(struct tp_touch *t) return delta; } +static inline int32_t +rotated(struct tp_dispatch *tp, unsigned int code, int value) +{ + const struct input_absinfo *absinfo; + + if (!tp->device->left_handed.enabled || + !tp->left_handed.rotate) + return value; + + switch (code) { + case ABS_X: + case ABS_MT_POSITION_X: + absinfo = tp->device->abs.absinfo_x; + break; + case ABS_Y: + case ABS_MT_POSITION_Y: + absinfo = tp->device->abs.absinfo_y; + break; + default: + abort(); + } + return absinfo->maximum - (value - absinfo->minimum); +} + static void tp_process_absolute(struct tp_dispatch *tp, const struct input_event *e, @@ -476,7 +504,7 @@ tp_process_absolute(struct tp_dispatch *tp, evdev_device_check_abs_axis_range(tp->device, e->code, e->value); - t->point.x = e->value; + t->point.x = rotated(tp, e->code, e->value); t->time = time; t->dirty = true; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -485,7 +513,7 @@ tp_process_absolute(struct tp_dispatch *tp, evdev_device_check_abs_axis_range(tp->device, e->code, e->value); - t->point.y = e->value; + t->point.y = rotated(tp, e->code, e->value); t->time = time; t->dirty = true; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -536,7 +564,7 @@ tp_process_absolute_st(struct tp_dispatch *tp, evdev_device_check_abs_axis_range(tp->device, e->code, e->value); - t->point.x = e->value; + t->point.x = rotated(tp, e->code, e->value); t->time = time; t->dirty = true; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -545,7 +573,7 @@ tp_process_absolute_st(struct tp_dispatch *tp, evdev_device_check_abs_axis_range(tp->device, e->code, e->value); - t->point.y = e->value; + t->point.y = rotated(tp, e->code, e->value); t->time = time; t->dirty = true; tp->queued |= TOUCHPAD_EVENT_MOTION; @@ -3700,11 +3728,80 @@ tp_change_to_left_handed(struct evdev_device *device) device->left_handed.enabled = device->left_handed.want_enabled; } +static bool +tp_init_left_handed_rotation(struct tp_dispatch *tp, + struct evdev_device *device) +{ + bool rotate = false; +#if HAVE_LIBWACOM + WacomDeviceDatabase *db; + WacomDevice **devices = NULL, + **d; + WacomDevice *dev; + uint32_t vid = evdev_device_get_id_vendor(device), + pid = evdev_device_get_id_product(device); + + db = libwacom_database_new(); + if (!db) { + evdev_log_info(device, + "Failed to initialize libwacom context.\n"); + goto out; + } + + /* Check if we have a device with the same vid/pid. If not, + we need to loop through all devices and check their paired + device. */ + dev = libwacom_new_from_usbid(db, vid, pid, NULL); + if (dev) { + rotate = libwacom_is_reversible(dev); + libwacom_destroy(dev); + goto out; + } + + devices = libwacom_list_devices_from_database(db, NULL); + if (!devices) + goto out; + d = devices; + while(*d) { + const WacomMatch *paired; + + paired = libwacom_get_paired_device(*d); + if (paired && + libwacom_match_get_vendor_id(paired) == vid && + libwacom_match_get_product_id(paired) == pid) { + rotate = libwacom_is_reversible(dev); + break; + } + d++; + } + + free(devices); +out: + if (db) + libwacom_database_destroy(db); +#endif + + return rotate; +} + +static void +tp_init_left_handed(struct tp_dispatch *tp, + struct evdev_device *device) +{ + bool want_left_handed = true; + + if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) + want_left_handed = false; + if (want_left_handed) + evdev_init_left_handed(device, tp_change_to_left_handed); + + tp->left_handed.rotate = tp_init_left_handed_rotation(tp, device); +} + struct evdev_dispatch * evdev_mt_touchpad_create(struct evdev_device *device) { struct tp_dispatch *tp; - bool want_left_handed = true; evdev_tag_touchpad(device, device->udev_device); @@ -3723,10 +3820,7 @@ evdev_mt_touchpad_create(struct evdev_device *device) tp->sendevents.config.get_mode = tp_sendevents_get_mode; tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode; - if (device->model_flags & EVDEV_MODEL_APPLE_TOUCHPAD_ONEBUTTON) - want_left_handed = false; - if (want_left_handed) - evdev_init_left_handed(device, tp_change_to_left_handed); + tp_init_left_handed(tp, device); return &tp->base; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 2e257ea1..c5c15711 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -485,6 +485,11 @@ struct tp_dispatch { struct libinput_event_listener listener; struct evdev_device *tablet_mode_switch; } tablet_mode_switch; + + struct { + /* true if the axes need rotation when left-handed is on*/ + bool rotate; + } left_handed; }; static inline struct tp_dispatch* diff --git a/test/test-touchpad.c b/test/test-touchpad.c index fd3615f4..6d4f7abe 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -2096,6 +2096,7 @@ START_TEST(touchpad_palm_clickfinger_size_2fg) litest_assert_empty_queue(li); } END_TEST + START_TEST(touchpad_left_handed) { struct litest_device *dev = litest_current_device(); @@ -2430,6 +2431,64 @@ START_TEST(touchpad_left_handed_clickpad_delayed) } END_TEST +static inline bool +touchpad_has_rotation(struct libevdev *evdev) +{ + return libevdev_get_id_vendor(evdev) == VENDOR_ID_WACOM; +} + +START_TEST(touchpad_left_handed_rotation) +{ + struct litest_device *dev = litest_current_device(); + struct libinput_device *d = dev->libinput_device; + struct libinput *li = dev->libinput; + enum libinput_config_status status; + struct libinput_event *event; + struct libinput_event_pointer *p; + bool rotate = touchpad_has_rotation(dev->evdev); + + if (!libinput_device_config_left_handed_is_available(d)) + return; + + status = libinput_device_config_left_handed_set(d, 1); + ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS); + + litest_drain_events(li); + + litest_touch_down(dev, 0, 20, 80); + litest_touch_move_to(dev, 0, 20, 80, 80, 20, 20); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + event = libinput_get_event(li); + ck_assert_notnull(event); + do { + double x, y, ux, uy; + + p = litest_is_motion_event(event); + + x = libinput_event_pointer_get_dx(p); + y = libinput_event_pointer_get_dy(p); + ux = libinput_event_pointer_get_dx_unaccelerated(p); + uy = libinput_event_pointer_get_dy_unaccelerated(p); + + if (rotate) { + ck_assert_double_lt(x, 0); + ck_assert_double_gt(y, 0); + ck_assert_double_lt(ux, 0); + ck_assert_double_gt(uy, 0); + } else { + ck_assert_double_gt(x, 0); + ck_assert_double_lt(y, 0); + ck_assert_double_gt(ux, 0); + ck_assert_double_lt(uy, 0); + } + + libinput_event_destroy(event); + } while ((event = libinput_get_event(li))); +} +END_TEST + static void hover_continue(struct litest_device *dev, unsigned int slot, int x, int y) @@ -7010,6 +7069,7 @@ TEST_COLLECTION(touchpad) litest_add("touchpad:left-handed", touchpad_left_handed_tapping_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); litest_add("touchpad:left-handed", touchpad_left_handed_delayed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD); litest_add("touchpad:left-handed", touchpad_left_handed_clickpad_delayed, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD); + litest_add("touchpad:left-handed", touchpad_left_handed_rotation, LITEST_TOUCHPAD, LITEST_ANY); /* Semi-MT hover tests aren't generic, they only work on this device and * ignore the semi-mt capability (it doesn't matter for the tests) */