[PATCH libinput 19/26] tablet: support z-rotation for the mouse/lens tool

Jason Gerecke killertofu at gmail.com
Wed Feb 25 17:30:08 PST 2015


On 2/23/2015 10:21 PM, Peter Hutterer wrote:
> 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;
Does this offset actually exist? The I4 mouse I'm testing with behaves 
as there is no offset, and nothing I can find internally suggests that 
one actually exists. I'm almost tempted to say that its a bug despite 
being in linuxwacom since seemingly the dawn of time. If either you or 
Benjamin (or anyone else) has an I4 mouse, I'd be interested in knowing 
what results you get.
> +	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);
Strictly speaking, atan2 should be fed the sine of the x and y tilt 
angles (...another longstanding linuxwacom bug?)
> +
> +	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");
>   }
>   

-- 
Jason
---
Now instead of four in the eights place /
you’ve got three, ‘Cause you added one /
(That is to say, eight) to the two, /
But you can’t take seven from three, /
So you look at the sixty-fours....



More information about the wayland-devel mailing list