[PATCH libinput 19/26] tablet: support z-rotation for the mouse/lens tool
Peter Hutterer
peter.hutterer at who-t.net
Mon Feb 23 22:21:22 PST 2015
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 <peter.hutterer at who-t.net>
---
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 52742cf..dce7ced 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
@@ -201,6 +214,32 @@ invert_axis(const struct input_absinfo *absinfo)
}
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,
uint32_t time,
@@ -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));
@@ -454,6 +511,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;
}
@@ -491,6 +551,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;
}
@@ -648,6 +712,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 9f32655..20d9e43 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 5ed3ffc..87ddc69 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 02b2c11..eabc9ac 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 9c3d76d..6eca5c4 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 09f208c..3503143 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");
}
--
2.1.0
More information about the wayland-devel
mailing list