Introduce unaccelerated motion event vectors
authorJonas Ådahl <jadahl@gmail.com>
Thu, 4 Dec 2014 03:44:09 +0000 (11:44 +0800)
committerPeter Hutterer <peter.hutterer@who-t.net>
Fri, 5 Dec 2014 01:54:02 +0000 (11:54 +1000)
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@gmail.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
src/evdev-mt-touchpad-edge-scroll.c
src/evdev-mt-touchpad.c
src/evdev-mt-touchpad.h
src/evdev.c
src/libinput-private.h
src/libinput.c
src/libinput.h
test/pointer.c
test/touchpad.c

index 1dca0eab7f5545522b07bffa19f8b62daa447e7a..d68fc686ef5b59dbf8db7a12dd46d6951243d796 100644 (file)
@@ -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;
index aa9c8197048af39ab2114d0de618c7820682d32c..8149c001e4078352dc6906b3da0b6f8e705c852d 100644 (file)
@@ -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
index b2603b44c4d7e7d77fa20d5613436619ff966790..da9c09149c28dbc41f18e858700ea5979e21a94c 100644 (file)
@@ -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);
index c2d10d7e885f76adf5d0e6592f00a5c2b604999f..fbfbcd35b4a6c8f3736ce0664a762e89e172e55b 100644 (file)
@@ -196,6 +196,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;
@@ -209,8 +210,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;
 
@@ -219,17 +222,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))
index 4a9bd540be25ce8947a26bc02ea7a7e85cdc3851..b36dc9542c541c09c5db1783cca17dfc7b9d9c8b 100644 (file)
@@ -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,
index 60505f6796f5066563ed839f877811b6713fb77f..279cce0f8d337226232b997dcab3cac372b4b22a 100644 (file)
@@ -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;
@@ -303,6 +305,20 @@ libinput_event_pointer_get_dy(struct libinput_event_pointer *event)
        return event->y;
 }
 
+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)
 {
@@ -891,7 +907,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;
 
@@ -903,6 +921,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,
index be4a22255899373fe8901bb415a464367a21957b..0e2ba6b21c8d4b960c07ef939a5a89bacdda9443 100644 (file)
@@ -555,6 +555,48 @@ libinput_event_pointer_get_dx(struct libinput_event_pointer *event);
 double
 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 @ref
+ * 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 are not equivalent to 'raw' events
+ * as read from the device.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref 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 @ref
+ * 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 are not equivalent to 'raw' events
+ * as read from the device.
+ *
+ * @note It is an application bug to call this function for events other than
+ * @ref 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
  *
index 206accd5bf04fd9036b229e500d7979306db53a5..dfed6b7d4a03be228016dcbdc0b1237c1b5e1c39 100644 (file)
 #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);
 }
@@ -139,6 +162,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)
 {
@@ -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);
index 2b79aafefd27fe4637993ce2d2775b23c63d5cf1..934674c1bf89a4e77ffe196bb8e87df819fc0cf9 100644 (file)
@@ -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);