mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-01-10 04:30:16 +01:00
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 <peter.hutterer@who-t.net> Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com> Reviewed-by: Stephen Chandler Paul <thatslyude@gmail.com>
This commit is contained in:
parent
cbce0d03b0
commit
8edae426e3
6 changed files with 173 additions and 9 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue