From 20ac4b3abd722c3bb415c545f5769b9f1cac15ba Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 5 Nov 2014 16:22:07 +1000 Subject: [PATCH] Add pointer axis sources to the API 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 Original patch, before the rebase onto today's master: Reviewed-by: Hans de Goede --- src/evdev-mt-touchpad-edge-scroll.c | 12 ++++-- src/evdev-mt-touchpad.c | 13 ++++-- src/evdev.c | 17 +++++++- src/evdev.h | 5 ++- src/libinput-private.h | 1 + src/libinput.c | 9 ++++ src/libinput.h | 65 +++++++++++++++++++++++++++-- src/libinput.sym | 1 + test/pointer.c | 2 + test/touchpad.c | 51 ++++++++++++++++++++++ test/trackpoint.c | 24 +++++++++++ 11 files changed, 188 insertions(+), 12 deletions(-) diff --git a/src/evdev-mt-touchpad-edge-scroll.c b/src/evdev-mt-touchpad-edge-scroll.c index bc6831dd..a4dc0939 100644 --- a/src/evdev-mt-touchpad-edge-scroll.c +++ b/src/evdev-mt-touchpad-edge-scroll.c @@ -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; } } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 0f3e72a6..7e8306d4 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -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; } diff --git a/src/evdev.c b/src/evdev.c index 06dd1df1..1718491d 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -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; diff --git a/src/evdev.h b/src/evdev.h index 820fdccd..a75dd134 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -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); diff --git a/src/libinput-private.h b/src/libinput-private.h index b36dc954..84a0d440 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -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 diff --git a/src/libinput.c b/src/libinput.c index d78e26db..426c3069 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -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, diff --git a/src/libinput.h b/src/libinput.h index d43b0f6a..0f251b5e 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -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 * diff --git a/src/libinput.sym b/src/libinput.sym index 057919c9..826bfde4 100644 --- a/src/libinput.sym +++ b/src/libinput.sym @@ -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; diff --git a/test/pointer.c b/test/pointer.c index dfed6b7d..b9bd3cc8 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -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); } diff --git a/test/touchpad.c b/test/touchpad.c index 19e1c284..422e8fe6 100644 --- a/test/touchpad.c +++ b/test/touchpad.c @@ -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); diff --git a/test/trackpoint.c b/test/trackpoint.c index 1d1a10a0..2708bad2 100644 --- a/test/trackpoint.c +++ b/test/trackpoint.c @@ -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); }