Add pointer axis sources to the API
authorPeter Hutterer <peter.hutterer@who-t.net>
Wed, 5 Nov 2014 06:22:07 +0000 (16:22 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Wed, 24 Dec 2014 00:47:00 +0000 (10:47 +1000)
For a caller to implement/provide kinetic scrolling ("inertial scrolling",
"fling scrolling"), it needs to know how the scrolling motion was implemented,
and what to expect in the future. Add this information to the pointer axis
event.

The three scroll sources we have are:
* wheels: scrolling is in discreet steps, you don't know when it ends, the
  wheel will just stop sending events
* fingers: scrolling is continuous coordinate space, we know when it stops and
  we can tell the caller
* continuous: scrolling is in continuous coordinate space but we may or may not
  know when it stops. if scroll lock is used, the device may never technically
  get out of scroll mode even if it doesn't send events at any given moment
  Use case: trackpoint/trackball scroll emulation on button press

The stop event is now codified in the API documentation, so callers can use
that for kinetic scrolling. libinput does not implement kinetic scrolling
itself.

Not covered by this patch:
* The wheel event is currently defined as "typical mouse wheel step", this is
  different to Qt where the step value is 1/8 of a degree. Some better
  definition here may help.
* It is unclear how an absolute device would map into relative motion if the
  device itself is not controlling absolute motion.
* For diagonal scrolling, the vertical/horizontal terminator events would come
  in separately. The caller would have to deal with that somehow.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Original patch, before the rebase onto today's master:
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
src/evdev-mt-touchpad-edge-scroll.c
src/evdev-mt-touchpad.c
src/evdev.c
src/evdev.h
src/libinput-private.h
src/libinput.c
src/libinput.h
src/libinput.sym
test/pointer.c
test/touchpad.c
test/trackpoint.c

index bc6831dde19ae2269f8ccfab4d009ebe9edd6051..a4dc0939666cf5082d557ca6e4be652846ddfd87 100644 (file)
@@ -325,7 +325,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
                                if (t->scroll.direction != -1) {
                                        /* Send stop scroll event */
                                        pointer_notify_axis(device, time,
-                                               t->scroll.direction, 0.0);
+                                               t->scroll.direction,
+                                               LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+                                               0.0);
                                        t->scroll.direction = -1;
                                }
                                continue;
@@ -347,7 +349,9 @@ tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
                if (fabs(*delta) < t->scroll.threshold)
                        continue;
 
-               pointer_notify_axis(device, time, axis, *delta);
+               pointer_notify_axis(device, time, axis,
+                                   LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+                                   *delta);
                t->scroll.direction = axis;
 
                tp_edge_scroll_handle_event(tp, t, SCROLL_EVENT_POSTED);
@@ -365,7 +369,9 @@ tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time)
        tp_for_each_touch(tp, t) {
                if (t->scroll.direction != -1) {
                        pointer_notify_axis(device, time,
-                                           t->scroll.direction, 0.0);
+                                           t->scroll.direction,
+                                           LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+                                           0.0);
                        t->scroll.direction = -1;
                }
        }
index 0f3e72a6908a002eb042b0e2fadbac71b61b1b85..7e8306d4b2c209259f4b139227df7622da87cfef 100644 (file)
@@ -435,7 +435,10 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
 
        tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
 
-       evdev_post_scroll(tp->device, time, dx, dy);
+       evdev_post_scroll(tp->device,
+                         time,
+                         LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+                         dx, dy);
        tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_ACTIVE;
 }
 
@@ -445,7 +448,9 @@ tp_twofinger_stop_scroll(struct tp_dispatch *tp, uint64_t time)
        struct tp_touch *t, *ptr = NULL;
        int nfingers_down = 0;
 
-       evdev_stop_scroll(tp->device, time);
+       evdev_stop_scroll(tp->device,
+                         time,
+                         LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 
        /* If we were scrolling and now there's exactly 1 active finger,
           switch back to pointer movement */
@@ -846,7 +851,9 @@ tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
                return;
 
        if (!tp->sendevents.trackpoint_active) {
-               evdev_stop_scroll(tp->device, time);
+               evdev_stop_scroll(tp->device,
+                                 time,
+                                 LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
                tp_tap_suspend(tp, time);
                tp->sendevents.trackpoint_active = true;
        }
index 06dd1df1eb617d836d6d76272173a28e84f93e50..1718491d77beed5b1f8c141373e1de987b750d1a 100644 (file)
@@ -222,6 +222,7 @@ 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,
+                                                 LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
                                                  dx_unaccel, dy_unaccel);
                        break;
                }
@@ -394,7 +395,8 @@ evdev_button_scroll_button(struct evdev_device *device,
        } else {
                libinput_timer_cancel(&device->scroll.timer);
                if (device->scroll.button_scroll_active) {
-                       evdev_stop_scroll(device, time);
+                       evdev_stop_scroll(device, time,
+                                         LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
                        device->scroll.button_scroll_active = false;
                } else {
                        /* If the button is released quickly enough emit the
@@ -538,6 +540,7 @@ static void
 evdev_notify_axis(struct evdev_device *device,
                  uint64_t time,
                  enum libinput_pointer_axis axis,
+                 enum libinput_pointer_axis_source source,
                  double value)
 {
        if (device->scroll.natural_scrolling_enabled)
@@ -546,6 +549,7 @@ evdev_notify_axis(struct evdev_device *device,
        pointer_notify_axis(&device->base,
                            time,
                            axis,
+                           source,
                            value);
 }
 
@@ -572,6 +576,7 @@ evdev_process_relative(struct evdev_device *device,
                        device,
                        time,
                        LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+                       LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
                        -1 * e->value * DEFAULT_AXIS_STEP_DISTANCE);
                break;
        case REL_HWHEEL:
@@ -580,6 +585,7 @@ evdev_process_relative(struct evdev_device *device,
                        device,
                        time,
                        LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
+                       LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
                        e->value * DEFAULT_AXIS_STEP_DISTANCE);
                break;
        }
@@ -1781,6 +1787,7 @@ evdev_start_scrolling(struct evdev_device *device,
 void
 evdev_post_scroll(struct evdev_device *device,
                  uint64_t time,
+                 enum libinput_pointer_axis_source source,
                  double dx,
                  double dy)
 {
@@ -1831,6 +1838,7 @@ evdev_post_scroll(struct evdev_device *device,
                evdev_notify_axis(device,
                                  time,
                                  LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+                                 source,
                                  dy);
        }
 
@@ -1840,23 +1848,28 @@ evdev_post_scroll(struct evdev_device *device,
                evdev_notify_axis(device,
                                  time,
                                  LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
+                                 source,
                                  dx);
        }
 }
 
 void
-evdev_stop_scroll(struct evdev_device *device, uint64_t time)
+evdev_stop_scroll(struct evdev_device *device,
+                 uint64_t time,
+                 enum libinput_pointer_axis_source source)
 {
        /* terminate scrolling with a zero scroll event */
        if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
                pointer_notify_axis(&device->base,
                                    time,
                                    LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+                                   source,
                                    0);
        if (device->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
                pointer_notify_axis(&device->base,
                                    time,
                                    LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
+                                   source,
                                    0);
 
        device->scroll.buildup_horizontal = 0;
index 820fdccdafca60b56494222946ec8ea528e8bf5c..a75dd134f8de475c3abb16ee3178d742a04068fc 100644 (file)
@@ -298,12 +298,15 @@ evdev_init_natural_scroll(struct evdev_device *device);
 void
 evdev_post_scroll(struct evdev_device *device,
                  uint64_t time,
+                 enum libinput_pointer_axis_source source,
                  double dx,
                  double dy);
 
 
 void
-evdev_stop_scroll(struct evdev_device *device, uint64_t time);
+evdev_stop_scroll(struct evdev_device *device,
+                 uint64_t time,
+                 enum libinput_pointer_axis_source source);
 
 void
 evdev_device_remove(struct evdev_device *device);
index b36dc9542c541c09c5db1783cca17dfc7b9d9c8b..84a0d4405a55d64f90ba4c6d966f08be7a2cf27f 100644 (file)
@@ -279,6 +279,7 @@ void
 pointer_notify_axis(struct libinput_device *device,
                    uint64_t time,
                    enum libinput_pointer_axis axis,
+                   enum libinput_pointer_axis_source source,
                    double value);
 
 void
index d78e26db6dadc9d49ef801165f139dc0fef3ccaa..426c30696f9f38062cd884f92ef45d8e3dd28da8 100644 (file)
@@ -65,6 +65,7 @@ struct libinput_event_pointer {
        uint32_t seat_button_count;
        enum libinput_button_state state;
        enum libinput_pointer_axis axis;
+       enum libinput_pointer_axis_source source;
        double value;
 };
 
@@ -390,6 +391,12 @@ libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event)
        return event->value;
 }
 
+LIBINPUT_EXPORT enum libinput_pointer_axis_source
+libinput_event_pointer_get_axis_source(struct libinput_event_pointer *event)
+{
+       return event->source;
+}
+
 LIBINPUT_EXPORT uint32_t
 libinput_event_touch_get_time(struct libinput_event_touch *event)
 {
@@ -986,6 +993,7 @@ void
 pointer_notify_axis(struct libinput_device *device,
                    uint64_t time,
                    enum libinput_pointer_axis axis,
+                   enum libinput_pointer_axis_source source,
                    double value)
 {
        struct libinput_event_pointer *axis_event;
@@ -998,6 +1006,7 @@ pointer_notify_axis(struct libinput_device *device,
                .time = time,
                .axis = axis,
                .value = value,
+               .source = source,
        };
 
        post_device_event(device, time,
index d43b0f6ae0147233e5f0766678050b49472c6221..0f251b5e1add5d0cb94ecdf2042942fc9d7efb08 100644 (file)
@@ -107,6 +107,28 @@ enum libinput_pointer_axis {
        LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL = 1,
 };
 
+/**
+ * @ingroup device
+ *
+ * The source for a libinput_pointer_axis event. See
+ * libinput_event_pointer_get_axis_source() for details.
+ */
+enum libinput_pointer_axis_source {
+       /**
+        * The event is caused by the rotation of a wheel.
+        */
+       LIBINPUT_POINTER_AXIS_SOURCE_WHEEL = 1,
+       /**
+        * The event is caused by the movement of one or more fingers on a
+        * device.
+        */
+       LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+       /**
+        * The event is caused by the motion of some device.
+        */
+       LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
+};
+
 /**
  * @ingroup base
  *
@@ -651,9 +673,8 @@ libinput_event_pointer_get_axis(struct libinput_event_pointer *event);
  * @ref LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL and
  * @ref LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, the value of the event is in
  * relative scroll units, with the positive direction being down or right,
- * respectively. The dimension of a scroll unit is equal to one unit of
- * motion in the respective axis, where applicable (e.g. touchpad two-finger
- * scrolling).
+ * respectively. For the interpretation of the value, see
+ * libinput_event_pointer_get_axis_source().
  *
  * For pointer events that are not of type @ref LIBINPUT_EVENT_POINTER_AXIS,
  * this function returns 0.
@@ -666,6 +687,44 @@ libinput_event_pointer_get_axis(struct libinput_event_pointer *event);
 double
 libinput_event_pointer_get_axis_value(struct libinput_event_pointer *event);
 
+/**
+ * @ingroup event_pointer
+ *
+ * Return the source for a given axis event. Axis events (scroll events) can
+ * be caused by a hardware item such as a scroll wheel or emulated from
+ * other input sources, such as two-finger or edge scrolling on a
+ * touchpad.
+ *
+ * If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_FINGER, libinput
+ * guarantees that a scroll sequence is terminated with a scroll value of 0.
+ * A caller may use this information to decide on whether kinetic scrolling
+ * should be triggered on this scroll sequence.
+ * The coordinate system is identical to the cursor movement, i.e. a
+ * scroll value of 1 represents the equivalent relative motion of 1.
+ *
+ * If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_WHEEL, no terminating
+ * event is guaranteed (though it may happen).
+ * Scrolling is in discreet steps, a value of 10 representing one click
+ * of a typical mouse wheel. Some mice may have differently grained wheels,
+ * libinput will adjust the value accordingly. It is up to the caller how to
+ * interpret such different step sizes.
+ *
+ * If the source is @ref LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, no
+ * terminating event is guaranteed (though it may happen).
+ * The coordinate system is identical to the cursor movement, i.e. a
+ * scroll value of 1 represents the equivalent relative motion of 1.
+ *
+ * For pointer events that are not of type LIBINPUT_EVENT_POINTER_AXIS,
+ * this function returns 0.
+ *
+ * @note It is an application bug to call this function for events other than
+ * LIBINPUT_EVENT_POINTER_AXIS.
+ *
+ * @return the source for this axis event
+ */
+enum libinput_pointer_axis_source
+libinput_event_pointer_get_axis_source(struct libinput_event_pointer *event);
+
 /**
  * @ingroup event_pointer
  *
index 057919c9edb5960e0681f808759c68391f343fe8..826bfde4a2b23cb2ddfdeedaa3b7a25b93c03b67 100644 (file)
@@ -71,6 +71,7 @@ global:
        libinput_event_pointer_get_absolute_y;
        libinput_event_pointer_get_absolute_y_transformed;
        libinput_event_pointer_get_axis;
+       libinput_event_pointer_get_axis_source;
        libinput_event_pointer_get_axis_value;
        libinput_event_pointer_get_base_event;
        libinput_event_pointer_get_button_state;
index dfed6b7d4a03be228016dcbdc0b1237c1b5e1c39..b9bd3cc8b5b50ab40c1e3e502b82aae98ae2d973 100644 (file)
@@ -377,6 +377,8 @@ test_wheel_event(struct litest_device *dev, int which, int amount)
                                LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL :
                                LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
        ck_assert_int_eq(libinput_event_pointer_get_axis_value(ptrev), expected);
+       ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
+                        LIBINPUT_POINTER_AXIS_SOURCE_WHEEL);
        libinput_event_destroy(event);
 }
 
index 19e1c28439942c081b4870ee4b1561e215f86ba0..422e8fe6c0c95649ee8bb7c37c9e283b7c701210 100644 (file)
@@ -1461,6 +1461,29 @@ START_TEST(touchpad_2fg_scroll_slow_distance)
 }
 END_TEST
 
+START_TEST(touchpad_2fg_scroll_source)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_event_pointer *ptrev;
+
+       litest_drain_events(li);
+
+       test_2fg_scroll(dev, 0, 20, 0);
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
+
+       while ((event = libinput_get_event(li))) {
+               ck_assert_int_eq(libinput_event_get_type(event),
+                                LIBINPUT_EVENT_POINTER_AXIS);
+               ptrev = libinput_event_get_pointer_event(event);
+               ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
+                                LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+               libinput_event_destroy(event);
+       }
+}
+END_TEST
+
 START_TEST(touchpad_2fg_scroll_return_to_motion)
 {
        struct litest_device *dev = litest_current_device();
@@ -1666,6 +1689,32 @@ START_TEST(touchpad_edge_scroll_no_edge_after_motion)
 }
 END_TEST
 
+START_TEST(touchpad_edge_scroll_source)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_event_pointer *ptrev;
+
+       litest_drain_events(li);
+
+       litest_touch_down(dev, 0, 99, 20);
+       litest_touch_move_to(dev, 0, 99, 20, 99, 80, 10, 0);
+       litest_touch_up(dev, 0);
+
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
+
+       while ((event = libinput_get_event(li))) {
+               ck_assert_int_eq(libinput_event_get_type(event),
+                                LIBINPUT_EVENT_POINTER_AXIS);
+               ptrev = libinput_event_get_pointer_event(event);
+               ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
+                                LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
+               libinput_event_destroy(event);
+       }
+}
+END_TEST
+
 START_TEST(touchpad_tap_is_available)
 {
        struct litest_device *dev = litest_current_device();
@@ -2219,6 +2268,7 @@ int main(int argc, char **argv) {
        litest_add("touchpad:scroll", touchpad_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
        litest_add("touchpad:scroll", touchpad_2fg_scroll_slow_distance, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
        litest_add("touchpad:scroll", touchpad_2fg_scroll_return_to_motion, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+       litest_add("touchpad:scroll", touchpad_2fg_scroll_source, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
        litest_add("touchpad:scroll", touchpad_scroll_natural_defaults, LITEST_TOUCHPAD, LITEST_ANY);
        litest_add("touchpad:scroll", touchpad_scroll_natural_enable_config, LITEST_TOUCHPAD, LITEST_ANY);
        litest_add("touchpad:scroll", touchpad_scroll_natural, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
@@ -2226,6 +2276,7 @@ int main(int argc, char **argv) {
        litest_add("touchpad:scroll", touchpad_edge_scroll_no_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
        litest_add("touchpad:scroll", touchpad_edge_scroll_no_edge_after_motion, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
        litest_add("touchpad:scroll", touchpad_edge_scroll_slow_distance, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
+       litest_add("touchpad:scroll", touchpad_edge_scroll_source, LITEST_TOUCHPAD|LITEST_SINGLE_TOUCH, LITEST_ANY);
 
        litest_add("touchpad:palm", touchpad_palm_detect_at_edge, LITEST_TOUCHPAD, LITEST_ANY);
        litest_add("touchpad:palm", touchpad_palm_detect_at_bottom_corners, LITEST_TOUCHPAD, LITEST_CLICKPAD);
index 1d1a10a0fc23c65f86866eefe1a0cabc8d5d33b4..2708bad2b7e3440ee5cdffef9f96fe0bae4a9614 100644 (file)
@@ -107,11 +107,35 @@ START_TEST(trackpoint_middlebutton_noscroll)
 }
 END_TEST
 
+START_TEST(trackpoint_scroll_source)
+{
+       struct litest_device *dev = litest_current_device();
+       struct libinput *li = dev->libinput;
+       struct libinput_event *event;
+       struct libinput_event_pointer *ptrev;
+
+       litest_drain_events(li);
+
+       litest_button_scroll(dev, BTN_MIDDLE, 0, 6);
+       litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
+
+       while ((event = libinput_get_event(li))) {
+               ptrev = libinput_event_get_pointer_event(event);
+
+               ck_assert_int_eq(libinput_event_pointer_get_axis_source(ptrev),
+                                LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
+
+               libinput_event_destroy(event);
+       }
+}
+END_TEST
+
 int main(int argc, char **argv) {
 
        litest_add("trackpoint:middlebutton", trackpoint_middlebutton, LITEST_POINTINGSTICK, LITEST_ANY);
        litest_add("trackpoint:middlebutton", trackpoint_middlebutton_noscroll, LITEST_POINTINGSTICK, LITEST_ANY);
        litest_add("trackpoint:scroll", trackpoint_scroll, LITEST_POINTINGSTICK, LITEST_ANY);
+       litest_add("trackpoint:scroll", trackpoint_scroll_source, LITEST_POINTINGSTICK, LITEST_ANY);
 
        return litest_run(argc, argv);
 }