From 8edae426e369b876813c224f619135fca5cba2e5 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 16 Feb 2015 15:19:44 +1000 Subject: [PATCH] tablet: support z-rotation for the mouse/lens tool Needs to be calculated from the x/y tilt values, the mouse has a fixed offset of 175 degrees counterclockwise. Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires Reviewed-by: Stephen Chandler Paul --- src/evdev-tablet.c | 88 ++++++++++++++++++++++++++++++++++++++---- src/libinput-private.h | 2 +- src/libinput.c | 1 + src/libinput.h | 6 +++ test/tablet.c | 77 ++++++++++++++++++++++++++++++++++++ tools/event-debug.c | 8 ++++ 6 files changed, 173 insertions(+), 9 deletions(-) diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index c502c213..bfdbcc4a 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -71,12 +71,25 @@ static int tablet_device_has_axis(struct tablet_dispatch *tablet, enum libinput_tablet_axis axis) { + struct libevdev *evdev = tablet->device->evdev; + bool has_axis = false; unsigned int code; - code = axis_to_evcode(axis); - return libevdev_has_event_code(tablet->device->evdev, - EV_ABS, - code); + if (axis == LIBINPUT_TABLET_AXIS_ROTATION_Z) { + has_axis = (libevdev_has_event_code(evdev, + EV_ABS, + ABS_TILT_X) && + libevdev_has_event_code(evdev, + EV_ABS, + ABS_TILT_Y)); + } else { + code = axis_to_evcode(axis); + has_axis = libevdev_has_event_code(evdev, + EV_ABS, + code); + } + + return has_axis; } static void @@ -200,6 +213,32 @@ invert_axis(const struct input_absinfo *absinfo) return absinfo->maximum - (absinfo->value - absinfo->minimum); } +static void +convert_tilt_to_rotation(struct tablet_dispatch *tablet) +{ + const int offset = 5; + double x, y; + double angle = 0.0; + + /* Wacom Intuos 4, 5, Pro mouse calculates rotation from the x/y tilt + values. The device has a 175 degree CCW hardware offset but since we use + atan2 the effective offset is just 5 degrees. + */ + x = tablet->axes[LIBINPUT_TABLET_AXIS_TILT_X]; + y = tablet->axes[LIBINPUT_TABLET_AXIS_TILT_Y]; + clear_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_X); + clear_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_Y); + + /* atan2 is CCW, we want CW -> negate x */ + if (x || y) + angle = ((180.0 * atan2(-x, y)) / M_PI); + + angle = fmod(360 + angle - offset, 360); + + tablet->axes[LIBINPUT_TABLET_AXIS_ROTATION_Z] = angle; + set_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_ROTATION_Z); +} + static void tablet_check_notify_axes(struct tablet_dispatch *tablet, struct evdev_device *device, @@ -209,12 +248,30 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, struct libinput_device *base = &device->base; bool axis_update_needed = false; int a; + double axes[LIBINPUT_TABLET_AXIS_MAX + 1] = {0}; for (a = LIBINPUT_TABLET_AXIS_X; a <= LIBINPUT_TABLET_AXIS_MAX; a++) { const struct input_absinfo *absinfo; - if (!bit_is_set(tablet->changed_axes, a)) + if (!bit_is_set(tablet->changed_axes, a)) { + axes[a] = tablet->axes[a]; continue; + } + + axis_update_needed = true; + + /* ROTATION_Z is higher than TILT_X/Y so we know that the + tilt axes are already normalized and set */ + if (a == LIBINPUT_TABLET_AXIS_ROTATION_Z) { + if (tablet->current_tool_type == LIBINPUT_TOOL_MOUSE || + tablet->current_tool_type == LIBINPUT_TOOL_LENS) { + convert_tilt_to_rotation(tablet); + axes[LIBINPUT_TABLET_AXIS_TILT_X] = 0; + axes[LIBINPUT_TABLET_AXIS_TILT_Y] = 0; + axes[a] = tablet->axes[a]; + } + continue; + } absinfo = libevdev_get_abs_info(device->evdev, axis_to_evcode(a)); @@ -241,7 +298,7 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, break; } - axis_update_needed = true; + axes[a] = tablet->axes[a]; } /* We need to make sure that we check that the tool is not out of @@ -258,13 +315,13 @@ tablet_check_notify_axes(struct tablet_dispatch *tablet, tool, LIBINPUT_TOOL_PROXIMITY_IN, tablet->changed_axes, - tablet->axes); + axes); else tablet_notify_axis(base, time, tool, tablet->changed_axes, - tablet->axes); + axes); } memset(tablet->changed_axes, 0, sizeof(tablet->changed_axes)); @@ -455,6 +512,9 @@ tool_set_bits_from_libwacom(const struct tablet_dispatch *tablet, copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_X); copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_Y); break; + case WSTYLUS_PUCK: + copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_ROTATION_Z); + break; default: break; } @@ -492,6 +552,10 @@ tool_set_bits(const struct tablet_dispatch *tablet, copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_X); copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_TILT_Y); break; + case LIBINPUT_TOOL_MOUSE: + case LIBINPUT_TOOL_LENS: + copy_axis_cap(tablet, tool, LIBINPUT_TABLET_AXIS_ROTATION_Z); + break; default: break; } @@ -650,6 +714,14 @@ sanitize_tablet_axes(struct tablet_dispatch *tablet) else tablet->axes[LIBINPUT_TABLET_AXIS_PRESSURE] = 0; } + + /* If we have a mouse/lens cursor and the tilt changed, the rotation + changed. Mark this, calculate the angle later */ + if ((tablet->current_tool_type == LIBINPUT_TOOL_MOUSE || + tablet->current_tool_type == LIBINPUT_TOOL_LENS) && + (bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_X) || + bit_is_set(tablet->changed_axes, LIBINPUT_TABLET_AXIS_TILT_Y))) + set_bit(tablet->changed_axes, LIBINPUT_TABLET_AXIS_ROTATION_Z); } static void diff --git a/src/libinput-private.h b/src/libinput-private.h index 9f326554..20d9e43b 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -30,7 +30,7 @@ #include "libinput.h" #include "libinput-util.h" -#define LIBINPUT_TABLET_AXIS_MAX LIBINPUT_TABLET_AXIS_TILT_Y +#define LIBINPUT_TABLET_AXIS_MAX LIBINPUT_TABLET_AXIS_ROTATION_Z struct libinput_source; diff --git a/src/libinput.c b/src/libinput.c index 5ed3ffc2..87ddc69c 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -584,6 +584,7 @@ libinput_event_tablet_get_axis_value(struct libinput_event_tablet *event, case LIBINPUT_TABLET_AXIS_PRESSURE: case LIBINPUT_TABLET_AXIS_TILT_X: case LIBINPUT_TABLET_AXIS_TILT_Y: + case LIBINPUT_TABLET_AXIS_ROTATION_Z: return event->axes[axis]; default: return 0; diff --git a/src/libinput.h b/src/libinput.h index 241b2b44..bbefc38f 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -142,6 +142,7 @@ enum libinput_tablet_axis { LIBINPUT_TABLET_AXIS_PRESSURE = 4, LIBINPUT_TABLET_AXIS_TILT_X = 5, LIBINPUT_TABLET_AXIS_TILT_Y = 6, + LIBINPUT_TABLET_AXIS_ROTATION_Z = 7, }; /** @@ -1050,6 +1051,11 @@ libinput_event_tablet_axis_has_changed(struct libinput_event_tablet *event, * - @ref LIBINPUT_TABLET_AXIS_TILT_X and @ref LIBINPUT_TABLET_AXIS_TILT_Y - * normalized value between -1 and 1 that indicates the X or Y tilt of the * tool + * - @ref LIBINPUT_TABLET_AXIS_ROTATION_Z - The z rotation of the tool in + * degrees, clockwise from the tool's logical neutral position. For the + * @ref LIBINPUT_TOOL_MOUSE and @ref LIBINPUT_TOOL_LENS tools the logical + * neutral position is pointing to the current logical north of the + * tablet. * * @note This function may be called for a specific axis even if * libinput_event_tablet_axis_has_changed() returns 0 for that axis. diff --git a/test/tablet.c b/test/tablet.c index 9c3d76d7..6eca5c42 100644 --- a/test/tablet.c +++ b/test/tablet.c @@ -1197,6 +1197,82 @@ START_TEST(mouse_buttons) } END_TEST +START_TEST(mouse_rotation) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_tablet *tev; + int angle; + int tilt_center_x, tilt_center_y; + const struct input_absinfo *abs; + double val, old_val = 0; + + struct axis_replacement axes[] = { + { ABS_DISTANCE, 10 }, + { ABS_TILT_X, 0 }, + { ABS_TILT_Y, 0 }, + { -1, -1 } + }; + + if (!libevdev_has_event_code(dev->evdev, + EV_KEY, + BTN_TOOL_MOUSE)) + return; + + abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_X); + ck_assert_notnull(abs); + tilt_center_x = (abs->maximum - abs->minimum + 1) / 2; + + abs = libevdev_get_abs_info(dev->evdev, ABS_TILT_Y); + ck_assert_notnull(abs); + tilt_center_y = (abs->maximum - abs->minimum + 1) / 2; + + litest_drain_events(li); + + litest_push_event_frame(dev); + litest_tablet_proximity_in(dev, 10, 10, axes); + litest_event(dev, EV_KEY, BTN_TOOL_MOUSE, 1); + litest_pop_event_frame(dev); + + litest_drain_events(li); + + /* cos/sin are 90 degrees offset from the north-is-zero that + libinput uses. 175 is the CCW offset in the mouse HW */ + for (angle = 5; angle < 360; angle += 5) { + double a = (angle - 90 - 175)/180.0 * M_PI; + int x, y; + + x = cos(a) * 20 + tilt_center_x; + y = sin(a) * 20 + tilt_center_y; + + litest_event(dev, EV_ABS, ABS_TILT_X, x); + litest_event(dev, EV_ABS, ABS_TILT_Y, y); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + + litest_wait_for_event_of_type(li, + LIBINPUT_EVENT_TABLET_AXIS, + -1); + event = libinput_get_event(li); + tev = libinput_event_get_tablet_event(event); + ck_assert(libinput_event_tablet_axis_has_changed(tev, + LIBINPUT_TABLET_AXIS_ROTATION_Z)); + val = libinput_event_tablet_get_axis_value(tev, + LIBINPUT_TABLET_AXIS_ROTATION_Z); + + /* rounding error galore, we can't test for anything more + precise than these */ + litest_assert_double_lt(val, 360.0); + litest_assert_double_gt(val, old_val); + litest_assert_double_lt(val, angle + 5); + + old_val = val; + libinput_event_destroy(event); + litest_assert_empty_queue(li); + } +} +END_TEST + int main(int argc, char **argv) { @@ -1219,6 +1295,7 @@ main(int argc, char **argv) litest_add("tablet:pad", pad_buttons_ignored, LITEST_TABLET, LITEST_ANY); litest_add("tablet:mouse", mouse_tool, LITEST_TABLET, LITEST_ANY); litest_add("tablet:mouse", mouse_buttons, LITEST_TABLET, LITEST_ANY); + litest_add("tablet:mouse", mouse_rotation, LITEST_TABLET, LITEST_ANY); return litest_run(argc, argv); } diff --git a/tools/event-debug.c b/tools/event-debug.c index 09f208c3..3503143e 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -293,6 +293,7 @@ print_tablet_axis_event(struct libinput_event *ev) struct libinput_event_tablet *t = libinput_event_get_tablet_event(ev); double x, y; double dist, pressure; + double rotation; print_event_time(libinput_event_tablet_get_time(t)); @@ -316,6 +317,13 @@ print_tablet_axis_event(struct libinput_event *ev) else printf("pressure: %.2f%s", pressure, tablet_axis_changed_sym(t, LIBINPUT_TABLET_AXIS_PRESSURE)); + + rotation = libinput_event_tablet_get_axis_value(t, + LIBINPUT_TABLET_AXIS_ROTATION_Z); + printf(" rotation: %.2f%s", + rotation, + tablet_axis_changed_sym(t, LIBINPUT_TABLET_AXIS_ROTATION_Z)); + printf("\n"); }