[PATCH libinput v2 1/2] Introduce unaccelerated motion event vectors

Jonas Ådahl jadahl at gmail.com
Wed Dec 3 19:44:09 PST 2014


For certain applications (such as FPS games) it is necessary to use
unaccelerated motion events (the motion vector that is passed to the
acceleration filter) to get a more natural feeling. Supply this
information by passing both accelerated and unaccelerated motion
vectors to the existing motion event.

Note that the unaccelerated motion event is not equivalent to 'raw'
events as read from devices.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---

Changes since v1:

Renamed non-accelerated to unaccelerated and don't abbreviate in the
public API.

Made the tested motion vectors longer in order to have the acceleration
to potentially affect it.

Improved documentation (assumes 1000 DPI patch has been applied).

For tests assuming valid accelerated motion vectors, wait for a motion
vector with a length more than zero.


Jonas



 src/evdev-mt-touchpad-edge-scroll.c |  2 +-
 src/evdev-mt-touchpad.c             | 20 ++++++--
 src/evdev-mt-touchpad.h             |  4 +-
 src/evdev.c                         | 19 ++++++--
 src/libinput-private.h              |  4 +-
 src/libinput.c                      | 22 ++++++++-
 src/libinput.h                      | 42 +++++++++++++++++
 test/pointer.c                      | 91 +++++++++++++++++++++++++++++++++----
 test/touchpad.c                     |  8 +++-
 9 files changed, 188 insertions(+), 24 deletions(-)

diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c
index 1dca0ea..d68fc68 100644
--- a/src/evdev-mt-touchpad-edge-scroll.c
+++ b/src/evdev-mt-touchpad-edge-scroll.c
@@ -338,7 +338,7 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
 		}
 
 		tp_get_delta(t, &dx, &dy);
-		tp_filter_motion(tp, &dx, &dy, time);
+		tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
 
 		if (fabs(*delta) < t->scroll.threshold)
 			continue;
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 8f76ddb..15120da 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -58,13 +58,20 @@ tp_motion_history_offset(struct tp_touch *t, int offset)
 
 void
 tp_filter_motion(struct tp_dispatch *tp,
-	         double *dx, double *dy, uint64_t time)
+	         double *dx, double *dy,
+	         double *dx_unaccel, double *dy_unaccel,
+		 uint64_t time)
 {
 	struct motion_params motion;
 
 	motion.dx = *dx * tp->accel.x_scale_coeff;
 	motion.dy = *dy * tp->accel.y_scale_coeff;
 
+	if (dx_unaccel)
+		*dx_unaccel = motion.dx;
+	if (dy_unaccel)
+		*dy_unaccel = motion.dy;
+
 	if (motion.dx != 0.0 || motion.dy != 0.0)
 		filter_dispatch(tp->device->pointer.filter, &motion, tp, time);
 
@@ -426,7 +433,7 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
 	dx /= nchanged;
 	dy /= nchanged;
 
-	tp_filter_motion(tp, &dx, &dy, time);
+	tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
 
 	evdev_post_scroll(tp->device, time, dx, dy);
 }
@@ -586,6 +593,7 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
 	struct tp_touch *t = tp_current_touch(tp);
 	double dx, dy;
 	int filter_motion = 0;
+	double dx_unaccel, dy_unaccel;
 
 	/* Only post (top) button events while suspended */
 	if (tp->device->suspended) {
@@ -617,10 +625,12 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
 		return;
 
 	tp_get_delta(t, &dx, &dy);
-	tp_filter_motion(tp, &dx, &dy, time);
+	tp_filter_motion(tp, &dx, &dy, &dx_unaccel, &dy_unaccel, time);
 
-	if (dx != 0.0 || dy != 0.0)
-		pointer_notify_motion(&tp->device->base, time, dx, dy);
+	if (dx != 0.0 || dy != 0.0 || dx_unaccel != 0.0 || dy_unaccel != 0.0) {
+		pointer_notify_motion(&tp->device->base, time,
+				      dx, dy, dx_unaccel, dy_unaccel);
+	}
 }
 
 static void
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index b2603b4..da9c091 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -276,7 +276,9 @@ tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t);
 
 void
 tp_filter_motion(struct tp_dispatch *tp,
-	         double *dx, double *dy, uint64_t time);
+	         double *dx, double *dy,
+	         double *dx_unaccel, double *dy_unaccel,
+		 uint64_t time);
 
 int
 tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time);
diff --git a/src/evdev.c b/src/evdev.c
index 908a8ba..1942539 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -198,6 +198,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
 {
 	struct libinput *libinput = device->base.seat->libinput;
 	struct motion_params motion;
+	double dx_unaccel, dy_unaccel;
 	int32_t cx, cy;
 	int32_t x, y;
 	int slot;
@@ -211,8 +212,10 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
 	case EVDEV_NONE:
 		return;
 	case EVDEV_RELATIVE_MOTION:
-		motion.dx = device->rel.dx / ((double)device->dpi / DEFAULT_MOUSE_DPI);
-		motion.dy = device->rel.dy / ((double)device->dpi / DEFAULT_MOUSE_DPI);
+		dx_unaccel = device->rel.dx / ((double) device->dpi /
+					       DEFAULT_MOUSE_DPI);
+		dy_unaccel = device->rel.dy / ((double) device->dpi /
+					       DEFAULT_MOUSE_DPI);
 		device->rel.dx = 0;
 		device->rel.dy = 0;
 
@@ -221,17 +224,23 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
 		    hw_is_key_down(device, device->scroll.button)) {
 			if (device->scroll.button_scroll_active)
 				evdev_post_scroll(device, time,
-						  motion.dx, motion.dy);
+						  dx_unaccel, dy_unaccel);
 			break;
 		}
 
 		/* Apply pointer acceleration. */
+		motion.dx = dx_unaccel;
+		motion.dy = dy_unaccel;
 		filter_dispatch(device->pointer.filter, &motion, device, time);
 
-		if (motion.dx == 0.0 && motion.dy == 0.0)
+		if (motion.dx == 0.0 && motion.dy == 0.0 &&
+		    dx_unaccel == 0.0 && dy_unaccel == 0.0) {
 			break;
+		}
 
-		pointer_notify_motion(base, time, motion.dx, motion.dy);
+		pointer_notify_motion(base, time,
+				      motion.dx, motion.dy,
+				      dx_unaccel, dy_unaccel);
 		break;
 	case EVDEV_ABSOLUTE_MT_DOWN:
 		if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
diff --git a/src/libinput-private.h b/src/libinput-private.h
index 4a9bd54..b36dc95 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -259,7 +259,9 @@ void
 pointer_notify_motion(struct libinput_device *device,
 		      uint64_t time,
 		      double dx,
-		      double dy);
+		      double dy,
+		      double dx_noaccel,
+		      double dy_noaccel);
 
 void
 pointer_notify_motion_absolute(struct libinput_device *device,
diff --git a/src/libinput.c b/src/libinput.c
index c318eee..40305c7 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -59,6 +59,8 @@ struct libinput_event_pointer {
 	uint32_t time;
 	double x;
 	double y;
+	double dx_unaccel;
+	double dy_unaccel;
 	uint32_t button;
 	uint32_t seat_button_count;
 	enum libinput_button_state state;
@@ -304,6 +306,20 @@ libinput_event_pointer_get_dy(struct libinput_event_pointer *event)
 }
 
 LIBINPUT_EXPORT double
+libinput_event_pointer_get_dx_unaccelerated(
+	struct libinput_event_pointer *event)
+{
+	return event->dx_unaccel;
+}
+
+LIBINPUT_EXPORT double
+libinput_event_pointer_get_dy_unaccelerated(
+	struct libinput_event_pointer *event)
+{
+	return event->dy_unaccel;
+}
+
+LIBINPUT_EXPORT double
 libinput_event_pointer_get_absolute_x(struct libinput_event_pointer *event)
 {
 	struct evdev_device *device =
@@ -885,7 +901,9 @@ void
 pointer_notify_motion(struct libinput_device *device,
 		      uint64_t time,
 		      double dx,
-		      double dy)
+		      double dy,
+		      double dx_unaccel,
+		      double dy_unaccel)
 {
 	struct libinput_event_pointer *motion_event;
 
@@ -897,6 +915,8 @@ pointer_notify_motion(struct libinput_device *device,
 		.time = time,
 		.x = dx,
 		.y = dy,
+		.dx_unaccel = dx_unaccel,
+		.dy_unaccel = dy_unaccel,
 	};
 
 	post_device_event(device, time,
diff --git a/src/libinput.h b/src/libinput.h
index a60d41e..c014b00 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -558,6 +558,48 @@ libinput_event_pointer_get_dy(struct libinput_event_pointer *event);
 /**
  * @ingroup event_pointer
  *
+ * Return the relative delta of the unaccelerated motion vector of the
+ * current event. For pointer events that are not of type
+ * LIBINPUT_EVENT_POINTER_MOTION, this function returns 0.
+ *
+ * Relative unaccelerated motion deltas are normalized to represent those of a
+ * device with 1000dpi resolution. See @ref motion_normalization for more
+ * details. Note that unaccelerated events do not equivalent to 'raw' events
+ * as read from the device.
+ *
+ * @note It is an application bug to call this function for events other than
+ * LIBINPUT_EVENT_POINTER_MOTION.
+ *
+ * @return the unaccelerated relative x movement since the last event
+ */
+double
+libinput_event_pointer_get_dx_unaccelerated(
+	struct libinput_event_pointer *event);
+
+/**
+ * @ingroup event_pointer
+ *
+ * Return the relative delta of the unaccelerated motion vector of the
+ * current event. For pointer events that are not of type
+ * LIBINPUT_EVENT_POINTER_MOTION, this function returns 0.
+ *
+ * Relative unaccelerated motion deltas are normalized to represent those of a
+ * device with 1000dpi resolution. See @ref motion_normalization for more
+ * details. Note that unaccelerated events do not equivalent to 'raw' events
+ * as read from the device.
+ *
+ * @note It is an application bug to call this function for events other than
+ * LIBINPUT_EVENT_POINTER_MOTION.
+ *
+ * @return the unaccelerated relative y movement since the last event
+ */
+double
+libinput_event_pointer_get_dy_unaccelerated(
+	struct libinput_event_pointer *event);
+
+/**
+ * @ingroup event_pointer
+ *
  * Return the current absolute x coordinate of the pointer event, in mm from
  * the top left corner of the device. To get the corresponding output screen
  * coordinate, use libinput_event_pointer_get_absolute_x_transformed().
diff --git a/test/pointer.c b/test/pointer.c
index 206accd..dfed6b7 100644
--- a/test/pointer.c
+++ b/test/pointer.c
@@ -29,15 +29,43 @@
 #include <libinput.h>
 #include <math.h>
 #include <unistd.h>
+#include <values.h>
 
 #include "libinput-util.h"
 #include "litest.h"
 
+static struct libinput_event_pointer *
+get_accelerated_motion_event(struct libinput *li)
+{
+	struct libinput_event *event;
+	struct libinput_event_pointer *ptrev;
+
+	while (1) {
+		event = libinput_get_event(li);
+		ck_assert_notnull(event);
+		ck_assert_int_eq(libinput_event_get_type(event),
+				 LIBINPUT_EVENT_POINTER_MOTION);
+
+		ptrev = libinput_event_get_pointer_event(event);
+		ck_assert_notnull(ptrev);
+
+		if (fabs(libinput_event_pointer_get_dx(ptrev)) < DBL_MIN &&
+		    fabs(libinput_event_pointer_get_dy(ptrev)) < DBL_MIN) {
+			libinput_event_destroy(event);
+			continue;
+		}
+
+		return ptrev;
+	}
+
+	ck_abort_msg("No accelerated pointer motion event found");
+	return NULL;
+}
+
 static void
 test_relative_event(struct litest_device *dev, int dx, int dy)
 {
 	struct libinput *li = dev->libinput;
-	struct libinput_event *event;
 	struct libinput_event_pointer *ptrev;
 	double ev_dx, ev_dy;
 	double expected_dir;
@@ -56,12 +84,7 @@ test_relative_event(struct litest_device *dev, int dx, int dy)
 
 	libinput_dispatch(li);
 
-	event = libinput_get_event(li);
-	ck_assert(event != NULL);
-	ck_assert_int_eq(libinput_event_get_type(event), LIBINPUT_EVENT_POINTER_MOTION);
-
-	ptrev = libinput_event_get_pointer_event(event);
-	ck_assert(ptrev != NULL);
+	ptrev = get_accelerated_motion_event(li);
 
 	expected_length = sqrt(4 * dx*dx + 4 * dy*dy);
 	expected_dir = atan2(dx, dy);
@@ -78,7 +101,7 @@ test_relative_event(struct litest_device *dev, int dx, int dy)
 	 * indifference). */
 	ck_assert(fabs(expected_dir - actual_dir) < M_PI_2);
 
-	libinput_event_destroy(event);
+	libinput_event_destroy(libinput_event_pointer_get_base_event(ptrev));
 
 	litest_drain_events(dev->libinput);
 }
@@ -140,6 +163,57 @@ START_TEST(pointer_motion_absolute)
 END_TEST
 
 static void
+test_unaccel_event(struct litest_device *dev, int dx, int dy)
+{
+      struct libinput *li = dev->libinput;
+      struct libinput_event *event;
+      struct libinput_event_pointer *ptrev;
+      double ev_dx, ev_dy;
+
+      litest_event(dev, EV_REL, REL_X, dx);
+      litest_event(dev, EV_REL, REL_Y, dy);
+      litest_event(dev, EV_SYN, SYN_REPORT, 0);
+
+      libinput_dispatch(li);
+
+      event = libinput_get_event(li);
+      ck_assert_notnull(event);
+      ck_assert_int_eq(libinput_event_get_type(event),
+                       LIBINPUT_EVENT_POINTER_MOTION);
+
+      ptrev = libinput_event_get_pointer_event(event);
+      ck_assert(ptrev != NULL);
+
+      ev_dx = libinput_event_pointer_get_dx_unaccelerated(ptrev);
+      ev_dy = libinput_event_pointer_get_dy_unaccelerated(ptrev);
+
+      ck_assert_int_eq(dx, ev_dx);
+      ck_assert_int_eq(dy, ev_dy);
+
+      libinput_event_destroy(event);
+
+      litest_drain_events(dev->libinput);
+}
+
+START_TEST(pointer_motion_unaccel)
+{
+      struct litest_device *dev = litest_current_device();
+
+      litest_drain_events(dev->libinput);
+
+      test_unaccel_event(dev, 10, 0);
+      test_unaccel_event(dev, 10, 10);
+      test_unaccel_event(dev, 10, -10);
+      test_unaccel_event(dev, 0, 10);
+
+      test_unaccel_event(dev, -10, 0);
+      test_unaccel_event(dev, -10, 10);
+      test_unaccel_event(dev, -10, -10);
+      test_unaccel_event(dev, 0, -10);
+}
+END_TEST
+
+static void
 test_button_event(struct litest_device *dev, unsigned int button, int state)
 {
 	struct libinput *li = dev->libinput;
@@ -652,6 +726,7 @@ int main (int argc, char **argv) {
 
 	litest_add("pointer:motion", pointer_motion_relative, LITEST_RELATIVE, LITEST_ANY);
 	litest_add("pointer:motion", pointer_motion_absolute, LITEST_ABSOLUTE, LITEST_ANY);
+	litest_add("pointer:motion", pointer_motion_unaccel, LITEST_RELATIVE, LITEST_ANY);
 	litest_add("pointer:button", pointer_button, LITEST_BUTTON, LITEST_CLICKPAD);
 	litest_add_no_device("pointer:button_auto_release", pointer_button_auto_release);
 	litest_add("pointer:scroll", pointer_scroll_wheel, LITEST_WHEEL, LITEST_ANY);
diff --git a/test/touchpad.c b/test/touchpad.c
index 2b79aaf..934674c 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -1173,8 +1173,12 @@ START_TEST(clickpad_softbutton_left_2nd_fg_move)
 		x = libinput_event_pointer_get_dx(p);
 		y = libinput_event_pointer_get_dy(p);
 
-		ck_assert(x > 0);
-		ck_assert(y == 0);
+		/* Ignore events only containing an unaccelerated motion
+		 * vector. */
+		if (x != 0 || y != 0) {
+			ck_assert(x > 0);
+			ck_assert(y == 0);
+		}
 
 		libinput_event_destroy(event);
 		libinput_dispatch(li);
-- 
1.8.5.1



More information about the wayland-devel mailing list