Commit 6229df18 authored by Peter Hutterer's avatar Peter Hutterer

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 #274Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 35428559
Pipeline #33935 passed with stages
in 2 minutes and 20 seconds
......@@ -28,6 +28,10 @@
#include <stdbool.h>
#include <limits.h>
#if HAVE_LIBWACOM
#include <libwacom/libwacom.h>
#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;
}
......@@ -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*
......
......@@ -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) */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment