[PATCH libinput] Add pointer axis sources to the API

Peter Hutterer peter.hutterer at who-t.net
Wed Nov 5 21:02:33 PST 2014


For a caller to implement/provide kinetic scrolling (intertial 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 at who-t.net>
---
 src/evdev-mt-touchpad.c | 16 +++++++++---
 src/evdev.c             | 15 ++++++++++--
 src/evdev.h             |  5 +++-
 src/libinput-private.h  |  1 +
 src/libinput.c          |  9 +++++++
 src/libinput.h          | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
 test/pointer.c          |  2 ++
 test/touchpad.c         | 23 +++++++++++++++++
 test/trackpoint.c       | 23 +++++++++++++++++
 9 files changed, 149 insertions(+), 10 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index b7a559f..1b1ff84 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -499,7 +499,9 @@ tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
 		dy = -dy;
 	}
 
-	evdev_post_scroll(tp->device, time, dx, dy);
+	evdev_post_scroll(tp->device, time,
+			  LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
+			  dx, dy);
 }
 
 static int
@@ -515,7 +517,9 @@ tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
 	}
 
 	if (nfingers_down != 2) {
-		evdev_stop_scroll(tp->device, time);
+		evdev_stop_scroll(tp->device,
+				  time,
+				  LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 		return 0;
 	}
 
@@ -540,7 +544,9 @@ tp_post_events(struct tp_dispatch *tp, uint64_t time)
 	filter_motion |= tp_post_button_events(tp, time);
 
 	if (filter_motion || tp->sendevents.trackpoint_active) {
-		evdev_stop_scroll(tp->device, time);
+		evdev_stop_scroll(tp->device,
+				  time,
+				  LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
 		return;
 	}
 
@@ -703,7 +709,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 3aa87a7..d04da88 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -217,6 +217,7 @@ evdev_flush_pending_event(struct evdev_device *device, uint64_t time)
 		    hw_is_key_down(device, BTN_MIDDLE)) {
 			if (device->scroll.middle_button_scroll_active)
 				evdev_post_scroll(device, time,
+						  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
 						  motion.dx, motion.dy);
 			break;
 		}
@@ -381,7 +382,8 @@ evdev_middle_button_scroll_button(struct evdev_device *device,
 	} else {
 		libinput_timer_cancel(&device->scroll.timer);
 		if (device->scroll.middle_button_scroll_active) {
-			evdev_stop_scroll(device, time);
+			evdev_stop_scroll(device, time,
+					  LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS);
 			device->scroll.middle_button_scroll_active = false;
 		} else {
 			/* If the button is released quickly enough emit the
@@ -545,6 +547,7 @@ evdev_process_relative(struct evdev_device *device,
 			base,
 			time,
 			LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+			LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
 			-1 * e->value * DEFAULT_AXIS_STEP_DISTANCE);
 		break;
 	case REL_HWHEEL:
@@ -558,6 +561,7 @@ evdev_process_relative(struct evdev_device *device,
 				base,
 				time,
 				LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
+				LIBINPUT_POINTER_AXIS_SOURCE_WHEEL,
 				e->value * DEFAULT_AXIS_STEP_DISTANCE);
 			break;
 		default:
@@ -1483,6 +1487,7 @@ evdev_device_get_size(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)
 {
@@ -1497,6 +1502,7 @@ evdev_post_scroll(struct evdev_device *device,
 		pointer_notify_axis(&device->base,
 				    time,
 				    LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
+				    source,
 				    dy);
 	}
 
@@ -1505,23 +1511,28 @@ evdev_post_scroll(struct evdev_device *device,
 		pointer_notify_axis(&device->base,
 				    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.direction = 0;
diff --git a/src/evdev.h b/src/evdev.h
index 666c8dc..19f6137 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -275,12 +275,15 @@ evdev_pointer_notify_button(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 92bd96b..7b71da2 100644
--- a/src/libinput-private.h
+++ b/src/libinput-private.h
@@ -263,6 +263,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 35262dd..b97117b 100644
--- a/src/libinput.c
+++ b/src/libinput.c
@@ -63,6 +63,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;
 };
 
@@ -374,6 +375,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)
 {
@@ -960,6 +967,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;
@@ -972,6 +980,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 5623bff..932e65d 100644
--- a/src/libinput.h
+++ b/src/libinput.h
@@ -202,6 +202,28 @@ enum libinput_pointer_axis {
 };
 
 /**
+ * @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
  *
  * Event type for events returned by libinput_get_event().
@@ -637,9 +659,8 @@ libinput_event_pointer_get_axis(struct libinput_event_pointer *event);
  * LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL and
  * 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 LIBINPUT_EVENT_POINTER_AXIS,
  * this function returns 0.
@@ -655,6 +676,44 @@ 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
+ *
  * @return The generic libinput_event of this event
  */
 struct libinput_event *
diff --git a/test/pointer.c b/test/pointer.c
index 56b6709..b82f2b8 100644
--- a/test/pointer.c
+++ b/test/pointer.c
@@ -262,6 +262,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 8cd838e..70de791 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -1368,6 +1368,28 @@ START_TEST(touchpad_2fg_scroll)
 }
 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.1, 40, 0);
+	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
+
+	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_FINGER);
+
+	libinput_event_destroy(event);
+}
+END_TEST
+
 START_TEST(touchpad_scroll_natural_defaults)
 {
 	struct litest_device *dev = litest_current_device();
@@ -1984,6 +2006,7 @@ int main(int argc, char **argv) {
 	litest_add("touchpad:topsoftbuttons", clickpad_topsoftbuttons_move_out_ignore, LITEST_TOPBUTTONPAD, LITEST_ANY);
 
 	litest_add("touchpad:scroll", touchpad_2fg_scroll, 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);
diff --git a/test/trackpoint.c b/test/trackpoint.c
index d4dfe41..729062a 100644
--- a/test/trackpoint.c
+++ b/test/trackpoint.c
@@ -94,10 +94,33 @@ START_TEST(trackpoint_scroll)
 }
 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);
+
+	test_2fg_scroll(dev, 1, 6);
+	litest_wait_for_event_of_type(li, LIBINPUT_EVENT_POINTER_AXIS, -1);
+
+	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);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 int main(int argc, char **argv) {
 
 	litest_add("trackpoint:middlebutton", trackpoint_middlebutton, 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);
 }
-- 
2.1.0



More information about the wayland-devel mailing list