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) */