From 0322403ea455f55ad2907381b52803ee4ee47839 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 16 Jan 2024 16:10:59 +1000 Subject: [PATCH] tablet: fix tilt handling for even-ranged tablets The tablet tilt range may be set as [-N, M] in which case we assume that a value of zero is vertical (and thus should result in a libinput tilt value of zero). Unfortunately some tablets report an even total value range, e.g. [-64, 63] so zero is not actually the mathematical center of the axis. Fix this by bumping the axis maximum so zero becomes the logical center. All devices we've seen so far have [-A, (A-1)] as range so bumping it by one makes it symmetric. --- src/evdev-tablet.c | 54 +++++++++++++++++++++++++++++- test/test-tablet.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 3b454f02..541485f1 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -368,7 +368,7 @@ normalize_pressure(const struct input_absinfo *absinfo, static inline double adjust_tilt(const struct input_absinfo *absinfo) { - double value = (absinfo->value - absinfo->minimum) / absinfo_range(absinfo); + double value = absinfo_normalize(absinfo); const int WACOM_MAX_DEGREES = 64; /* If resolution is nonzero, it's in units/radian. But require @@ -2562,6 +2562,57 @@ tablet_reject_device(struct evdev_device *device) return true; } +static void +tablet_fix_tilt(struct tablet_dispatch *tablet, + struct evdev_device *device) +{ + struct libevdev *evdev = device->evdev; + + if (libevdev_has_event_code(evdev, EV_ABS, ABS_TILT_X) != + libevdev_has_event_code(evdev, EV_ABS, ABS_TILT_Y)) { + libevdev_disable_event_code(evdev, EV_ABS, ABS_TILT_X); + libevdev_disable_event_code(evdev, EV_ABS, ABS_TILT_Y); + return; + } + + if (!libevdev_has_event_code(evdev, EV_ABS, ABS_TILT_X)) + return; + + /* Wacom has three types of devices: + * - symmetrical: [-90, 90], like the ISDv4 524c + * - asymmetrical: [-64, 63], like the Cintiq l3HDT + * - zero-based: [0, 127], like the Cintiq 12WX + * + * Note how the latter two cases have an even range and thus do + * not have a logical center value. But this is tilt and at + * least in the asymmetrical case we assume that hardware zero + * means vertical. So we cheat and adjust the range depending + * on whether it's odd, then use the center value. + * + * Since it's always the max that's one too low let's go with that and + * fix it if we run into a device where that isn't the case. + */ + for (unsigned int axis = ABS_TILT_X; axis <= ABS_TILT_Y; axis++) { + struct input_absinfo abs = *libevdev_get_abs_info(evdev, axis); + + /* Don't touch axes reporting radians */ + if (abs.resolution != 0) + continue; + + if ((int)absinfo_range(&abs) % 2 == 1) + continue; + + abs.maximum += 1; + libevdev_set_abs_info(evdev, axis, &abs); + + evdev_log_debug(device, + "Adjusting %s range to [%d, %d]\n", + libevdev_event_code_get_name(EV_ABS, axis), + abs.minimum, + abs.maximum); + } +} + static int tablet_init(struct tablet_dispatch *tablet, struct evdev_device *device) @@ -2592,6 +2643,7 @@ tablet_init(struct tablet_dispatch *tablet, libevdev_disable_event_code(evdev, EV_KEY, BTN_TOOL_LENS); } + tablet_fix_tilt(tablet, device); tablet_init_calibration(tablet, device); tablet_init_proximity_threshold(tablet, device); rc = tablet_init_accel(tablet, device); diff --git a/test/test-tablet.c b/test/test-tablet.c index 7ebd5049..b94f3568 100644 --- a/test/test-tablet.c +++ b/test/test-tablet.c @@ -37,6 +37,12 @@ #include "litest.h" #include "util-input-event.h" +enum { + TILT_MINIMUM, + TILT_CENTER, + TILT_MAXIMUM, +}; + static inline unsigned int pick_stylus_or_btn0(struct litest_device *dev) { @@ -4487,6 +4493,80 @@ START_TEST(tilt_y) } END_TEST +START_TEST(tilt_fixed_points) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet_tool *tev; + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { ABS_PRESSURE, 0 }, + { -1, -1 } + }; + int testcase = _i; /* ranged test */ + int axis_value; + double expected; + + /* On devices with a range of [-N, M], make sure we calculate the hw zero position + * as zero and that the respective min/max resolve to our (hardcoded) min/max degree + * values + */ + const struct input_absinfo *abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_X); + if (abs->minimum >= 0) + return; + + /* If the tablet reports physical resolutions we don't need to test them */ + if (abs->resolution != 0) + return; + + /* see tablet_fix_tilt() */ + bool is_adjusted = (int)absinfo_range(abs) % 2 == 0; + + switch (testcase) { + case TILT_MINIMUM: + axis_value = abs->minimum; + expected = -64.0; + break; + case TILT_CENTER: + axis_value = 0; + expected = 0.0; + break; + case TILT_MAXIMUM: + axis_value = abs->maximum; + expected = 64.0; + break; + default: + abort(); + } + + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_ABS, ABS_TILT_X, axis_value); + litest_event(dev, EV_ABS, ABS_TILT_Y, axis_value); + litest_pop_event_frame(dev); + + libinput_dispatch(li); + event = libinput_get_event(li); + tev = litest_is_tablet_event(event, + LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY); + + double tx = libinput_event_tablet_tool_get_tilt_x(tev); + double ty = libinput_event_tablet_tool_get_tilt_y(tev); + ck_assert_double_eq(tx, expected); + if (is_adjusted) { + ck_assert_double_ge(ty, expected - 1); + ck_assert_double_lt(ty, expected); + } else { + ck_assert_double_eq(ty, expected); + } + + libinput_event_destroy(event); +} +END_TEST + START_TEST(relative_no_profile) { struct litest_device *dev = litest_current_device(); @@ -6152,6 +6232,7 @@ TEST_COLLECTION(tablet) struct range with_timeout = { 0, 2 }; struct range xyaxes = { ABS_X, ABS_Y + 1 }; struct range lh_transitions = {0, 16}; /* 2 bits for in, 2 bits for out */ + struct range tilt_cases = {TILT_MINIMUM, TILT_MAXIMUM + 1}; litest_add(tool_ref, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); litest_add(tool_user_data, LITEST_TABLET | LITEST_TOOL_SERIAL, LITEST_ANY); @@ -6213,6 +6294,7 @@ TEST_COLLECTION(tablet) litest_add(tilt_not_available, LITEST_TABLET, LITEST_TILT); litest_add(tilt_x, LITEST_TABLET|LITEST_TILT, LITEST_ANY); litest_add(tilt_y, LITEST_TABLET|LITEST_TILT, LITEST_ANY); + litest_add_ranged(tilt_fixed_points, LITEST_TABLET|LITEST_TILT, LITEST_ANY, &tilt_cases); litest_add_for_device(left_handed, LITEST_WACOM_INTUOS); litest_add_for_device(left_handed_tilt, LITEST_WACOM_INTUOS); litest_add_for_device(left_handed_mouse_rotation, LITEST_WACOM_INTUOS);