[weston] Support axis source, axis discreet, axis frame and axis stop events

Submitted by Peter Hutterer on Oct. 16, 2015, 2:39 a.m.

Details

Message ID 20151016023922.GA7196@jelly.redhat.com
State Superseded
Delegated to: Jonas Ådahl
Headers show

Not browsing as part of any series.

Commit Message

Peter Hutterer Oct. 16, 2015, 2:39 a.m.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
---
The client-side is the simplest implementation here, and I went the easy
route since most clients won't care to register a multitude of handlers for
axis events.

The eventdemo client merely prints the events, it doesn't accumulate them as
they should be. That'd be the job of a slightly more sophisticated toolkit.

Possibly contentious: there's no notify_axis_stop(), it's folded into
notify_axis(). Easy fix if needed.

Also, I recommend not merging this one until we have a test implementation
in mutter + GTK to make sure the protocol is sane enough to use from complex
clients.

 clients/eventdemo.c      | 55 ++++++++++++++++++++++++++++-
 clients/window.c         | 91 +++++++++++++++++++++++++++++++++++++++++++++++-
 clients/window.h         | 28 +++++++++++++++
 src/compositor-wayland.c | 39 +++++++++++++++++++++
 src/compositor-x11.c     | 16 ++++++---
 src/compositor.h         | 10 ++++++
 src/input.c              | 74 ++++++++++++++++++++++++++++++++++-----
 src/libinput-device.c    | 63 +++++++++++++++++++++++++++++++--
 8 files changed, 360 insertions(+), 16 deletions(-)

Patch hide | download patch | download mbox

diff --git a/clients/eventdemo.c b/clients/eventdemo.c
index bdad6fd..f520233 100644
--- a/clients/eventdemo.c
+++ b/clients/eventdemo.c
@@ -259,6 +259,54 @@  axis_handler(struct widget *widget, struct input *input, uint32_t time,
 	       wl_fixed_to_double(value));
 }
 
+static void
+axis_frame_handler(struct widget *widget, struct input *input, void *data)
+{
+	printf("axis frame\n");
+}
+
+static void
+axis_source_handler(struct widget *widget, struct input *input,
+		    uint32_t source, void *data)
+{
+	const char *axis_source;
+
+	switch (source) {
+	case WL_POINTER_AXIS_SOURCE_WHEEL:
+		axis_source = "wheel";
+		break;
+	case WL_POINTER_AXIS_SOURCE_FINGER:
+		axis_source = "finger";
+		break;
+	case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
+		axis_source = "continuous";
+		break;
+	default:
+		axis_source = "<invalid source value>";
+		break;
+	}
+
+	printf("axis source: %s\n", axis_source);
+}
+
+static void
+axis_stop_handler(struct widget *widget, struct input *input,
+		  uint32_t time, uint32_t axis,
+		  void *data)
+{
+	printf("axis stop time: %d, axis: %s\n",
+	       time,
+	       axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
+							 "horizontal");
+}
+
+static void
+axis_discrete_handler(struct widget *widget, struct input *input,
+		      int32_t discrete, void *data)
+{
+	printf("axis discrete value: %d\n", discrete);
+}
+
 /**
  * \brief CALLBACK function, Waylands informs about pointer motion
  * \param widget widget
@@ -348,7 +396,12 @@  eventdemo_create(struct display *d)
 	widget_set_motion_handler(e->widget, motion_handler);
 
 	/* Set the callback axis handler for the window */
-	widget_set_axis_handler(e->widget, axis_handler);
+	widget_set_axis_handlers(e->widget,
+				 axis_handler,
+				 axis_frame_handler,
+				 axis_source_handler,
+				 axis_stop_handler,
+				 axis_discrete_handler);
 
 	/* Initial drawing of the window */
 	window_schedule_resize(e->window, width, height);
diff --git a/clients/window.c b/clients/window.c
index 6d3e944..121037b 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -288,6 +288,10 @@  struct widget {
 	widget_touch_frame_handler_t touch_frame_handler;
 	widget_touch_cancel_handler_t touch_cancel_handler;
 	widget_axis_handler_t axis_handler;
+	widget_axis_frame_handler_t axis_frame_handler;
+	widget_axis_source_handler_t axis_source_handler;
+	widget_axis_stop_handler_t axis_stop_handler;
+	widget_axis_discrete_handler_t axis_discrete_handler;
 	void *user_data;
 	int opaque;
 	int tooltip_count;
@@ -1935,6 +1939,21 @@  widget_set_axis_handler(struct widget *widget,
 	widget->axis_handler = handler;
 }
 
+void
+widget_set_axis_handlers(struct widget *widget,
+			widget_axis_handler_t axis_handler,
+			widget_axis_frame_handler_t axis_frame_handler,
+			widget_axis_source_handler_t axis_source_handler,
+			widget_axis_stop_handler_t axis_stop_handler,
+			widget_axis_discrete_handler_t axis_discrete_handler)
+{
+	widget->axis_handler = axis_handler;
+	widget->axis_frame_handler = axis_frame_handler;
+	widget->axis_source_handler = axis_source_handler;
+	widget->axis_stop_handler = axis_stop_handler;
+	widget->axis_discrete_handler = axis_discrete_handler;
+}
+
 static void
 window_schedule_redraw_task(struct window *window);
 
@@ -2816,12 +2835,82 @@  pointer_handle_axis(void *data, struct wl_pointer *pointer,
 					widget->user_data);
 }
 
+static void
+pointer_handle_axis_frame(void *data, struct wl_pointer *pointer)
+{
+	struct input *input = data;
+	struct widget *widget;
+
+	widget = input->focus_widget;
+	if (input->grab)
+		widget = input->grab;
+	if (widget && widget->axis_frame_handler)
+		(*widget->axis_frame_handler)(widget,
+					      input,
+					      widget->user_data);
+}
+
+static void
+pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
+			   uint32_t source)
+{
+	struct input *input = data;
+	struct widget *widget;
+
+	widget = input->focus_widget;
+	if (input->grab)
+		widget = input->grab;
+	if (widget && widget->axis_source_handler)
+		(*widget->axis_source_handler)(widget,
+					       input,
+					       source,
+					       widget->user_data);
+}
+
+static void
+pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
+			 uint32_t time, uint32_t axis)
+{
+	struct input *input = data;
+	struct widget *widget;
+
+	widget = input->focus_widget;
+	if (input->grab)
+		widget = input->grab;
+	if (widget && widget->axis_stop_handler)
+		(*widget->axis_stop_handler)(widget,
+					     input, time,
+					     axis,
+					     widget->user_data);
+}
+
+static void
+pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
+			     int32_t discrete)
+{
+	struct input *input = data;
+	struct widget *widget;
+
+	widget = input->focus_widget;
+	if (input->grab)
+		widget = input->grab;
+	if (widget && widget->axis_discrete_handler)
+		(*widget->axis_discrete_handler)(widget,
+						 input,
+						 discrete,
+						 widget->user_data);
+}
+
 static const struct wl_pointer_listener pointer_listener = {
 	pointer_handle_enter,
 	pointer_handle_leave,
 	pointer_handle_motion,
 	pointer_handle_button,
 	pointer_handle_axis,
+	pointer_handle_axis_frame,
+	pointer_handle_axis_source,
+	pointer_handle_axis_stop,
+	pointer_handle_axis_discrete,
 };
 
 static void
@@ -5225,7 +5314,7 @@  display_add_input(struct display *d, uint32_t id)
 	input = xzalloc(sizeof *input);
 	input->display = d;
 	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
-				       MIN(d->seat_version, 4));
+				       MIN(d->seat_version, 5));
 	input->touch_focus = NULL;
 	input->pointer_focus = NULL;
 	input->keyboard_focus = NULL;
diff --git a/clients/window.h b/clients/window.h
index b61a62a..c52ed6b 100644
--- a/clients/window.h
+++ b/clients/window.h
@@ -267,6 +267,26 @@  typedef void (*widget_axis_handler_t)(struct widget *widget,
 				      wl_fixed_t value,
 				      void *data);
 
+typedef void (*widget_axis_frame_handler_t)(struct widget *widget,
+					    struct input *input,
+					    void *data);
+
+typedef void (*widget_axis_source_handler_t)(struct widget *widget,
+					     struct input *input,
+					     uint32_t source,
+					     void *data);
+
+typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
+					   struct input *input,
+					   uint32_t time,
+					   uint32_t axis,
+					   void *data);
+
+typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
+					       struct input *input,
+					       int32_t discrete,
+					       void *data);
+
 struct window *
 window_create(struct display *display);
 struct window *
@@ -520,6 +540,14 @@  void
 widget_set_axis_handler(struct widget *widget,
 			widget_axis_handler_t handler);
 void
+widget_set_axis_handlers(struct widget *widget,
+			widget_axis_handler_t axis_handler,
+			widget_axis_frame_handler_t axis_frame_handler,
+			widget_axis_source_handler_t axis_source_handler,
+			widget_axis_stop_handler_t axis_stop_handler,
+			widget_axis_discrete_handler_t axis_discrete_handler);
+
+void
 widget_schedule_redraw(struct widget *widget);
 void
 widget_set_use_cairo(struct widget *widget, int use_cairo);
diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
index 7b11ae4..d3bbcf8 100644
--- a/src/compositor-wayland.c
+++ b/src/compositor-wayland.c
@@ -1429,12 +1429,51 @@  input_handle_axis(void *data, struct wl_pointer *pointer,
 	notify_axis(&input->base, time, axis, value);
 }
 
+static void
+input_handle_axis_frame(void *data, struct wl_pointer *pointer)
+{
+	struct wayland_input *input = data;
+
+	notify_axis_frame(&input->base);
+}
+
+static void
+input_handle_axis_source(void *data, struct wl_pointer *pointer,
+			 uint32_t source)
+{
+	struct wayland_input *input = data;
+
+	notify_axis_source(&input->base, source);
+}
+
+static void
+input_handle_axis_stop(void *data, struct wl_pointer *pointer,
+		       uint32_t time, uint32_t axis)
+{
+	struct wayland_input *input = data;
+
+	notify_axis(&input->base, time, axis, 0);
+}
+
+static void
+input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
+			   int32_t discrete)
+{
+	struct wayland_input *input = data;
+
+	notify_axis_discrete(&input->base, discrete);
+}
+
 static const struct wl_pointer_listener pointer_listener = {
 	input_handle_pointer_enter,
 	input_handle_pointer_leave,
 	input_handle_motion,
 	input_handle_button,
 	input_handle_axis,
+	input_handle_axis_frame,
+	input_handle_axis_source,
+	input_handle_axis_stop,
+	input_handle_axis_discrete,
 };
 
 static void
diff --git a/src/compositor-x11.c b/src/compositor-x11.c
index 9a23996..4b5499d 100644
--- a/src/compositor-x11.c
+++ b/src/compositor-x11.c
@@ -1082,32 +1082,40 @@  x11_backend_deliver_button_event(struct x11_backend *b,
 	case 4:
 		/* Axis are measured in pixels, but the xcb events are discrete
 		 * steps. Therefore move the axis by some pixels every step. */
-		if (state)
+		if (state) {
+			notify_axis_discrete(&b->core_seat, -1);
 			notify_axis(&b->core_seat,
 				    weston_compositor_get_time(),
 				    WL_POINTER_AXIS_VERTICAL_SCROLL,
 				    -DEFAULT_AXIS_STEP_DISTANCE);
+		}
 		return;
 	case 5:
-		if (state)
+		if (state) {
+			notify_axis_discrete(&b->core_seat, 1);
 			notify_axis(&b->core_seat,
 				    weston_compositor_get_time(),
 				    WL_POINTER_AXIS_VERTICAL_SCROLL,
 				    DEFAULT_AXIS_STEP_DISTANCE);
+		}
 		return;
 	case 6:
-		if (state)
+		if (state) {
+			notify_axis_discrete(&b->core_seat, -1);
 			notify_axis(&b->core_seat,
 				    weston_compositor_get_time(),
 				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
 				    -DEFAULT_AXIS_STEP_DISTANCE);
+		}
 		return;
 	case 7:
-		if (state)
+		if (state) {
+			notify_axis_discrete(&b->core_seat, 1);
 			notify_axis(&b->core_seat,
 				    weston_compositor_get_time(),
 				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
 				    DEFAULT_AXIS_STEP_DISTANCE);
+		}
 		return;
 	default:
 		button = button_event->detail + BTN_SIDE - 8;
diff --git a/src/compositor.h b/src/compositor.h
index 2e2a185..1e518e1 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -1075,6 +1075,16 @@  notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
 void
 notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
 	    wl_fixed_t value);
+
+void
+notify_axis_discrete(struct weston_seat *seat, int32_t discrete);
+
+void
+notify_axis_source(struct weston_seat *seat, uint32_t source);
+
+void
+notify_axis_frame(struct weston_seat *seat);
+
 void
 notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
 	   enum wl_keyboard_key_state state,
diff --git a/src/input.c b/src/input.c
index 500c39a..4d7ac2b 100644
--- a/src/input.c
+++ b/src/input.c
@@ -1089,17 +1089,75 @@  notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
 
 	weston_compositor_wake(compositor);
 
-	if (!value)
-		return;
-
 	if (weston_compositor_run_axis_binding(compositor, pointer,
 					       time, axis, value))
 		return;
 
 	resource_list = &pointer->focus_resource_list;
-	wl_resource_for_each(resource, resource_list)
-		wl_pointer_send_axis(resource, time, axis,
-				     value);
+	wl_resource_for_each(resource, resource_list) {
+		if (value)
+			wl_pointer_send_axis(resource, time, axis, value);
+		else if (wl_resource_get_version(resource) >=
+			 WL_POINTER_AXIS_STOP_SINCE_VERSION)
+			wl_pointer_send_axis_stop(resource, time, axis);
+	}
+}
+
+WL_EXPORT void
+notify_axis_discrete(struct weston_seat *seat, int32_t discrete)
+{
+	struct weston_compositor *compositor = seat->compositor;
+	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+	struct wl_resource *resource;
+	struct wl_list *resource_list;
+
+	weston_compositor_wake(compositor);
+
+	resource_list = &pointer->focus_resource_list;
+	wl_resource_for_each(resource, resource_list) {
+		if (wl_resource_get_version(resource) >=
+		    WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
+			wl_pointer_send_axis_discrete(resource, discrete);
+		}
+	}
+}
+
+WL_EXPORT void
+notify_axis_source(struct weston_seat *seat, uint32_t source)
+{
+	struct weston_compositor *compositor = seat->compositor;
+	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+	struct wl_resource *resource;
+	struct wl_list *resource_list;
+
+	weston_compositor_wake(compositor);
+
+	resource_list = &pointer->focus_resource_list;
+	wl_resource_for_each(resource, resource_list) {
+		if (wl_resource_get_version(resource) >=
+		    WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
+			wl_pointer_send_axis_source(resource, source);
+		}
+	}
+}
+
+WL_EXPORT void
+notify_axis_frame(struct weston_seat *seat)
+{
+	struct weston_compositor *compositor = seat->compositor;
+	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
+	struct wl_resource *resource;
+	struct wl_list *resource_list;
+
+	weston_compositor_wake(compositor);
+
+	resource_list = &pointer->focus_resource_list;
+	wl_resource_for_each(resource, resource_list) {
+		if (wl_resource_get_version(resource) >=
+		    WL_POINTER_AXIS_FRAME_SINCE_VERSION) {
+			wl_pointer_send_axis_frame(resource);
+		}
+	}
 }
 
 WL_EXPORT int
@@ -1958,7 +2016,7 @@  bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
 	enum wl_seat_capability caps = 0;
 
 	resource = wl_resource_create(client,
-				      &wl_seat_interface, MIN(version, 4), id);
+				      &wl_seat_interface, MIN(version, 5), id);
 	wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
 	wl_resource_set_implementation(resource, &seat_interface, data,
 				       unbind_resource);
@@ -2361,7 +2419,7 @@  weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
 	wl_signal_init(&seat->destroy_signal);
 	wl_signal_init(&seat->updated_caps_signal);
 
-	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 4,
+	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
 					seat, bind_seat);
 
 	seat->compositor = ec;
diff --git a/src/libinput-device.c b/src/libinput-device.c
index 2cbfb88..f7f2eef 100644
--- a/src/libinput-device.c
+++ b/src/libinput-device.c
@@ -161,6 +161,7 @@  normalize_scroll(struct libinput_event_pointer *pointer_event,
 	double value;
 
 	source = libinput_event_pointer_get_axis_source(pointer_event);
+
 	/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
 	   the value is the angle of the click in degrees. To keep
 	   backwards-compat with existing clients, we just send multiples of
@@ -180,8 +181,7 @@  normalize_scroll(struct libinput_event_pointer *pointer_event,
 	default:
 		value = 0;
 		if (warned < 5) {
-			weston_log("Unknown scroll source %d. Event discarded\n",
-				   source);
+			weston_log("Unknown scroll source %d.\n", source);
 			warned++;
 		}
 		break;
@@ -190,6 +190,23 @@  normalize_scroll(struct libinput_event_pointer *pointer_event,
 	return value;
 }
 
+static bool
+fetch_axis_discrete(struct libinput_event_pointer *pointer_event,
+		    enum libinput_pointer_axis axis,
+		    int32_t *discrete)
+{
+	enum libinput_pointer_axis_source source;
+
+	source = libinput_event_pointer_get_axis_source(pointer_event);
+
+	if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
+		return false;
+
+	*discrete = libinput_event_pointer_get_axis_value_discrete(
+						   pointer_event, axis);
+	return true;
+}
+
 static void
 handle_pointer_axis(struct libinput_device *libinput_device,
 		    struct libinput_event_pointer *pointer_event)
@@ -197,10 +214,46 @@  handle_pointer_axis(struct libinput_device *libinput_device,
 	struct evdev_device *device =
 		libinput_device_get_user_data(libinput_device);
 	double value;
+	int32_t value_discrete;
 	enum libinput_pointer_axis axis;
+	enum libinput_pointer_axis_source source;
+	uint32_t wl_axis_source;
+	bool need_axis_source_event = true;
+	bool has_vert, has_horiz;
+
+	has_vert = libinput_event_pointer_has_axis(pointer_event,
+				   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
+	has_horiz = libinput_event_pointer_has_axis(pointer_event,
+				   LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
+
+	if (!has_vert && !has_horiz)
+		return;
+
+	source = libinput_event_pointer_get_axis_source(pointer_event);
+	switch (source) {
+	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
+		wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
+		wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
+		break;
+	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
+		wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
+		break;
+	default:
+		need_axis_source_event = false;
+		break;
+	}
+
+	if (need_axis_source_event)
+		notify_axis_source(device->seat, wl_axis_source);
 
 	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
 	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
+		if (fetch_axis_discrete(pointer_event, axis,
+					&value_discrete))
+		    notify_axis_discrete(device->seat, value_discrete);
+
 		value = normalize_scroll(pointer_event, axis);
 		notify_axis(device->seat,
 			    libinput_event_pointer_get_time(pointer_event),
@@ -210,12 +263,18 @@  handle_pointer_axis(struct libinput_device *libinput_device,
 
 	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
 	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
+		if (fetch_axis_discrete(pointer_event, axis,
+					&value_discrete))
+		    notify_axis_discrete(device->seat, value_discrete);
+
 		value = normalize_scroll(pointer_event, axis);
 		notify_axis(device->seat,
 			    libinput_event_pointer_get_time(pointer_event),
 			    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
 			    wl_fixed_from_double(value));
 	}
+
+	notify_axis_frame(device->seat);
 }
 
 static void

Comments

On Fri, Oct 16, 2015 at 12:39:22PM +1000, Peter Hutterer wrote:
> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> ---
> The client-side is the simplest implementation here, and I went the easy
> route since most clients won't care to register a multitude of handlers for
> axis events.
> 
> The eventdemo client merely prints the events, it doesn't accumulate them as
> they should be. That'd be the job of a slightly more sophisticated toolkit.
> 
> Possibly contentious: there's no notify_axis_stop(), it's folded into
> notify_axis(). Easy fix if needed.
> 
> Also, I recommend not merging this one until we have a test implementation
> in mutter + GTK to make sure the protocol is sane enough to use from complex
> clients.

Thanks for resending these patches.  Since the main goal here is
assuring the protocol is right, should we also hold off on landing the
wayland piece until there's consensus around this one too, or is the
protocol part ok to land?

Bryce

>  clients/eventdemo.c      | 55 ++++++++++++++++++++++++++++-
>  clients/window.c         | 91 +++++++++++++++++++++++++++++++++++++++++++++++-
>  clients/window.h         | 28 +++++++++++++++
>  src/compositor-wayland.c | 39 +++++++++++++++++++++
>  src/compositor-x11.c     | 16 ++++++---
>  src/compositor.h         | 10 ++++++
>  src/input.c              | 74 ++++++++++++++++++++++++++++++++++-----
>  src/libinput-device.c    | 63 +++++++++++++++++++++++++++++++--
>  8 files changed, 360 insertions(+), 16 deletions(-)
> 
> diff --git a/clients/eventdemo.c b/clients/eventdemo.c
> index bdad6fd..f520233 100644
> --- a/clients/eventdemo.c
> +++ b/clients/eventdemo.c
> @@ -259,6 +259,54 @@ axis_handler(struct widget *widget, struct input *input, uint32_t time,
>  	       wl_fixed_to_double(value));
>  }
>  
> +static void
> +axis_frame_handler(struct widget *widget, struct input *input, void *data)
> +{
> +	printf("axis frame\n");
> +}
> +
> +static void
> +axis_source_handler(struct widget *widget, struct input *input,
> +		    uint32_t source, void *data)
> +{
> +	const char *axis_source;
> +
> +	switch (source) {
> +	case WL_POINTER_AXIS_SOURCE_WHEEL:
> +		axis_source = "wheel";
> +		break;
> +	case WL_POINTER_AXIS_SOURCE_FINGER:
> +		axis_source = "finger";
> +		break;
> +	case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
> +		axis_source = "continuous";
> +		break;
> +	default:
> +		axis_source = "<invalid source value>";
> +		break;
> +	}
> +
> +	printf("axis source: %s\n", axis_source);
> +}
> +
> +static void
> +axis_stop_handler(struct widget *widget, struct input *input,
> +		  uint32_t time, uint32_t axis,
> +		  void *data)
> +{
> +	printf("axis stop time: %d, axis: %s\n",
> +	       time,
> +	       axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
> +							 "horizontal");
> +}
> +
> +static void
> +axis_discrete_handler(struct widget *widget, struct input *input,
> +		      int32_t discrete, void *data)
> +{
> +	printf("axis discrete value: %d\n", discrete);
> +}
> +
>  /**
>   * \brief CALLBACK function, Waylands informs about pointer motion
>   * \param widget widget
> @@ -348,7 +396,12 @@ eventdemo_create(struct display *d)
>  	widget_set_motion_handler(e->widget, motion_handler);
>  
>  	/* Set the callback axis handler for the window */
> -	widget_set_axis_handler(e->widget, axis_handler);
> +	widget_set_axis_handlers(e->widget,
> +				 axis_handler,
> +				 axis_frame_handler,
> +				 axis_source_handler,
> +				 axis_stop_handler,
> +				 axis_discrete_handler);
>  
>  	/* Initial drawing of the window */
>  	window_schedule_resize(e->window, width, height);
> diff --git a/clients/window.c b/clients/window.c
> index 6d3e944..121037b 100644
> --- a/clients/window.c
> +++ b/clients/window.c
> @@ -288,6 +288,10 @@ struct widget {
>  	widget_touch_frame_handler_t touch_frame_handler;
>  	widget_touch_cancel_handler_t touch_cancel_handler;
>  	widget_axis_handler_t axis_handler;
> +	widget_axis_frame_handler_t axis_frame_handler;
> +	widget_axis_source_handler_t axis_source_handler;
> +	widget_axis_stop_handler_t axis_stop_handler;
> +	widget_axis_discrete_handler_t axis_discrete_handler;
>  	void *user_data;
>  	int opaque;
>  	int tooltip_count;
> @@ -1935,6 +1939,21 @@ widget_set_axis_handler(struct widget *widget,
>  	widget->axis_handler = handler;
>  }
>  
> +void
> +widget_set_axis_handlers(struct widget *widget,
> +			widget_axis_handler_t axis_handler,
> +			widget_axis_frame_handler_t axis_frame_handler,
> +			widget_axis_source_handler_t axis_source_handler,
> +			widget_axis_stop_handler_t axis_stop_handler,
> +			widget_axis_discrete_handler_t axis_discrete_handler)
> +{
> +	widget->axis_handler = axis_handler;
> +	widget->axis_frame_handler = axis_frame_handler;
> +	widget->axis_source_handler = axis_source_handler;
> +	widget->axis_stop_handler = axis_stop_handler;
> +	widget->axis_discrete_handler = axis_discrete_handler;
> +}
> +
>  static void
>  window_schedule_redraw_task(struct window *window);
>  
> @@ -2816,12 +2835,82 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
>  					widget->user_data);
>  }
>  
> +static void
> +pointer_handle_axis_frame(void *data, struct wl_pointer *pointer)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_frame_handler)
> +		(*widget->axis_frame_handler)(widget,
> +					      input,
> +					      widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
> +			   uint32_t source)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_source_handler)
> +		(*widget->axis_source_handler)(widget,
> +					       input,
> +					       source,
> +					       widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
> +			 uint32_t time, uint32_t axis)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_stop_handler)
> +		(*widget->axis_stop_handler)(widget,
> +					     input, time,
> +					     axis,
> +					     widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> +			     int32_t discrete)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_discrete_handler)
> +		(*widget->axis_discrete_handler)(widget,
> +						 input,
> +						 discrete,
> +						 widget->user_data);
> +}
> +
>  static const struct wl_pointer_listener pointer_listener = {
>  	pointer_handle_enter,
>  	pointer_handle_leave,
>  	pointer_handle_motion,
>  	pointer_handle_button,
>  	pointer_handle_axis,
> +	pointer_handle_axis_frame,
> +	pointer_handle_axis_source,
> +	pointer_handle_axis_stop,
> +	pointer_handle_axis_discrete,
>  };
>  
>  static void
> @@ -5225,7 +5314,7 @@ display_add_input(struct display *d, uint32_t id)
>  	input = xzalloc(sizeof *input);
>  	input->display = d;
>  	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
> -				       MIN(d->seat_version, 4));
> +				       MIN(d->seat_version, 5));
>  	input->touch_focus = NULL;
>  	input->pointer_focus = NULL;
>  	input->keyboard_focus = NULL;
> diff --git a/clients/window.h b/clients/window.h
> index b61a62a..c52ed6b 100644
> --- a/clients/window.h
> +++ b/clients/window.h
> @@ -267,6 +267,26 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
>  				      wl_fixed_t value,
>  				      void *data);
>  
> +typedef void (*widget_axis_frame_handler_t)(struct widget *widget,
> +					    struct input *input,
> +					    void *data);
> +
> +typedef void (*widget_axis_source_handler_t)(struct widget *widget,
> +					     struct input *input,
> +					     uint32_t source,
> +					     void *data);
> +
> +typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
> +					   struct input *input,
> +					   uint32_t time,
> +					   uint32_t axis,
> +					   void *data);
> +
> +typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
> +					       struct input *input,
> +					       int32_t discrete,
> +					       void *data);
> +
>  struct window *
>  window_create(struct display *display);
>  struct window *
> @@ -520,6 +540,14 @@ void
>  widget_set_axis_handler(struct widget *widget,
>  			widget_axis_handler_t handler);
>  void
> +widget_set_axis_handlers(struct widget *widget,
> +			widget_axis_handler_t axis_handler,
> +			widget_axis_frame_handler_t axis_frame_handler,
> +			widget_axis_source_handler_t axis_source_handler,
> +			widget_axis_stop_handler_t axis_stop_handler,
> +			widget_axis_discrete_handler_t axis_discrete_handler);
> +
> +void
>  widget_schedule_redraw(struct widget *widget);
>  void
>  widget_set_use_cairo(struct widget *widget, int use_cairo);
> diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
> index 7b11ae4..d3bbcf8 100644
> --- a/src/compositor-wayland.c
> +++ b/src/compositor-wayland.c
> @@ -1429,12 +1429,51 @@ input_handle_axis(void *data, struct wl_pointer *pointer,
>  	notify_axis(&input->base, time, axis, value);
>  }
>  
> +static void
> +input_handle_axis_frame(void *data, struct wl_pointer *pointer)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_frame(&input->base);
> +}
> +
> +static void
> +input_handle_axis_source(void *data, struct wl_pointer *pointer,
> +			 uint32_t source)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_source(&input->base, source);
> +}
> +
> +static void
> +input_handle_axis_stop(void *data, struct wl_pointer *pointer,
> +		       uint32_t time, uint32_t axis)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis(&input->base, time, axis, 0);
> +}
> +
> +static void
> +input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> +			   int32_t discrete)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_discrete(&input->base, discrete);
> +}
> +
>  static const struct wl_pointer_listener pointer_listener = {
>  	input_handle_pointer_enter,
>  	input_handle_pointer_leave,
>  	input_handle_motion,
>  	input_handle_button,
>  	input_handle_axis,
> +	input_handle_axis_frame,
> +	input_handle_axis_source,
> +	input_handle_axis_stop,
> +	input_handle_axis_discrete,
>  };
>  
>  static void
> diff --git a/src/compositor-x11.c b/src/compositor-x11.c
> index 9a23996..4b5499d 100644
> --- a/src/compositor-x11.c
> +++ b/src/compositor-x11.c
> @@ -1082,32 +1082,40 @@ x11_backend_deliver_button_event(struct x11_backend *b,
>  	case 4:
>  		/* Axis are measured in pixels, but the xcb events are discrete
>  		 * steps. Therefore move the axis by some pixels every step. */
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, -1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
>  				    -DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 5:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, 1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
>  				    DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 6:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, -1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  				    -DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 7:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, 1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  				    DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	default:
>  		button = button_event->detail + BTN_SIDE - 8;
> diff --git a/src/compositor.h b/src/compositor.h
> index 2e2a185..1e518e1 100644
> --- a/src/compositor.h
> +++ b/src/compositor.h
> @@ -1075,6 +1075,16 @@ notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
>  void
>  notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
>  	    wl_fixed_t value);
> +
> +void
> +notify_axis_discrete(struct weston_seat *seat, int32_t discrete);
> +
> +void
> +notify_axis_source(struct weston_seat *seat, uint32_t source);
> +
> +void
> +notify_axis_frame(struct weston_seat *seat);
> +
>  void
>  notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
>  	   enum wl_keyboard_key_state state,
> diff --git a/src/input.c b/src/input.c
> index 500c39a..4d7ac2b 100644
> --- a/src/input.c
> +++ b/src/input.c
> @@ -1089,17 +1089,75 @@ notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
>  
>  	weston_compositor_wake(compositor);
>  
> -	if (!value)
> -		return;
> -
>  	if (weston_compositor_run_axis_binding(compositor, pointer,
>  					       time, axis, value))
>  		return;
>  
>  	resource_list = &pointer->focus_resource_list;
> -	wl_resource_for_each(resource, resource_list)
> -		wl_pointer_send_axis(resource, time, axis,
> -				     value);
> +	wl_resource_for_each(resource, resource_list) {
> +		if (value)
> +			wl_pointer_send_axis(resource, time, axis, value);
> +		else if (wl_resource_get_version(resource) >=
> +			 WL_POINTER_AXIS_STOP_SINCE_VERSION)
> +			wl_pointer_send_axis_stop(resource, time, axis);
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_discrete(struct weston_seat *seat, int32_t discrete)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
> +			wl_pointer_send_axis_discrete(resource, discrete);
> +		}
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_source(struct weston_seat *seat, uint32_t source)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
> +			wl_pointer_send_axis_source(resource, source);
> +		}
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_frame(struct weston_seat *seat)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_FRAME_SINCE_VERSION) {
> +			wl_pointer_send_axis_frame(resource);
> +		}
> +	}
>  }
>  
>  WL_EXPORT int
> @@ -1958,7 +2016,7 @@ bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
>  	enum wl_seat_capability caps = 0;
>  
>  	resource = wl_resource_create(client,
> -				      &wl_seat_interface, MIN(version, 4), id);
> +				      &wl_seat_interface, MIN(version, 5), id);
>  	wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
>  	wl_resource_set_implementation(resource, &seat_interface, data,
>  				       unbind_resource);
> @@ -2361,7 +2419,7 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
>  	wl_signal_init(&seat->destroy_signal);
>  	wl_signal_init(&seat->updated_caps_signal);
>  
> -	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 4,
> +	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
>  					seat, bind_seat);
>  
>  	seat->compositor = ec;
> diff --git a/src/libinput-device.c b/src/libinput-device.c
> index 2cbfb88..f7f2eef 100644
> --- a/src/libinput-device.c
> +++ b/src/libinput-device.c
> @@ -161,6 +161,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	double value;
>  
>  	source = libinput_event_pointer_get_axis_source(pointer_event);
> +
>  	/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
>  	   the value is the angle of the click in degrees. To keep
>  	   backwards-compat with existing clients, we just send multiples of
> @@ -180,8 +181,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	default:
>  		value = 0;
>  		if (warned < 5) {
> -			weston_log("Unknown scroll source %d. Event discarded\n",
> -				   source);
> +			weston_log("Unknown scroll source %d.\n", source);
>  			warned++;
>  		}
>  		break;
> @@ -190,6 +190,23 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	return value;
>  }
>  
> +static bool
> +fetch_axis_discrete(struct libinput_event_pointer *pointer_event,
> +		    enum libinput_pointer_axis axis,
> +		    int32_t *discrete)
> +{
> +	enum libinput_pointer_axis_source source;
> +
> +	source = libinput_event_pointer_get_axis_source(pointer_event);
> +
> +	if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
> +		return false;
> +
> +	*discrete = libinput_event_pointer_get_axis_value_discrete(
> +						   pointer_event, axis);
> +	return true;
> +}
> +
>  static void
>  handle_pointer_axis(struct libinput_device *libinput_device,
>  		    struct libinput_event_pointer *pointer_event)
> @@ -197,10 +214,46 @@ handle_pointer_axis(struct libinput_device *libinput_device,
>  	struct evdev_device *device =
>  		libinput_device_get_user_data(libinput_device);
>  	double value;
> +	int32_t value_discrete;
>  	enum libinput_pointer_axis axis;
> +	enum libinput_pointer_axis_source source;
> +	uint32_t wl_axis_source;
> +	bool need_axis_source_event = true;
> +	bool has_vert, has_horiz;
> +
> +	has_vert = libinput_event_pointer_has_axis(pointer_event,
> +				   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
> +	has_horiz = libinput_event_pointer_has_axis(pointer_event,
> +				   LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
> +
> +	if (!has_vert && !has_horiz)
> +		return;
> +
> +	source = libinput_event_pointer_get_axis_source(pointer_event);
> +	switch (source) {
> +	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
> +		break;
> +	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
> +		break;
> +	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
> +		break;
> +	default:
> +		need_axis_source_event = false;
> +		break;
> +	}
> +
> +	if (need_axis_source_event)
> +		notify_axis_source(device->seat, wl_axis_source);
>  
>  	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
>  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> +		if (fetch_axis_discrete(pointer_event, axis,
> +					&value_discrete))
> +		    notify_axis_discrete(device->seat, value_discrete);
> +
>  		value = normalize_scroll(pointer_event, axis);
>  		notify_axis(device->seat,
>  			    libinput_event_pointer_get_time(pointer_event),
> @@ -210,12 +263,18 @@ handle_pointer_axis(struct libinput_device *libinput_device,
>  
>  	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
>  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> +		if (fetch_axis_discrete(pointer_event, axis,
> +					&value_discrete))
> +		    notify_axis_discrete(device->seat, value_discrete);
> +
>  		value = normalize_scroll(pointer_event, axis);
>  		notify_axis(device->seat,
>  			    libinput_event_pointer_get_time(pointer_event),
>  			    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  			    wl_fixed_from_double(value));
>  	}
> +
> +	notify_axis_frame(device->seat);
>  }
>  
>  static void
> -- 
> 2.4.3
On Mon, Oct 19, 2015 at 06:17:31PM -0700, Bryce Harrington wrote:
> On Fri, Oct 16, 2015 at 12:39:22PM +1000, Peter Hutterer wrote:
> > Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> > ---
> > The client-side is the simplest implementation here, and I went the easy
> > route since most clients won't care to register a multitude of handlers for
> > axis events.
> > 
> > The eventdemo client merely prints the events, it doesn't accumulate them as
> > they should be. That'd be the job of a slightly more sophisticated toolkit.
> > 
> > Possibly contentious: there's no notify_axis_stop(), it's folded into
> > notify_axis(). Easy fix if needed.
> > 
> > Also, I recommend not merging this one until we have a test implementation
> > in mutter + GTK to make sure the protocol is sane enough to use from complex
> > clients.
> 
> Thanks for resending these patches.  Since the main goal here is
> assuring the protocol is right, should we also hold off on landing the
> wayland piece until there's consensus around this one too, or is the
> protocol part ok to land?

I'm planning to do a second review of the protocol patches, and there
will at least be minor changes needed, so please don't merge anything
yet. Will try to get that done as soon as possible.


Jonas

> 
> Bryce
> 
> >  clients/eventdemo.c      | 55 ++++++++++++++++++++++++++++-
> >  clients/window.c         | 91 +++++++++++++++++++++++++++++++++++++++++++++++-
> >  clients/window.h         | 28 +++++++++++++++
> >  src/compositor-wayland.c | 39 +++++++++++++++++++++
> >  src/compositor-x11.c     | 16 ++++++---
> >  src/compositor.h         | 10 ++++++
> >  src/input.c              | 74 ++++++++++++++++++++++++++++++++++-----
> >  src/libinput-device.c    | 63 +++++++++++++++++++++++++++++++--
> >  8 files changed, 360 insertions(+), 16 deletions(-)
> > 
> > diff --git a/clients/eventdemo.c b/clients/eventdemo.c
> > index bdad6fd..f520233 100644
> > --- a/clients/eventdemo.c
> > +++ b/clients/eventdemo.c
> > @@ -259,6 +259,54 @@ axis_handler(struct widget *widget, struct input *input, uint32_t time,
> >  	       wl_fixed_to_double(value));
> >  }
> >  
> > +static void
> > +axis_frame_handler(struct widget *widget, struct input *input, void *data)
> > +{
> > +	printf("axis frame\n");
> > +}
> > +
> > +static void
> > +axis_source_handler(struct widget *widget, struct input *input,
> > +		    uint32_t source, void *data)
> > +{
> > +	const char *axis_source;
> > +
> > +	switch (source) {
> > +	case WL_POINTER_AXIS_SOURCE_WHEEL:
> > +		axis_source = "wheel";
> > +		break;
> > +	case WL_POINTER_AXIS_SOURCE_FINGER:
> > +		axis_source = "finger";
> > +		break;
> > +	case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
> > +		axis_source = "continuous";
> > +		break;
> > +	default:
> > +		axis_source = "<invalid source value>";
> > +		break;
> > +	}
> > +
> > +	printf("axis source: %s\n", axis_source);
> > +}
> > +
> > +static void
> > +axis_stop_handler(struct widget *widget, struct input *input,
> > +		  uint32_t time, uint32_t axis,
> > +		  void *data)
> > +{
> > +	printf("axis stop time: %d, axis: %s\n",
> > +	       time,
> > +	       axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
> > +							 "horizontal");
> > +}
> > +
> > +static void
> > +axis_discrete_handler(struct widget *widget, struct input *input,
> > +		      int32_t discrete, void *data)
> > +{
> > +	printf("axis discrete value: %d\n", discrete);
> > +}
> > +
> >  /**
> >   * \brief CALLBACK function, Waylands informs about pointer motion
> >   * \param widget widget
> > @@ -348,7 +396,12 @@ eventdemo_create(struct display *d)
> >  	widget_set_motion_handler(e->widget, motion_handler);
> >  
> >  	/* Set the callback axis handler for the window */
> > -	widget_set_axis_handler(e->widget, axis_handler);
> > +	widget_set_axis_handlers(e->widget,
> > +				 axis_handler,
> > +				 axis_frame_handler,
> > +				 axis_source_handler,
> > +				 axis_stop_handler,
> > +				 axis_discrete_handler);
> >  
> >  	/* Initial drawing of the window */
> >  	window_schedule_resize(e->window, width, height);
> > diff --git a/clients/window.c b/clients/window.c
> > index 6d3e944..121037b 100644
> > --- a/clients/window.c
> > +++ b/clients/window.c
> > @@ -288,6 +288,10 @@ struct widget {
> >  	widget_touch_frame_handler_t touch_frame_handler;
> >  	widget_touch_cancel_handler_t touch_cancel_handler;
> >  	widget_axis_handler_t axis_handler;
> > +	widget_axis_frame_handler_t axis_frame_handler;
> > +	widget_axis_source_handler_t axis_source_handler;
> > +	widget_axis_stop_handler_t axis_stop_handler;
> > +	widget_axis_discrete_handler_t axis_discrete_handler;
> >  	void *user_data;
> >  	int opaque;
> >  	int tooltip_count;
> > @@ -1935,6 +1939,21 @@ widget_set_axis_handler(struct widget *widget,
> >  	widget->axis_handler = handler;
> >  }
> >  
> > +void
> > +widget_set_axis_handlers(struct widget *widget,
> > +			widget_axis_handler_t axis_handler,
> > +			widget_axis_frame_handler_t axis_frame_handler,
> > +			widget_axis_source_handler_t axis_source_handler,
> > +			widget_axis_stop_handler_t axis_stop_handler,
> > +			widget_axis_discrete_handler_t axis_discrete_handler)
> > +{
> > +	widget->axis_handler = axis_handler;
> > +	widget->axis_frame_handler = axis_frame_handler;
> > +	widget->axis_source_handler = axis_source_handler;
> > +	widget->axis_stop_handler = axis_stop_handler;
> > +	widget->axis_discrete_handler = axis_discrete_handler;
> > +}
> > +
> >  static void
> >  window_schedule_redraw_task(struct window *window);
> >  
> > @@ -2816,12 +2835,82 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
> >  					widget->user_data);
> >  }
> >  
> > +static void
> > +pointer_handle_axis_frame(void *data, struct wl_pointer *pointer)
> > +{
> > +	struct input *input = data;
> > +	struct widget *widget;
> > +
> > +	widget = input->focus_widget;
> > +	if (input->grab)
> > +		widget = input->grab;
> > +	if (widget && widget->axis_frame_handler)
> > +		(*widget->axis_frame_handler)(widget,
> > +					      input,
> > +					      widget->user_data);
> > +}
> > +
> > +static void
> > +pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
> > +			   uint32_t source)
> > +{
> > +	struct input *input = data;
> > +	struct widget *widget;
> > +
> > +	widget = input->focus_widget;
> > +	if (input->grab)
> > +		widget = input->grab;
> > +	if (widget && widget->axis_source_handler)
> > +		(*widget->axis_source_handler)(widget,
> > +					       input,
> > +					       source,
> > +					       widget->user_data);
> > +}
> > +
> > +static void
> > +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
> > +			 uint32_t time, uint32_t axis)
> > +{
> > +	struct input *input = data;
> > +	struct widget *widget;
> > +
> > +	widget = input->focus_widget;
> > +	if (input->grab)
> > +		widget = input->grab;
> > +	if (widget && widget->axis_stop_handler)
> > +		(*widget->axis_stop_handler)(widget,
> > +					     input, time,
> > +					     axis,
> > +					     widget->user_data);
> > +}
> > +
> > +static void
> > +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> > +			     int32_t discrete)
> > +{
> > +	struct input *input = data;
> > +	struct widget *widget;
> > +
> > +	widget = input->focus_widget;
> > +	if (input->grab)
> > +		widget = input->grab;
> > +	if (widget && widget->axis_discrete_handler)
> > +		(*widget->axis_discrete_handler)(widget,
> > +						 input,
> > +						 discrete,
> > +						 widget->user_data);
> > +}
> > +
> >  static const struct wl_pointer_listener pointer_listener = {
> >  	pointer_handle_enter,
> >  	pointer_handle_leave,
> >  	pointer_handle_motion,
> >  	pointer_handle_button,
> >  	pointer_handle_axis,
> > +	pointer_handle_axis_frame,
> > +	pointer_handle_axis_source,
> > +	pointer_handle_axis_stop,
> > +	pointer_handle_axis_discrete,
> >  };
> >  
> >  static void
> > @@ -5225,7 +5314,7 @@ display_add_input(struct display *d, uint32_t id)
> >  	input = xzalloc(sizeof *input);
> >  	input->display = d;
> >  	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
> > -				       MIN(d->seat_version, 4));
> > +				       MIN(d->seat_version, 5));
> >  	input->touch_focus = NULL;
> >  	input->pointer_focus = NULL;
> >  	input->keyboard_focus = NULL;
> > diff --git a/clients/window.h b/clients/window.h
> > index b61a62a..c52ed6b 100644
> > --- a/clients/window.h
> > +++ b/clients/window.h
> > @@ -267,6 +267,26 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
> >  				      wl_fixed_t value,
> >  				      void *data);
> >  
> > +typedef void (*widget_axis_frame_handler_t)(struct widget *widget,
> > +					    struct input *input,
> > +					    void *data);
> > +
> > +typedef void (*widget_axis_source_handler_t)(struct widget *widget,
> > +					     struct input *input,
> > +					     uint32_t source,
> > +					     void *data);
> > +
> > +typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
> > +					   struct input *input,
> > +					   uint32_t time,
> > +					   uint32_t axis,
> > +					   void *data);
> > +
> > +typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
> > +					       struct input *input,
> > +					       int32_t discrete,
> > +					       void *data);
> > +
> >  struct window *
> >  window_create(struct display *display);
> >  struct window *
> > @@ -520,6 +540,14 @@ void
> >  widget_set_axis_handler(struct widget *widget,
> >  			widget_axis_handler_t handler);
> >  void
> > +widget_set_axis_handlers(struct widget *widget,
> > +			widget_axis_handler_t axis_handler,
> > +			widget_axis_frame_handler_t axis_frame_handler,
> > +			widget_axis_source_handler_t axis_source_handler,
> > +			widget_axis_stop_handler_t axis_stop_handler,
> > +			widget_axis_discrete_handler_t axis_discrete_handler);
> > +
> > +void
> >  widget_schedule_redraw(struct widget *widget);
> >  void
> >  widget_set_use_cairo(struct widget *widget, int use_cairo);
> > diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
> > index 7b11ae4..d3bbcf8 100644
> > --- a/src/compositor-wayland.c
> > +++ b/src/compositor-wayland.c
> > @@ -1429,12 +1429,51 @@ input_handle_axis(void *data, struct wl_pointer *pointer,
> >  	notify_axis(&input->base, time, axis, value);
> >  }
> >  
> > +static void
> > +input_handle_axis_frame(void *data, struct wl_pointer *pointer)
> > +{
> > +	struct wayland_input *input = data;
> > +
> > +	notify_axis_frame(&input->base);
> > +}
> > +
> > +static void
> > +input_handle_axis_source(void *data, struct wl_pointer *pointer,
> > +			 uint32_t source)
> > +{
> > +	struct wayland_input *input = data;
> > +
> > +	notify_axis_source(&input->base, source);
> > +}
> > +
> > +static void
> > +input_handle_axis_stop(void *data, struct wl_pointer *pointer,
> > +		       uint32_t time, uint32_t axis)
> > +{
> > +	struct wayland_input *input = data;
> > +
> > +	notify_axis(&input->base, time, axis, 0);
> > +}
> > +
> > +static void
> > +input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> > +			   int32_t discrete)
> > +{
> > +	struct wayland_input *input = data;
> > +
> > +	notify_axis_discrete(&input->base, discrete);
> > +}
> > +
> >  static const struct wl_pointer_listener pointer_listener = {
> >  	input_handle_pointer_enter,
> >  	input_handle_pointer_leave,
> >  	input_handle_motion,
> >  	input_handle_button,
> >  	input_handle_axis,
> > +	input_handle_axis_frame,
> > +	input_handle_axis_source,
> > +	input_handle_axis_stop,
> > +	input_handle_axis_discrete,
> >  };
> >  
> >  static void
> > diff --git a/src/compositor-x11.c b/src/compositor-x11.c
> > index 9a23996..4b5499d 100644
> > --- a/src/compositor-x11.c
> > +++ b/src/compositor-x11.c
> > @@ -1082,32 +1082,40 @@ x11_backend_deliver_button_event(struct x11_backend *b,
> >  	case 4:
> >  		/* Axis are measured in pixels, but the xcb events are discrete
> >  		 * steps. Therefore move the axis by some pixels every step. */
> > -		if (state)
> > +		if (state) {
> > +			notify_axis_discrete(&b->core_seat, -1);
> >  			notify_axis(&b->core_seat,
> >  				    weston_compositor_get_time(),
> >  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
> >  				    -DEFAULT_AXIS_STEP_DISTANCE);
> > +		}
> >  		return;
> >  	case 5:
> > -		if (state)
> > +		if (state) {
> > +			notify_axis_discrete(&b->core_seat, 1);
> >  			notify_axis(&b->core_seat,
> >  				    weston_compositor_get_time(),
> >  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
> >  				    DEFAULT_AXIS_STEP_DISTANCE);
> > +		}
> >  		return;
> >  	case 6:
> > -		if (state)
> > +		if (state) {
> > +			notify_axis_discrete(&b->core_seat, -1);
> >  			notify_axis(&b->core_seat,
> >  				    weston_compositor_get_time(),
> >  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> >  				    -DEFAULT_AXIS_STEP_DISTANCE);
> > +		}
> >  		return;
> >  	case 7:
> > -		if (state)
> > +		if (state) {
> > +			notify_axis_discrete(&b->core_seat, 1);
> >  			notify_axis(&b->core_seat,
> >  				    weston_compositor_get_time(),
> >  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> >  				    DEFAULT_AXIS_STEP_DISTANCE);
> > +		}
> >  		return;
> >  	default:
> >  		button = button_event->detail + BTN_SIDE - 8;
> > diff --git a/src/compositor.h b/src/compositor.h
> > index 2e2a185..1e518e1 100644
> > --- a/src/compositor.h
> > +++ b/src/compositor.h
> > @@ -1075,6 +1075,16 @@ notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
> >  void
> >  notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
> >  	    wl_fixed_t value);
> > +
> > +void
> > +notify_axis_discrete(struct weston_seat *seat, int32_t discrete);
> > +
> > +void
> > +notify_axis_source(struct weston_seat *seat, uint32_t source);
> > +
> > +void
> > +notify_axis_frame(struct weston_seat *seat);
> > +
> >  void
> >  notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
> >  	   enum wl_keyboard_key_state state,
> > diff --git a/src/input.c b/src/input.c
> > index 500c39a..4d7ac2b 100644
> > --- a/src/input.c
> > +++ b/src/input.c
> > @@ -1089,17 +1089,75 @@ notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
> >  
> >  	weston_compositor_wake(compositor);
> >  
> > -	if (!value)
> > -		return;
> > -
> >  	if (weston_compositor_run_axis_binding(compositor, pointer,
> >  					       time, axis, value))
> >  		return;
> >  
> >  	resource_list = &pointer->focus_resource_list;
> > -	wl_resource_for_each(resource, resource_list)
> > -		wl_pointer_send_axis(resource, time, axis,
> > -				     value);
> > +	wl_resource_for_each(resource, resource_list) {
> > +		if (value)
> > +			wl_pointer_send_axis(resource, time, axis, value);
> > +		else if (wl_resource_get_version(resource) >=
> > +			 WL_POINTER_AXIS_STOP_SINCE_VERSION)
> > +			wl_pointer_send_axis_stop(resource, time, axis);
> > +	}
> > +}
> > +
> > +WL_EXPORT void
> > +notify_axis_discrete(struct weston_seat *seat, int32_t discrete)
> > +{
> > +	struct weston_compositor *compositor = seat->compositor;
> > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > +	struct wl_resource *resource;
> > +	struct wl_list *resource_list;
> > +
> > +	weston_compositor_wake(compositor);
> > +
> > +	resource_list = &pointer->focus_resource_list;
> > +	wl_resource_for_each(resource, resource_list) {
> > +		if (wl_resource_get_version(resource) >=
> > +		    WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
> > +			wl_pointer_send_axis_discrete(resource, discrete);
> > +		}
> > +	}
> > +}
> > +
> > +WL_EXPORT void
> > +notify_axis_source(struct weston_seat *seat, uint32_t source)
> > +{
> > +	struct weston_compositor *compositor = seat->compositor;
> > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > +	struct wl_resource *resource;
> > +	struct wl_list *resource_list;
> > +
> > +	weston_compositor_wake(compositor);
> > +
> > +	resource_list = &pointer->focus_resource_list;
> > +	wl_resource_for_each(resource, resource_list) {
> > +		if (wl_resource_get_version(resource) >=
> > +		    WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
> > +			wl_pointer_send_axis_source(resource, source);
> > +		}
> > +	}
> > +}
> > +
> > +WL_EXPORT void
> > +notify_axis_frame(struct weston_seat *seat)
> > +{
> > +	struct weston_compositor *compositor = seat->compositor;
> > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > +	struct wl_resource *resource;
> > +	struct wl_list *resource_list;
> > +
> > +	weston_compositor_wake(compositor);
> > +
> > +	resource_list = &pointer->focus_resource_list;
> > +	wl_resource_for_each(resource, resource_list) {
> > +		if (wl_resource_get_version(resource) >=
> > +		    WL_POINTER_AXIS_FRAME_SINCE_VERSION) {
> > +			wl_pointer_send_axis_frame(resource);
> > +		}
> > +	}
> >  }
> >  
> >  WL_EXPORT int
> > @@ -1958,7 +2016,7 @@ bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
> >  	enum wl_seat_capability caps = 0;
> >  
> >  	resource = wl_resource_create(client,
> > -				      &wl_seat_interface, MIN(version, 4), id);
> > +				      &wl_seat_interface, MIN(version, 5), id);
> >  	wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
> >  	wl_resource_set_implementation(resource, &seat_interface, data,
> >  				       unbind_resource);
> > @@ -2361,7 +2419,7 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
> >  	wl_signal_init(&seat->destroy_signal);
> >  	wl_signal_init(&seat->updated_caps_signal);
> >  
> > -	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 4,
> > +	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
> >  					seat, bind_seat);
> >  
> >  	seat->compositor = ec;
> > diff --git a/src/libinput-device.c b/src/libinput-device.c
> > index 2cbfb88..f7f2eef 100644
> > --- a/src/libinput-device.c
> > +++ b/src/libinput-device.c
> > @@ -161,6 +161,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> >  	double value;
> >  
> >  	source = libinput_event_pointer_get_axis_source(pointer_event);
> > +
> >  	/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
> >  	   the value is the angle of the click in degrees. To keep
> >  	   backwards-compat with existing clients, we just send multiples of
> > @@ -180,8 +181,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> >  	default:
> >  		value = 0;
> >  		if (warned < 5) {
> > -			weston_log("Unknown scroll source %d. Event discarded\n",
> > -				   source);
> > +			weston_log("Unknown scroll source %d.\n", source);
> >  			warned++;
> >  		}
> >  		break;
> > @@ -190,6 +190,23 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> >  	return value;
> >  }
> >  
> > +static bool
> > +fetch_axis_discrete(struct libinput_event_pointer *pointer_event,
> > +		    enum libinput_pointer_axis axis,
> > +		    int32_t *discrete)
> > +{
> > +	enum libinput_pointer_axis_source source;
> > +
> > +	source = libinput_event_pointer_get_axis_source(pointer_event);
> > +
> > +	if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
> > +		return false;
> > +
> > +	*discrete = libinput_event_pointer_get_axis_value_discrete(
> > +						   pointer_event, axis);
> > +	return true;
> > +}
> > +
> >  static void
> >  handle_pointer_axis(struct libinput_device *libinput_device,
> >  		    struct libinput_event_pointer *pointer_event)
> > @@ -197,10 +214,46 @@ handle_pointer_axis(struct libinput_device *libinput_device,
> >  	struct evdev_device *device =
> >  		libinput_device_get_user_data(libinput_device);
> >  	double value;
> > +	int32_t value_discrete;
> >  	enum libinput_pointer_axis axis;
> > +	enum libinput_pointer_axis_source source;
> > +	uint32_t wl_axis_source;
> > +	bool need_axis_source_event = true;
> > +	bool has_vert, has_horiz;
> > +
> > +	has_vert = libinput_event_pointer_has_axis(pointer_event,
> > +				   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
> > +	has_horiz = libinput_event_pointer_has_axis(pointer_event,
> > +				   LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
> > +
> > +	if (!has_vert && !has_horiz)
> > +		return;
> > +
> > +	source = libinput_event_pointer_get_axis_source(pointer_event);
> > +	switch (source) {
> > +	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
> > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
> > +		break;
> > +	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
> > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
> > +		break;
> > +	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
> > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
> > +		break;
> > +	default:
> > +		need_axis_source_event = false;
> > +		break;
> > +	}
> > +
> > +	if (need_axis_source_event)
> > +		notify_axis_source(device->seat, wl_axis_source);
> >  
> >  	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
> >  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> > +		if (fetch_axis_discrete(pointer_event, axis,
> > +					&value_discrete))
> > +		    notify_axis_discrete(device->seat, value_discrete);
> > +
> >  		value = normalize_scroll(pointer_event, axis);
> >  		notify_axis(device->seat,
> >  			    libinput_event_pointer_get_time(pointer_event),
> > @@ -210,12 +263,18 @@ handle_pointer_axis(struct libinput_device *libinput_device,
> >  
> >  	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
> >  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> > +		if (fetch_axis_discrete(pointer_event, axis,
> > +					&value_discrete))
> > +		    notify_axis_discrete(device->seat, value_discrete);
> > +
> >  		value = normalize_scroll(pointer_event, axis);
> >  		notify_axis(device->seat,
> >  			    libinput_event_pointer_get_time(pointer_event),
> >  			    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> >  			    wl_fixed_from_double(value));
> >  	}
> > +
> > +	notify_axis_frame(device->seat);
> >  }
> >  
> >  static void
> > -- 
> > 2.4.3
On Tue, Oct 20, 2015 at 09:19:59AM +0800, Jonas Ådahl wrote:
> On Mon, Oct 19, 2015 at 06:17:31PM -0700, Bryce Harrington wrote:
> > On Fri, Oct 16, 2015 at 12:39:22PM +1000, Peter Hutterer wrote:
> > > Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> > > ---
> > > The client-side is the simplest implementation here, and I went the easy
> > > route since most clients won't care to register a multitude of handlers for
> > > axis events.
> > > 
> > > The eventdemo client merely prints the events, it doesn't accumulate them as
> > > they should be. That'd be the job of a slightly more sophisticated toolkit.
> > > 
> > > Possibly contentious: there's no notify_axis_stop(), it's folded into
> > > notify_axis(). Easy fix if needed.
> > > 
> > > Also, I recommend not merging this one until we have a test implementation
> > > in mutter + GTK to make sure the protocol is sane enough to use from complex
> > > clients.
> > 
> > Thanks for resending these patches.  Since the main goal here is
> > assuring the protocol is right, should we also hold off on landing the
> > wayland piece until there's consensus around this one too, or is the
> > protocol part ok to land?
> 
> I'm planning to do a second review of the protocol patches, and there
> will at least be minor changes needed, so please don't merge anything
> yet. Will try to get that done as soon as possible.

Thanks, sounds good.
Bryce

> Jonas
> 
> > 
> > Bryce
> > 
> > >  clients/eventdemo.c      | 55 ++++++++++++++++++++++++++++-
> > >  clients/window.c         | 91 +++++++++++++++++++++++++++++++++++++++++++++++-
> > >  clients/window.h         | 28 +++++++++++++++
> > >  src/compositor-wayland.c | 39 +++++++++++++++++++++
> > >  src/compositor-x11.c     | 16 ++++++---
> > >  src/compositor.h         | 10 ++++++
> > >  src/input.c              | 74 ++++++++++++++++++++++++++++++++++-----
> > >  src/libinput-device.c    | 63 +++++++++++++++++++++++++++++++--
> > >  8 files changed, 360 insertions(+), 16 deletions(-)
> > > 
> > > diff --git a/clients/eventdemo.c b/clients/eventdemo.c
> > > index bdad6fd..f520233 100644
> > > --- a/clients/eventdemo.c
> > > +++ b/clients/eventdemo.c
> > > @@ -259,6 +259,54 @@ axis_handler(struct widget *widget, struct input *input, uint32_t time,
> > >  	       wl_fixed_to_double(value));
> > >  }
> > >  
> > > +static void
> > > +axis_frame_handler(struct widget *widget, struct input *input, void *data)
> > > +{
> > > +	printf("axis frame\n");
> > > +}
> > > +
> > > +static void
> > > +axis_source_handler(struct widget *widget, struct input *input,
> > > +		    uint32_t source, void *data)
> > > +{
> > > +	const char *axis_source;
> > > +
> > > +	switch (source) {
> > > +	case WL_POINTER_AXIS_SOURCE_WHEEL:
> > > +		axis_source = "wheel";
> > > +		break;
> > > +	case WL_POINTER_AXIS_SOURCE_FINGER:
> > > +		axis_source = "finger";
> > > +		break;
> > > +	case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
> > > +		axis_source = "continuous";
> > > +		break;
> > > +	default:
> > > +		axis_source = "<invalid source value>";
> > > +		break;
> > > +	}
> > > +
> > > +	printf("axis source: %s\n", axis_source);
> > > +}
> > > +
> > > +static void
> > > +axis_stop_handler(struct widget *widget, struct input *input,
> > > +		  uint32_t time, uint32_t axis,
> > > +		  void *data)
> > > +{
> > > +	printf("axis stop time: %d, axis: %s\n",
> > > +	       time,
> > > +	       axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
> > > +							 "horizontal");
> > > +}
> > > +
> > > +static void
> > > +axis_discrete_handler(struct widget *widget, struct input *input,
> > > +		      int32_t discrete, void *data)
> > > +{
> > > +	printf("axis discrete value: %d\n", discrete);
> > > +}
> > > +
> > >  /**
> > >   * \brief CALLBACK function, Waylands informs about pointer motion
> > >   * \param widget widget
> > > @@ -348,7 +396,12 @@ eventdemo_create(struct display *d)
> > >  	widget_set_motion_handler(e->widget, motion_handler);
> > >  
> > >  	/* Set the callback axis handler for the window */
> > > -	widget_set_axis_handler(e->widget, axis_handler);
> > > +	widget_set_axis_handlers(e->widget,
> > > +				 axis_handler,
> > > +				 axis_frame_handler,
> > > +				 axis_source_handler,
> > > +				 axis_stop_handler,
> > > +				 axis_discrete_handler);
> > >  
> > >  	/* Initial drawing of the window */
> > >  	window_schedule_resize(e->window, width, height);
> > > diff --git a/clients/window.c b/clients/window.c
> > > index 6d3e944..121037b 100644
> > > --- a/clients/window.c
> > > +++ b/clients/window.c
> > > @@ -288,6 +288,10 @@ struct widget {
> > >  	widget_touch_frame_handler_t touch_frame_handler;
> > >  	widget_touch_cancel_handler_t touch_cancel_handler;
> > >  	widget_axis_handler_t axis_handler;
> > > +	widget_axis_frame_handler_t axis_frame_handler;
> > > +	widget_axis_source_handler_t axis_source_handler;
> > > +	widget_axis_stop_handler_t axis_stop_handler;
> > > +	widget_axis_discrete_handler_t axis_discrete_handler;
> > >  	void *user_data;
> > >  	int opaque;
> > >  	int tooltip_count;
> > > @@ -1935,6 +1939,21 @@ widget_set_axis_handler(struct widget *widget,
> > >  	widget->axis_handler = handler;
> > >  }
> > >  
> > > +void
> > > +widget_set_axis_handlers(struct widget *widget,
> > > +			widget_axis_handler_t axis_handler,
> > > +			widget_axis_frame_handler_t axis_frame_handler,
> > > +			widget_axis_source_handler_t axis_source_handler,
> > > +			widget_axis_stop_handler_t axis_stop_handler,
> > > +			widget_axis_discrete_handler_t axis_discrete_handler)
> > > +{
> > > +	widget->axis_handler = axis_handler;
> > > +	widget->axis_frame_handler = axis_frame_handler;
> > > +	widget->axis_source_handler = axis_source_handler;
> > > +	widget->axis_stop_handler = axis_stop_handler;
> > > +	widget->axis_discrete_handler = axis_discrete_handler;
> > > +}
> > > +
> > >  static void
> > >  window_schedule_redraw_task(struct window *window);
> > >  
> > > @@ -2816,12 +2835,82 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
> > >  					widget->user_data);
> > >  }
> > >  
> > > +static void
> > > +pointer_handle_axis_frame(void *data, struct wl_pointer *pointer)
> > > +{
> > > +	struct input *input = data;
> > > +	struct widget *widget;
> > > +
> > > +	widget = input->focus_widget;
> > > +	if (input->grab)
> > > +		widget = input->grab;
> > > +	if (widget && widget->axis_frame_handler)
> > > +		(*widget->axis_frame_handler)(widget,
> > > +					      input,
> > > +					      widget->user_data);
> > > +}
> > > +
> > > +static void
> > > +pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
> > > +			   uint32_t source)
> > > +{
> > > +	struct input *input = data;
> > > +	struct widget *widget;
> > > +
> > > +	widget = input->focus_widget;
> > > +	if (input->grab)
> > > +		widget = input->grab;
> > > +	if (widget && widget->axis_source_handler)
> > > +		(*widget->axis_source_handler)(widget,
> > > +					       input,
> > > +					       source,
> > > +					       widget->user_data);
> > > +}
> > > +
> > > +static void
> > > +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
> > > +			 uint32_t time, uint32_t axis)
> > > +{
> > > +	struct input *input = data;
> > > +	struct widget *widget;
> > > +
> > > +	widget = input->focus_widget;
> > > +	if (input->grab)
> > > +		widget = input->grab;
> > > +	if (widget && widget->axis_stop_handler)
> > > +		(*widget->axis_stop_handler)(widget,
> > > +					     input, time,
> > > +					     axis,
> > > +					     widget->user_data);
> > > +}
> > > +
> > > +static void
> > > +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> > > +			     int32_t discrete)
> > > +{
> > > +	struct input *input = data;
> > > +	struct widget *widget;
> > > +
> > > +	widget = input->focus_widget;
> > > +	if (input->grab)
> > > +		widget = input->grab;
> > > +	if (widget && widget->axis_discrete_handler)
> > > +		(*widget->axis_discrete_handler)(widget,
> > > +						 input,
> > > +						 discrete,
> > > +						 widget->user_data);
> > > +}
> > > +
> > >  static const struct wl_pointer_listener pointer_listener = {
> > >  	pointer_handle_enter,
> > >  	pointer_handle_leave,
> > >  	pointer_handle_motion,
> > >  	pointer_handle_button,
> > >  	pointer_handle_axis,
> > > +	pointer_handle_axis_frame,
> > > +	pointer_handle_axis_source,
> > > +	pointer_handle_axis_stop,
> > > +	pointer_handle_axis_discrete,
> > >  };
> > >  
> > >  static void
> > > @@ -5225,7 +5314,7 @@ display_add_input(struct display *d, uint32_t id)
> > >  	input = xzalloc(sizeof *input);
> > >  	input->display = d;
> > >  	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
> > > -				       MIN(d->seat_version, 4));
> > > +				       MIN(d->seat_version, 5));
> > >  	input->touch_focus = NULL;
> > >  	input->pointer_focus = NULL;
> > >  	input->keyboard_focus = NULL;
> > > diff --git a/clients/window.h b/clients/window.h
> > > index b61a62a..c52ed6b 100644
> > > --- a/clients/window.h
> > > +++ b/clients/window.h
> > > @@ -267,6 +267,26 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
> > >  				      wl_fixed_t value,
> > >  				      void *data);
> > >  
> > > +typedef void (*widget_axis_frame_handler_t)(struct widget *widget,
> > > +					    struct input *input,
> > > +					    void *data);
> > > +
> > > +typedef void (*widget_axis_source_handler_t)(struct widget *widget,
> > > +					     struct input *input,
> > > +					     uint32_t source,
> > > +					     void *data);
> > > +
> > > +typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
> > > +					   struct input *input,
> > > +					   uint32_t time,
> > > +					   uint32_t axis,
> > > +					   void *data);
> > > +
> > > +typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
> > > +					       struct input *input,
> > > +					       int32_t discrete,
> > > +					       void *data);
> > > +
> > >  struct window *
> > >  window_create(struct display *display);
> > >  struct window *
> > > @@ -520,6 +540,14 @@ void
> > >  widget_set_axis_handler(struct widget *widget,
> > >  			widget_axis_handler_t handler);
> > >  void
> > > +widget_set_axis_handlers(struct widget *widget,
> > > +			widget_axis_handler_t axis_handler,
> > > +			widget_axis_frame_handler_t axis_frame_handler,
> > > +			widget_axis_source_handler_t axis_source_handler,
> > > +			widget_axis_stop_handler_t axis_stop_handler,
> > > +			widget_axis_discrete_handler_t axis_discrete_handler);
> > > +
> > > +void
> > >  widget_schedule_redraw(struct widget *widget);
> > >  void
> > >  widget_set_use_cairo(struct widget *widget, int use_cairo);
> > > diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
> > > index 7b11ae4..d3bbcf8 100644
> > > --- a/src/compositor-wayland.c
> > > +++ b/src/compositor-wayland.c
> > > @@ -1429,12 +1429,51 @@ input_handle_axis(void *data, struct wl_pointer *pointer,
> > >  	notify_axis(&input->base, time, axis, value);
> > >  }
> > >  
> > > +static void
> > > +input_handle_axis_frame(void *data, struct wl_pointer *pointer)
> > > +{
> > > +	struct wayland_input *input = data;
> > > +
> > > +	notify_axis_frame(&input->base);
> > > +}
> > > +
> > > +static void
> > > +input_handle_axis_source(void *data, struct wl_pointer *pointer,
> > > +			 uint32_t source)
> > > +{
> > > +	struct wayland_input *input = data;
> > > +
> > > +	notify_axis_source(&input->base, source);
> > > +}
> > > +
> > > +static void
> > > +input_handle_axis_stop(void *data, struct wl_pointer *pointer,
> > > +		       uint32_t time, uint32_t axis)
> > > +{
> > > +	struct wayland_input *input = data;
> > > +
> > > +	notify_axis(&input->base, time, axis, 0);
> > > +}
> > > +
> > > +static void
> > > +input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> > > +			   int32_t discrete)
> > > +{
> > > +	struct wayland_input *input = data;
> > > +
> > > +	notify_axis_discrete(&input->base, discrete);
> > > +}
> > > +
> > >  static const struct wl_pointer_listener pointer_listener = {
> > >  	input_handle_pointer_enter,
> > >  	input_handle_pointer_leave,
> > >  	input_handle_motion,
> > >  	input_handle_button,
> > >  	input_handle_axis,
> > > +	input_handle_axis_frame,
> > > +	input_handle_axis_source,
> > > +	input_handle_axis_stop,
> > > +	input_handle_axis_discrete,
> > >  };
> > >  
> > >  static void
> > > diff --git a/src/compositor-x11.c b/src/compositor-x11.c
> > > index 9a23996..4b5499d 100644
> > > --- a/src/compositor-x11.c
> > > +++ b/src/compositor-x11.c
> > > @@ -1082,32 +1082,40 @@ x11_backend_deliver_button_event(struct x11_backend *b,
> > >  	case 4:
> > >  		/* Axis are measured in pixels, but the xcb events are discrete
> > >  		 * steps. Therefore move the axis by some pixels every step. */
> > > -		if (state)
> > > +		if (state) {
> > > +			notify_axis_discrete(&b->core_seat, -1);
> > >  			notify_axis(&b->core_seat,
> > >  				    weston_compositor_get_time(),
> > >  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
> > >  				    -DEFAULT_AXIS_STEP_DISTANCE);
> > > +		}
> > >  		return;
> > >  	case 5:
> > > -		if (state)
> > > +		if (state) {
> > > +			notify_axis_discrete(&b->core_seat, 1);
> > >  			notify_axis(&b->core_seat,
> > >  				    weston_compositor_get_time(),
> > >  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
> > >  				    DEFAULT_AXIS_STEP_DISTANCE);
> > > +		}
> > >  		return;
> > >  	case 6:
> > > -		if (state)
> > > +		if (state) {
> > > +			notify_axis_discrete(&b->core_seat, -1);
> > >  			notify_axis(&b->core_seat,
> > >  				    weston_compositor_get_time(),
> > >  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> > >  				    -DEFAULT_AXIS_STEP_DISTANCE);
> > > +		}
> > >  		return;
> > >  	case 7:
> > > -		if (state)
> > > +		if (state) {
> > > +			notify_axis_discrete(&b->core_seat, 1);
> > >  			notify_axis(&b->core_seat,
> > >  				    weston_compositor_get_time(),
> > >  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> > >  				    DEFAULT_AXIS_STEP_DISTANCE);
> > > +		}
> > >  		return;
> > >  	default:
> > >  		button = button_event->detail + BTN_SIDE - 8;
> > > diff --git a/src/compositor.h b/src/compositor.h
> > > index 2e2a185..1e518e1 100644
> > > --- a/src/compositor.h
> > > +++ b/src/compositor.h
> > > @@ -1075,6 +1075,16 @@ notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
> > >  void
> > >  notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
> > >  	    wl_fixed_t value);
> > > +
> > > +void
> > > +notify_axis_discrete(struct weston_seat *seat, int32_t discrete);
> > > +
> > > +void
> > > +notify_axis_source(struct weston_seat *seat, uint32_t source);
> > > +
> > > +void
> > > +notify_axis_frame(struct weston_seat *seat);
> > > +
> > >  void
> > >  notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
> > >  	   enum wl_keyboard_key_state state,
> > > diff --git a/src/input.c b/src/input.c
> > > index 500c39a..4d7ac2b 100644
> > > --- a/src/input.c
> > > +++ b/src/input.c
> > > @@ -1089,17 +1089,75 @@ notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
> > >  
> > >  	weston_compositor_wake(compositor);
> > >  
> > > -	if (!value)
> > > -		return;
> > > -
> > >  	if (weston_compositor_run_axis_binding(compositor, pointer,
> > >  					       time, axis, value))
> > >  		return;
> > >  
> > >  	resource_list = &pointer->focus_resource_list;
> > > -	wl_resource_for_each(resource, resource_list)
> > > -		wl_pointer_send_axis(resource, time, axis,
> > > -				     value);
> > > +	wl_resource_for_each(resource, resource_list) {
> > > +		if (value)
> > > +			wl_pointer_send_axis(resource, time, axis, value);
> > > +		else if (wl_resource_get_version(resource) >=
> > > +			 WL_POINTER_AXIS_STOP_SINCE_VERSION)
> > > +			wl_pointer_send_axis_stop(resource, time, axis);
> > > +	}
> > > +}
> > > +
> > > +WL_EXPORT void
> > > +notify_axis_discrete(struct weston_seat *seat, int32_t discrete)
> > > +{
> > > +	struct weston_compositor *compositor = seat->compositor;
> > > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > > +	struct wl_resource *resource;
> > > +	struct wl_list *resource_list;
> > > +
> > > +	weston_compositor_wake(compositor);
> > > +
> > > +	resource_list = &pointer->focus_resource_list;
> > > +	wl_resource_for_each(resource, resource_list) {
> > > +		if (wl_resource_get_version(resource) >=
> > > +		    WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
> > > +			wl_pointer_send_axis_discrete(resource, discrete);
> > > +		}
> > > +	}
> > > +}
> > > +
> > > +WL_EXPORT void
> > > +notify_axis_source(struct weston_seat *seat, uint32_t source)
> > > +{
> > > +	struct weston_compositor *compositor = seat->compositor;
> > > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > > +	struct wl_resource *resource;
> > > +	struct wl_list *resource_list;
> > > +
> > > +	weston_compositor_wake(compositor);
> > > +
> > > +	resource_list = &pointer->focus_resource_list;
> > > +	wl_resource_for_each(resource, resource_list) {
> > > +		if (wl_resource_get_version(resource) >=
> > > +		    WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
> > > +			wl_pointer_send_axis_source(resource, source);
> > > +		}
> > > +	}
> > > +}
> > > +
> > > +WL_EXPORT void
> > > +notify_axis_frame(struct weston_seat *seat)
> > > +{
> > > +	struct weston_compositor *compositor = seat->compositor;
> > > +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> > > +	struct wl_resource *resource;
> > > +	struct wl_list *resource_list;
> > > +
> > > +	weston_compositor_wake(compositor);
> > > +
> > > +	resource_list = &pointer->focus_resource_list;
> > > +	wl_resource_for_each(resource, resource_list) {
> > > +		if (wl_resource_get_version(resource) >=
> > > +		    WL_POINTER_AXIS_FRAME_SINCE_VERSION) {
> > > +			wl_pointer_send_axis_frame(resource);
> > > +		}
> > > +	}
> > >  }
> > >  
> > >  WL_EXPORT int
> > > @@ -1958,7 +2016,7 @@ bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
> > >  	enum wl_seat_capability caps = 0;
> > >  
> > >  	resource = wl_resource_create(client,
> > > -				      &wl_seat_interface, MIN(version, 4), id);
> > > +				      &wl_seat_interface, MIN(version, 5), id);
> > >  	wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
> > >  	wl_resource_set_implementation(resource, &seat_interface, data,
> > >  				       unbind_resource);
> > > @@ -2361,7 +2419,7 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
> > >  	wl_signal_init(&seat->destroy_signal);
> > >  	wl_signal_init(&seat->updated_caps_signal);
> > >  
> > > -	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 4,
> > > +	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
> > >  					seat, bind_seat);
> > >  
> > >  	seat->compositor = ec;
> > > diff --git a/src/libinput-device.c b/src/libinput-device.c
> > > index 2cbfb88..f7f2eef 100644
> > > --- a/src/libinput-device.c
> > > +++ b/src/libinput-device.c
> > > @@ -161,6 +161,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> > >  	double value;
> > >  
> > >  	source = libinput_event_pointer_get_axis_source(pointer_event);
> > > +
> > >  	/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
> > >  	   the value is the angle of the click in degrees. To keep
> > >  	   backwards-compat with existing clients, we just send multiples of
> > > @@ -180,8 +181,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> > >  	default:
> > >  		value = 0;
> > >  		if (warned < 5) {
> > > -			weston_log("Unknown scroll source %d. Event discarded\n",
> > > -				   source);
> > > +			weston_log("Unknown scroll source %d.\n", source);
> > >  			warned++;
> > >  		}
> > >  		break;
> > > @@ -190,6 +190,23 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
> > >  	return value;
> > >  }
> > >  
> > > +static bool
> > > +fetch_axis_discrete(struct libinput_event_pointer *pointer_event,
> > > +		    enum libinput_pointer_axis axis,
> > > +		    int32_t *discrete)
> > > +{
> > > +	enum libinput_pointer_axis_source source;
> > > +
> > > +	source = libinput_event_pointer_get_axis_source(pointer_event);
> > > +
> > > +	if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
> > > +		return false;
> > > +
> > > +	*discrete = libinput_event_pointer_get_axis_value_discrete(
> > > +						   pointer_event, axis);
> > > +	return true;
> > > +}
> > > +
> > >  static void
> > >  handle_pointer_axis(struct libinput_device *libinput_device,
> > >  		    struct libinput_event_pointer *pointer_event)
> > > @@ -197,10 +214,46 @@ handle_pointer_axis(struct libinput_device *libinput_device,
> > >  	struct evdev_device *device =
> > >  		libinput_device_get_user_data(libinput_device);
> > >  	double value;
> > > +	int32_t value_discrete;
> > >  	enum libinput_pointer_axis axis;
> > > +	enum libinput_pointer_axis_source source;
> > > +	uint32_t wl_axis_source;
> > > +	bool need_axis_source_event = true;
> > > +	bool has_vert, has_horiz;
> > > +
> > > +	has_vert = libinput_event_pointer_has_axis(pointer_event,
> > > +				   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
> > > +	has_horiz = libinput_event_pointer_has_axis(pointer_event,
> > > +				   LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
> > > +
> > > +	if (!has_vert && !has_horiz)
> > > +		return;
> > > +
> > > +	source = libinput_event_pointer_get_axis_source(pointer_event);
> > > +	switch (source) {
> > > +	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
> > > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
> > > +		break;
> > > +	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
> > > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
> > > +		break;
> > > +	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
> > > +		wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
> > > +		break;
> > > +	default:
> > > +		need_axis_source_event = false;
> > > +		break;
> > > +	}
> > > +
> > > +	if (need_axis_source_event)
> > > +		notify_axis_source(device->seat, wl_axis_source);
> > >  
> > >  	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
> > >  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> > > +		if (fetch_axis_discrete(pointer_event, axis,
> > > +					&value_discrete))
> > > +		    notify_axis_discrete(device->seat, value_discrete);
> > > +
> > >  		value = normalize_scroll(pointer_event, axis);
> > >  		notify_axis(device->seat,
> > >  			    libinput_event_pointer_get_time(pointer_event),
> > > @@ -210,12 +263,18 @@ handle_pointer_axis(struct libinput_device *libinput_device,
> > >  
> > >  	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
> > >  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> > > +		if (fetch_axis_discrete(pointer_event, axis,
> > > +					&value_discrete))
> > > +		    notify_axis_discrete(device->seat, value_discrete);
> > > +
> > >  		value = normalize_scroll(pointer_event, axis);
> > >  		notify_axis(device->seat,
> > >  			    libinput_event_pointer_get_time(pointer_event),
> > >  			    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
> > >  			    wl_fixed_from_double(value));
> > >  	}
> > > +
> > > +	notify_axis_frame(device->seat);
> > >  }
> > >  
> > >  static void
> > > -- 
> > > 2.4.3
On 15/10/15 09:39 PM, Peter Hutterer wrote:
> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
> ---
> The client-side is the simplest implementation here, and I went the easy
> route since most clients won't care to register a multitude of handlers for
> axis events.
> 
> The eventdemo client merely prints the events, it doesn't accumulate them as
> they should be. That'd be the job of a slightly more sophisticated toolkit.
> 
> Possibly contentious: there's no notify_axis_stop(), it's folded into
> notify_axis(). Easy fix if needed.
> 
> Also, I recommend not merging this one until we have a test implementation
> in mutter + GTK to make sure the protocol is sane enough to use from complex
> clients.
> 
>  clients/eventdemo.c      | 55 ++++++++++++++++++++++++++++-
>  clients/window.c         | 91 +++++++++++++++++++++++++++++++++++++++++++++++-
>  clients/window.h         | 28 +++++++++++++++
>  src/compositor-wayland.c | 39 +++++++++++++++++++++
>  src/compositor-x11.c     | 16 ++++++---
>  src/compositor.h         | 10 ++++++
>  src/input.c              | 74 ++++++++++++++++++++++++++++++++++-----
>  src/libinput-device.c    | 63 +++++++++++++++++++++++++++++++--
>  8 files changed, 360 insertions(+), 16 deletions(-)
> 
> diff --git a/clients/eventdemo.c b/clients/eventdemo.c
> index bdad6fd..f520233 100644
> --- a/clients/eventdemo.c
> +++ b/clients/eventdemo.c
> @@ -259,6 +259,54 @@ axis_handler(struct widget *widget, struct input *input, uint32_t time,
>  	       wl_fixed_to_double(value));
>  }
>  
> +static void
> +axis_frame_handler(struct widget *widget, struct input *input, void *data)
> +{
> +	printf("axis frame\n");
> +}
> +
> +static void
> +axis_source_handler(struct widget *widget, struct input *input,
> +		    uint32_t source, void *data)
> +{
> +	const char *axis_source;
> +
> +	switch (source) {
> +	case WL_POINTER_AXIS_SOURCE_WHEEL:
> +		axis_source = "wheel";
> +		break;
> +	case WL_POINTER_AXIS_SOURCE_FINGER:
> +		axis_source = "finger";
> +		break;
> +	case WL_POINTER_AXIS_SOURCE_CONTINUOUS:
> +		axis_source = "continuous";
> +		break;
> +	default:
> +		axis_source = "<invalid source value>";
> +		break;
> +	}
> +
> +	printf("axis source: %s\n", axis_source);
> +}
> +
> +static void
> +axis_stop_handler(struct widget *widget, struct input *input,
> +		  uint32_t time, uint32_t axis,
> +		  void *data)
> +{
> +	printf("axis stop time: %d, axis: %s\n",
> +	       time,
> +	       axis == WL_POINTER_AXIS_VERTICAL_SCROLL ? "vertical" :
> +							 "horizontal");
> +}
> +
> +static void
> +axis_discrete_handler(struct widget *widget, struct input *input,
> +		      int32_t discrete, void *data)
> +{
> +	printf("axis discrete value: %d\n", discrete);
> +}
> +
>  /**
>   * \brief CALLBACK function, Waylands informs about pointer motion
>   * \param widget widget
> @@ -348,7 +396,12 @@ eventdemo_create(struct display *d)
>  	widget_set_motion_handler(e->widget, motion_handler);
>  
>  	/* Set the callback axis handler for the window */
> -	widget_set_axis_handler(e->widget, axis_handler);
> +	widget_set_axis_handlers(e->widget,
> +				 axis_handler,
> +				 axis_frame_handler,
> +				 axis_source_handler,
> +				 axis_stop_handler,
> +				 axis_discrete_handler);
>  
>  	/* Initial drawing of the window */
>  	window_schedule_resize(e->window, width, height);
> diff --git a/clients/window.c b/clients/window.c
> index 6d3e944..121037b 100644
> --- a/clients/window.c
> +++ b/clients/window.c
> @@ -288,6 +288,10 @@ struct widget {
>  	widget_touch_frame_handler_t touch_frame_handler;
>  	widget_touch_cancel_handler_t touch_cancel_handler;
>  	widget_axis_handler_t axis_handler;
> +	widget_axis_frame_handler_t axis_frame_handler;
> +	widget_axis_source_handler_t axis_source_handler;
> +	widget_axis_stop_handler_t axis_stop_handler;
> +	widget_axis_discrete_handler_t axis_discrete_handler;
>  	void *user_data;
>  	int opaque;
>  	int tooltip_count;
> @@ -1935,6 +1939,21 @@ widget_set_axis_handler(struct widget *widget,
>  	widget->axis_handler = handler;
>  }
>  
> +void
> +widget_set_axis_handlers(struct widget *widget,
> +			widget_axis_handler_t axis_handler,
> +			widget_axis_frame_handler_t axis_frame_handler,
> +			widget_axis_source_handler_t axis_source_handler,
> +			widget_axis_stop_handler_t axis_stop_handler,
> +			widget_axis_discrete_handler_t axis_discrete_handler)
> +{
> +	widget->axis_handler = axis_handler;
> +	widget->axis_frame_handler = axis_frame_handler;
> +	widget->axis_source_handler = axis_source_handler;
> +	widget->axis_stop_handler = axis_stop_handler;
> +	widget->axis_discrete_handler = axis_discrete_handler;
> +}
> +
>  static void
>  window_schedule_redraw_task(struct window *window);
>  
> @@ -2816,12 +2835,82 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
>  					widget->user_data);
>  }
>  
> +static void
> +pointer_handle_axis_frame(void *data, struct wl_pointer *pointer)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_frame_handler)
> +		(*widget->axis_frame_handler)(widget,
> +					      input,
> +					      widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
> +			   uint32_t source)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_source_handler)
> +		(*widget->axis_source_handler)(widget,
> +					       input,
> +					       source,
> +					       widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
> +			 uint32_t time, uint32_t axis)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_stop_handler)
> +		(*widget->axis_stop_handler)(widget,
> +					     input, time,
> +					     axis,
> +					     widget->user_data);
> +}
> +
> +static void
> +pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> +			     int32_t discrete)
> +{
> +	struct input *input = data;
> +	struct widget *widget;
> +
> +	widget = input->focus_widget;
> +	if (input->grab)
> +		widget = input->grab;
> +	if (widget && widget->axis_discrete_handler)
> +		(*widget->axis_discrete_handler)(widget,
> +						 input,
> +						 discrete,
> +						 widget->user_data);
> +}
> +
>  static const struct wl_pointer_listener pointer_listener = {
>  	pointer_handle_enter,
>  	pointer_handle_leave,
>  	pointer_handle_motion,
>  	pointer_handle_button,
>  	pointer_handle_axis,
> +	pointer_handle_axis_frame,
> +	pointer_handle_axis_source,
> +	pointer_handle_axis_stop,
> +	pointer_handle_axis_discrete,
>  };
>  
>  static void
> @@ -5225,7 +5314,7 @@ display_add_input(struct display *d, uint32_t id)
>  	input = xzalloc(sizeof *input);
>  	input->display = d;
>  	input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface,
> -				       MIN(d->seat_version, 4));
> +				       MIN(d->seat_version, 5));
>  	input->touch_focus = NULL;
>  	input->pointer_focus = NULL;
>  	input->keyboard_focus = NULL;
> diff --git a/clients/window.h b/clients/window.h
> index b61a62a..c52ed6b 100644
> --- a/clients/window.h
> +++ b/clients/window.h
> @@ -267,6 +267,26 @@ typedef void (*widget_axis_handler_t)(struct widget *widget,
>  				      wl_fixed_t value,
>  				      void *data);
>  
> +typedef void (*widget_axis_frame_handler_t)(struct widget *widget,
> +					    struct input *input,
> +					    void *data);
> +
> +typedef void (*widget_axis_source_handler_t)(struct widget *widget,
> +					     struct input *input,
> +					     uint32_t source,
> +					     void *data);
> +
> +typedef void (*widget_axis_stop_handler_t)(struct widget *widget,
> +					   struct input *input,
> +					   uint32_t time,
> +					   uint32_t axis,
> +					   void *data);
> +
> +typedef void (*widget_axis_discrete_handler_t)(struct widget *widget,
> +					       struct input *input,
> +					       int32_t discrete,
> +					       void *data);
> +
>  struct window *
>  window_create(struct display *display);
>  struct window *
> @@ -520,6 +540,14 @@ void
>  widget_set_axis_handler(struct widget *widget,
>  			widget_axis_handler_t handler);
>  void
> +widget_set_axis_handlers(struct widget *widget,
> +			widget_axis_handler_t axis_handler,
> +			widget_axis_frame_handler_t axis_frame_handler,
> +			widget_axis_source_handler_t axis_source_handler,
> +			widget_axis_stop_handler_t axis_stop_handler,
> +			widget_axis_discrete_handler_t axis_discrete_handler);
> +
> +void
>  widget_schedule_redraw(struct widget *widget);
>  void
>  widget_set_use_cairo(struct widget *widget, int use_cairo);
> diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c
> index 7b11ae4..d3bbcf8 100644
> --- a/src/compositor-wayland.c
> +++ b/src/compositor-wayland.c
> @@ -1429,12 +1429,51 @@ input_handle_axis(void *data, struct wl_pointer *pointer,
>  	notify_axis(&input->base, time, axis, value);
>  }
>  
> +static void
> +input_handle_axis_frame(void *data, struct wl_pointer *pointer)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_frame(&input->base);
> +}
> +
> +static void
> +input_handle_axis_source(void *data, struct wl_pointer *pointer,
> +			 uint32_t source)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_source(&input->base, source);
> +}
> +
> +static void
> +input_handle_axis_stop(void *data, struct wl_pointer *pointer,
> +		       uint32_t time, uint32_t axis)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis(&input->base, time, axis, 0);
> +}
> +
> +static void
> +input_handle_axis_discrete(void *data, struct wl_pointer *pointer,
> +			   int32_t discrete)
> +{
> +	struct wayland_input *input = data;
> +
> +	notify_axis_discrete(&input->base, discrete);
> +}
> +
>  static const struct wl_pointer_listener pointer_listener = {
>  	input_handle_pointer_enter,
>  	input_handle_pointer_leave,
>  	input_handle_motion,
>  	input_handle_button,
>  	input_handle_axis,
> +	input_handle_axis_frame,
> +	input_handle_axis_source,
> +	input_handle_axis_stop,
> +	input_handle_axis_discrete,
>  };
>  
>  static void
> diff --git a/src/compositor-x11.c b/src/compositor-x11.c
> index 9a23996..4b5499d 100644
> --- a/src/compositor-x11.c
> +++ b/src/compositor-x11.c
> @@ -1082,32 +1082,40 @@ x11_backend_deliver_button_event(struct x11_backend *b,
>  	case 4:
>  		/* Axis are measured in pixels, but the xcb events are discrete
>  		 * steps. Therefore move the axis by some pixels every step. */
> -		if (state)
> +		if (state) {

I think notify_axis_discrete() needs an update to the latest version of
the proposed protocol spec...

> +			notify_axis_discrete(&b->core_seat, -1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
>  				    -DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 5:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, 1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_VERTICAL_SCROLL,
>  				    DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 6:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, -1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  				    -DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	case 7:
> -		if (state)
> +		if (state) {
> +			notify_axis_discrete(&b->core_seat, 1);
>  			notify_axis(&b->core_seat,
>  				    weston_compositor_get_time(),
>  				    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  				    DEFAULT_AXIS_STEP_DISTANCE);
> +		}
>  		return;
>  	default:
>  		button = button_event->detail + BTN_SIDE - 8;
> diff --git a/src/compositor.h b/src/compositor.h
> index 2e2a185..1e518e1 100644
> --- a/src/compositor.h
> +++ b/src/compositor.h
> @@ -1075,6 +1075,16 @@ notify_button(struct weston_seat *seat, uint32_t time, int32_t button,
>  void
>  notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
>  	    wl_fixed_t value);
> +
> +void
> +notify_axis_discrete(struct weston_seat *seat, int32_t discrete);
> +
> +void
> +notify_axis_source(struct weston_seat *seat, uint32_t source);
> +
> +void
> +notify_axis_frame(struct weston_seat *seat);
> +
>  void
>  notify_key(struct weston_seat *seat, uint32_t time, uint32_t key,
>  	   enum wl_keyboard_key_state state,
> diff --git a/src/input.c b/src/input.c
> index 500c39a..4d7ac2b 100644
> --- a/src/input.c
> +++ b/src/input.c
> @@ -1089,17 +1089,75 @@ notify_axis(struct weston_seat *seat, uint32_t time, uint32_t axis,
>  
>  	weston_compositor_wake(compositor);
>  
> -	if (!value)
> -		return;
> -
>  	if (weston_compositor_run_axis_binding(compositor, pointer,
>  					       time, axis, value))
>  		return;
>  
>  	resource_list = &pointer->focus_resource_list;
> -	wl_resource_for_each(resource, resource_list)
> -		wl_pointer_send_axis(resource, time, axis,
> -				     value);
> +	wl_resource_for_each(resource, resource_list) {
> +		if (value)
> +			wl_pointer_send_axis(resource, time, axis, value);
> +		else if (wl_resource_get_version(resource) >=
> +			 WL_POINTER_AXIS_STOP_SINCE_VERSION)
> +			wl_pointer_send_axis_stop(resource, time, axis);
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_discrete(struct weston_seat *seat, int32_t discrete)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
> +			wl_pointer_send_axis_discrete(resource, discrete);
> +		}
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_source(struct weston_seat *seat, uint32_t source)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
> +			wl_pointer_send_axis_source(resource, source);
> +		}
> +	}
> +}
> +
> +WL_EXPORT void
> +notify_axis_frame(struct weston_seat *seat)
> +{
> +	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_pointer *pointer = weston_seat_get_pointer(seat);
> +	struct wl_resource *resource;
> +	struct wl_list *resource_list;
> +
> +	weston_compositor_wake(compositor);
> +
> +	resource_list = &pointer->focus_resource_list;
> +	wl_resource_for_each(resource, resource_list) {
> +		if (wl_resource_get_version(resource) >=
> +		    WL_POINTER_AXIS_FRAME_SINCE_VERSION) {
> +			wl_pointer_send_axis_frame(resource);
> +		}
> +	}
>  }
>  
>  WL_EXPORT int
> @@ -1958,7 +2016,7 @@ bind_seat(struct wl_client *client, void *data, uint32_t version, uint32_t id)
>  	enum wl_seat_capability caps = 0;
>  
>  	resource = wl_resource_create(client,
> -				      &wl_seat_interface, MIN(version, 4), id);
> +				      &wl_seat_interface, MIN(version, 5), id);
>  	wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));
>  	wl_resource_set_implementation(resource, &seat_interface, data,
>  				       unbind_resource);
> @@ -2361,7 +2419,7 @@ weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
>  	wl_signal_init(&seat->destroy_signal);
>  	wl_signal_init(&seat->updated_caps_signal);
>  
> -	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 4,
> +	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
>  					seat, bind_seat);
>  
>  	seat->compositor = ec;
> diff --git a/src/libinput-device.c b/src/libinput-device.c
> index 2cbfb88..f7f2eef 100644
> --- a/src/libinput-device.c
> +++ b/src/libinput-device.c
> @@ -161,6 +161,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	double value;
>  
>  	source = libinput_event_pointer_get_axis_source(pointer_event);
> +

I guess this hunk's superfluous, but I don't care that much. :)

>  	/* libinput < 0.8 sent wheel click events with value 10. Since 0.8
>  	   the value is the angle of the click in degrees. To keep
>  	   backwards-compat with existing clients, we just send multiples of
> @@ -180,8 +181,7 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	default:
>  		value = 0;
>  		if (warned < 5) {
> -			weston_log("Unknown scroll source %d. Event discarded\n",
> -				   source);
> +			weston_log("Unknown scroll source %d.\n", source);

Do we no longer discard the event?

Derek
>  			warned++;
>  		}
>  		break;
> @@ -190,6 +190,23 @@ normalize_scroll(struct libinput_event_pointer *pointer_event,
>  	return value;
>  }
>  
> +static bool
> +fetch_axis_discrete(struct libinput_event_pointer *pointer_event,
> +		    enum libinput_pointer_axis axis,
> +		    int32_t *discrete)
> +{
> +	enum libinput_pointer_axis_source source;
> +
> +	source = libinput_event_pointer_get_axis_source(pointer_event);
> +
> +	if (source != LIBINPUT_POINTER_AXIS_SOURCE_WHEEL)
> +		return false;
> +
> +	*discrete = libinput_event_pointer_get_axis_value_discrete(
> +						   pointer_event, axis);
> +	return true;
> +}
> +
>  static void
>  handle_pointer_axis(struct libinput_device *libinput_device,
>  		    struct libinput_event_pointer *pointer_event)
> @@ -197,10 +214,46 @@ handle_pointer_axis(struct libinput_device *libinput_device,
>  	struct evdev_device *device =
>  		libinput_device_get_user_data(libinput_device);
>  	double value;
> +	int32_t value_discrete;
>  	enum libinput_pointer_axis axis;
> +	enum libinput_pointer_axis_source source;
> +	uint32_t wl_axis_source;
> +	bool need_axis_source_event = true;
> +	bool has_vert, has_horiz;
> +
> +	has_vert = libinput_event_pointer_has_axis(pointer_event,
> +				   LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
> +	has_horiz = libinput_event_pointer_has_axis(pointer_event,
> +				   LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
> +
> +	if (!has_vert && !has_horiz)
> +		return;
> +
> +	source = libinput_event_pointer_get_axis_source(pointer_event);
> +	switch (source) {
> +	case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
> +		break;
> +	case LIBINPUT_POINTER_AXIS_SOURCE_FINGER:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_FINGER;
> +		break;
> +	case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS:
> +		wl_axis_source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
> +		break;
> +	default:
> +		need_axis_source_event = false;
> +		break;
> +	}
> +
> +	if (need_axis_source_event)
> +		notify_axis_source(device->seat, wl_axis_source);
>  
>  	axis = LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL;
>  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> +		if (fetch_axis_discrete(pointer_event, axis,
> +					&value_discrete))
> +		    notify_axis_discrete(device->seat, value_discrete);
> +
>  		value = normalize_scroll(pointer_event, axis);
>  		notify_axis(device->seat,
>  			    libinput_event_pointer_get_time(pointer_event),
> @@ -210,12 +263,18 @@ handle_pointer_axis(struct libinput_device *libinput_device,
>  
>  	axis = LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL;
>  	if (libinput_event_pointer_has_axis(pointer_event, axis)) {
> +		if (fetch_axis_discrete(pointer_event, axis,
> +					&value_discrete))
> +		    notify_axis_discrete(device->seat, value_discrete);
> +
>  		value = normalize_scroll(pointer_event, axis);
>  		notify_axis(device->seat,
>  			    libinput_event_pointer_get_time(pointer_event),
>  			    WL_POINTER_AXIS_HORIZONTAL_SCROLL,
>  			    wl_fixed_from_double(value));
>  	}
> +
> +	notify_axis_frame(device->seat);
>  }
>  
>  static void
>