wayland: add support for wl_pointer frame/axis_source/axis_discrete/axis_stop

This adds support for the new wl_pointer events available in v5.

The wl_pointer.axis_source events can be ignored for the purposes here, the
main reason they exist is so that the combination of axis_source=finger and
axis_stop triggers kinetic scrolling. We don't need to care about the source,
axis_stop is enough for us to tell us when we're scrolling.

The wl_pointer.frame events group events together and is intended as a
mechanism to coalesce events together. This for example allows us to now
send a single GTK scroll event for a diagonal scroll. Previously, the two
wl_pointer.axis events had to be handled separately.

The wl_pointer.axis_discrete event sends mouse wheel clicks where
appropriate, and is translated into up/down/left/right scroll events.

https://bugzilla.gnome.org/show_bug.cgi?id=756729
This commit is contained in:
Peter Hutterer 2015-10-20 13:21:54 +10:00 committed by Carlos Garnacho
parent ac95bac5b8
commit 3fca36169a
4 changed files with 253 additions and 44 deletions

View File

@ -43,6 +43,7 @@
#define BUTTON_BASE (BTN_LEFT - 1) /* Used to translate to 1-indexed buttons */
typedef struct _GdkWaylandTouchData GdkWaylandTouchData;
typedef struct _GdkWaylandPointerFrameData GdkWaylandPointerFrameData;
struct _GdkWaylandTouchData
{
@ -54,6 +55,15 @@ struct _GdkWaylandTouchData
guint initial_touch : 1;
};
struct _GdkWaylandPointerFrameData
{
GdkEvent *event;
/* Specific to the scroll event */
gdouble delta_x, delta_y;
int32_t discrete_x, discrete_y;
};
struct _GdkWaylandSeat
{
GdkSeat parent_instance;
@ -118,6 +128,9 @@ struct _GdkWaylandSeat
gdouble gesture_scale;
GdkCursor *grab_cursor;
/* Accumulated event data for a pointer frame */
GdkWaylandPointerFrameData pointer_frame;
};
G_DEFINE_TYPE (GdkWaylandSeat, gdk_wayland_seat, GDK_TYPE_SEAT)
@ -918,6 +931,114 @@ static const struct wl_data_device_listener data_device_listener = {
data_device_selection
};
static GdkEvent *
create_scroll_event (GdkWaylandSeat *seat,
gboolean emulated)
{
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (seat->display);
GdkEvent *event;
event = gdk_event_new (GDK_SCROLL);
event->scroll.window = g_object_ref (seat->pointer_focus);
gdk_event_set_device (event, seat->master_pointer);
gdk_event_set_source_device (event, seat->pointer);
event->scroll.time = seat->time;
event->scroll.state = seat->button_modifiers | seat->key_modifiers;
gdk_event_set_screen (event, display->screen);
_gdk_event_set_pointer_emulated (event, emulated);
get_coordinates (seat,
&event->scroll.x,
&event->scroll.y,
&event->scroll.x_root,
&event->scroll.y_root);
return event;
}
static void
flush_discrete_scroll_event (GdkWaylandSeat *seat,
GdkScrollDirection direction)
{
GdkEvent *event;
event = create_scroll_event (seat, TRUE);
event->scroll.direction = direction;
_gdk_wayland_display_deliver_event (seat->display, event);
}
static void
flush_smooth_scroll_event (GdkWaylandSeat *seat,
gdouble delta_x,
gdouble delta_y)
{
GdkEvent *event;
event = create_scroll_event (seat, FALSE);
event->scroll.direction = GDK_SCROLL_SMOOTH;
event->scroll.delta_x = delta_x;
event->scroll.delta_y = delta_y;
_gdk_wayland_display_deliver_event (seat->display, event);
}
static void
flush_scroll_event (GdkWaylandSeat *seat,
GdkWaylandPointerFrameData *pointer_frame)
{
if (pointer_frame->discrete_x || pointer_frame->discrete_y)
{
GdkScrollDirection direction;
if (pointer_frame->discrete_x > 0)
direction = GDK_SCROLL_LEFT;
else if (pointer_frame->discrete_x < 0)
direction = GDK_SCROLL_RIGHT;
else if (pointer_frame->discrete_y > 0)
direction = GDK_SCROLL_UP;
else
direction = GDK_SCROLL_DOWN;
flush_discrete_scroll_event (seat, direction);
}
flush_smooth_scroll_event (seat,
pointer_frame->delta_x,
pointer_frame->delta_y);
pointer_frame->delta_x = 0;
pointer_frame->delta_y = 0;
pointer_frame->discrete_x = 0;
pointer_frame->discrete_y = 0;
}
static void
gdk_wayland_seat_flush_frame_event (GdkWaylandSeat *seat)
{
if (seat->pointer_frame.event)
{
_gdk_wayland_display_deliver_event (gdk_seat_get_display (GDK_SEAT (seat)),
seat->pointer_frame.event);
seat->pointer_frame.event = NULL;
}
else
flush_scroll_event (seat, &seat->pointer_frame);
}
static GdkEvent *
gdk_wayland_seat_get_frame_event (GdkWaylandSeat *seat,
GdkEventType evtype)
{
if (seat->pointer_frame.event &&
seat->pointer_frame.event->type != evtype)
gdk_wayland_seat_flush_frame_event (seat);
seat->pointer_frame.event = gdk_event_new (evtype);
return seat->pointer_frame.event;
}
static void
pointer_handle_enter (void *data,
struct wl_pointer *pointer,
@ -946,7 +1067,7 @@ pointer_handle_enter (void *data,
device->surface_y = wl_fixed_to_double (sy);
device->enter_serial = serial;
event = gdk_event_new (GDK_ENTER_NOTIFY);
event = gdk_wayland_seat_get_frame_event (device, GDK_ENTER_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
@ -966,11 +1087,12 @@ pointer_handle_enter (void *data,
&event->crossing.x_root,
&event->crossing.y_root);
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("enter, device %p surface %p",
device, device->pointer_focus));
if (wayland_display->seat_version < WL_POINTER_HAS_FRAME)
gdk_wayland_seat_flush_frame_event (device);
}
static void
@ -994,7 +1116,7 @@ pointer_handle_leave (void *data,
_gdk_wayland_display_update_serial (wayland_display, serial);
event = gdk_event_new (GDK_LEAVE_NOTIFY);
event = gdk_wayland_seat_get_frame_event (device, GDK_LEAVE_NOTIFY);
event->crossing.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
@ -1014,8 +1136,6 @@ pointer_handle_leave (void *data,
&event->crossing.x_root,
&event->crossing.y_root);
_gdk_wayland_display_deliver_event (device->display, event);
GDK_NOTE (EVENTS,
g_message ("leave, device %p surface %p",
device, device->pointer_focus));
@ -1025,6 +1145,9 @@ pointer_handle_leave (void *data,
gdk_wayland_device_stop_window_cursor_animation (device);
device->pointer_focus = NULL;
if (wayland_display->seat_version < WL_POINTER_HAS_FRAME)
gdk_wayland_seat_flush_frame_event (device);
}
static void
@ -1041,13 +1164,11 @@ pointer_handle_motion (void *data,
if (!device->pointer_focus)
return;
event = gdk_event_new (GDK_NOTHING);
device->time = time;
device->surface_x = wl_fixed_to_double (sx);
device->surface_y = wl_fixed_to_double (sy);
event->motion.type = GDK_MOTION_NOTIFY;
event = gdk_wayland_seat_get_frame_event (device, GDK_MOTION_NOTIFY);
event->motion.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
@ -1067,9 +1188,10 @@ pointer_handle_motion (void *data,
GDK_NOTE (EVENTS,
g_message ("motion %f %f, device %p state %d",
wl_fixed_to_double (sx), wl_fixed_to_double (sy),
device, event->button.state));
device, event->motion.state));
_gdk_wayland_display_deliver_event (device->display, event);
if (display->seat_version < WL_POINTER_HAS_FRAME)
gdk_wayland_seat_flush_frame_event (device);
}
static void
@ -1112,7 +1234,9 @@ pointer_handle_button (void *data,
if (state)
device->button_press_serial = serial;
event = gdk_event_new (state ? GDK_BUTTON_PRESS : GDK_BUTTON_RELEASE);
event = gdk_wayland_seat_get_frame_event (device,
state ? GDK_BUTTON_PRESS :
GDK_BUTTON_RELEASE);
event->button.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
@ -1142,7 +1266,8 @@ pointer_handle_button (void *data,
device,
event->button.state));
_gdk_wayland_display_deliver_event (device->display, event);
if (display->seat_version < WL_POINTER_HAS_FRAME)
gdk_wayland_seat_flush_frame_event (device);
}
static void
@ -1152,53 +1277,129 @@ pointer_handle_axis (void *data,
uint32_t axis,
wl_fixed_t value)
{
GdkWaylandDeviceData *device = data;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (device->display);
GdkEvent *event;
gdouble delta_x, delta_y;
GdkWaylandSeat *seat = data;
GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (seat->display);
if (!device->pointer_focus)
if (!seat->pointer_focus)
return;
/* get the delta and convert it into the expected range */
switch (axis)
{
case WL_POINTER_AXIS_VERTICAL_SCROLL:
delta_x = 0;
delta_y = wl_fixed_to_double (value) / 10.0;
pointer_frame->delta_y = wl_fixed_to_double (value) / 10.0;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
delta_x = wl_fixed_to_double (value) / 10.0;
delta_y = 0;
pointer_frame->delta_x = wl_fixed_to_double (value) / 10.0;
break;
default:
g_return_if_reached ();
}
device->time = time;
event = gdk_event_new (GDK_SCROLL);
event->scroll.window = g_object_ref (device->pointer_focus);
gdk_event_set_device (event, device->master_pointer);
gdk_event_set_source_device (event, device->pointer);
gdk_event_set_seat (event, gdk_device_get_seat (device->master_pointer));
event->scroll.time = time;
event->scroll.direction = GDK_SCROLL_SMOOTH;
event->scroll.delta_x = delta_x;
event->scroll.delta_y = delta_y;
event->scroll.state = device->button_modifiers | device->key_modifiers;
gdk_event_set_screen (event, display->screen);
get_coordinates (device,
&event->scroll.x,
&event->scroll.y,
&event->scroll.x_root,
&event->scroll.y_root);
seat->time = time;
GDK_NOTE (EVENTS,
g_message ("scroll %f %f, device %p",
event->scroll.delta_x, event->scroll.delta_y, device));
g_message ("scroll, axis %d, value %f, seat %p",
axis, wl_fixed_to_double (value) / 10.0,
seat));
_gdk_wayland_display_deliver_event (device->display, event);
if (display->seat_version < WL_POINTER_HAS_FRAME)
gdk_wayland_seat_flush_frame_event (seat);
}
static void
pointer_handle_frame (void *data,
struct wl_pointer *pointer)
{
GdkWaylandSeat *seat = data;
if (!seat->pointer_focus)
return;
GDK_NOTE (EVENTS,
g_message ("frame, seat %p", seat));
gdk_wayland_seat_flush_frame_event (seat);
}
static void
pointer_handle_axis_source (void *data,
struct wl_pointer *pointer,
enum wl_pointer_axis_source source)
{
GdkWaylandSeat *seat = data;
if (!seat->pointer_focus)
return;
/* We don't need to handle the scroll source right now. It only has real
* meaning for 'finger' (to trigger kinetic scrolling). The axis_stop
* event will generate the zero delta required to trigger kinetic
* scrolling, so explicity handling the source is not required.
*/
GDK_NOTE (EVENTS,
g_message ("axis source %d, seat %p", source, seat));
}
static void
pointer_handle_axis_stop (void *data,
struct wl_pointer *pointer,
uint32_t time,
uint32_t axis)
{
GdkWaylandSeat *seat = data;
GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
if (!seat->pointer_focus)
return;
seat->time = time;
switch (axis)
{
case WL_POINTER_AXIS_VERTICAL_SCROLL:
pointer_frame->delta_y = 0;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
pointer_frame->delta_x = 0;
break;
default:
g_return_if_reached ();
}
GDK_NOTE (EVENTS,
g_message ("axis stop, seat %p", seat));
}
static void
pointer_handle_axis_discrete (void *data,
struct wl_pointer *pointer,
uint32_t axis,
int32_t value)
{
GdkWaylandSeat *seat = data;
GdkWaylandPointerFrameData *pointer_frame = &seat->pointer_frame;
if (!seat->pointer_focus)
return;
switch (axis)
{
case WL_POINTER_AXIS_VERTICAL_SCROLL:
pointer_frame->discrete_y = value;
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
pointer_frame->discrete_x = value;
break;
default:
g_return_if_reached ();
}
GDK_NOTE (EVENTS,
g_message ("discrete scroll, axis %d, value %d, seat %p",
axis, value, seat));
}
static void
@ -2005,6 +2206,10 @@ static const struct wl_pointer_listener pointer_listener = {
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
pointer_handle_frame,
pointer_handle_axis_source,
pointer_handle_axis_stop,
pointer_handle_axis_discrete,
};
static const struct wl_keyboard_listener keyboard_listener = {

View File

@ -212,8 +212,10 @@ _gdk_wayland_display_add_seat (GdkWaylandDisplay *display_wayland,
GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (display_wayland);
struct wl_seat *seat;
display_wayland->seat_version = MIN (version, 5);
seat = wl_registry_bind (display_wayland->wl_registry,
id, &wl_seat_interface, MIN (version, 4));
id, &wl_seat_interface,
display_wayland->seat_version);
_gdk_wayland_device_manager_add_seat (gdk_display->device_manager,
id, seat);
_gdk_wayland_display_async_roundtrip (display_wayland);

View File

@ -92,6 +92,7 @@ struct _GdkWaylandDisplay
GSource *event_source;
int compositor_version;
int seat_version;
struct xkb_context *xkb_context;

View File

@ -41,6 +41,7 @@
#include "config.h"
#define WL_SURFACE_HAS_BUFFER_SCALE 3
#define WL_POINTER_HAS_FRAME 5
#define SUPPORTED_GTK_SHELL_VERSION 2