Fixed bug 4917 - Wayland: handle discrete pointer axis events

Luis Caceres

The current handling of Wayland mouse pointer events only handles wl_pointer.axis events, which, according to the Wayland documentation, deal with mouse wheel scroll events on a continuous scale. While this is reasonable for some input sources (e.g. touchpad two-finger scrolling), it is not for mouse wheel clicks which generate wl_pointer.axis events with large deltas.

This patch adds handling for wl_pointer.axis_discrete and wl_pointer.frame events and prefers to report SDL_MouseWheelEvent in discrete units if they are available. This means that for mouse wheel scrolling we count in clicks, but for touchpad two-finger scrolling we still use whatever units Wayland uses. This behaviour is closer to that of the X11 backend.

Since these events are only available since version 5 of the wl_seat interface, this patch also checks for this and falls back to the previous behaviour if its not available. I also had to add definitions for some of the pointer and keyboard events specified in versions 2-5 but these are just stubs and do nothing.
This commit is contained in:
Sam Lantinga 2020-02-03 00:57:12 -08:00
parent 68e702b657
commit 67f4478867

View file

@ -72,6 +72,15 @@ struct SDL_WaylandInput {
struct xkb_keymap *keymap;
struct xkb_state *state;
} xkb;
/* information about axis events on current frame */
struct {
SDL_bool is_x_discrete;
float x;
SDL_bool is_y_discrete;
float y;
} pointer_curr_axis_info;
};
struct SDL_WaylandTouchPoint {
@ -361,8 +370,8 @@ pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
}
static void
pointer_handle_axis_common(struct SDL_WaylandInput *input,
uint32_t time, uint32_t axis, wl_fixed_t value)
pointer_handle_axis_common_v1(struct SDL_WaylandInput *input,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
SDL_WindowData *window = input->pointer_focus;
enum wl_pointer_axis a = axis;
@ -386,25 +395,104 @@ pointer_handle_axis_common(struct SDL_WaylandInput *input,
}
}
static void
pointer_handle_axis_common(struct SDL_WaylandInput *input, SDL_bool discrete,
uint32_t axis, wl_fixed_t value)
{
enum wl_pointer_axis a = axis;
if (input->pointer_focus) {
switch (a) {
case WL_POINTER_AXIS_VERTICAL_SCROLL:
if (discrete) {
/* this is a discrete axis event so we process it and flag
* to ignore future continuous axis events in this frame */
input->pointer_curr_axis_info.is_y_discrete = SDL_TRUE;
} else if(input->pointer_curr_axis_info.is_y_discrete) {
/* this is a continuous axis event and we have already
* processed a discrete axis event before so we ignore it */
break;
}
input->pointer_curr_axis_info.y = 0 - (float)wl_fixed_to_double(value);
break;
case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
if (discrete) {
/* this is a discrete axis event so we process it and flag
* to ignore future continuous axis events in this frame */
input->pointer_curr_axis_info.is_x_discrete = SDL_TRUE;
} else if(input->pointer_curr_axis_info.is_x_discrete) {
/* this is a continuous axis event and we have already
* processed a discrete axis event before so we ignore it */
break;
}
input->pointer_curr_axis_info.x = 0 - (float)wl_fixed_to_double(value);
break;
}
}
}
static void
pointer_handle_axis(void *data, struct wl_pointer *pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
struct SDL_WaylandInput *input = data;
pointer_handle_axis_common(input, time, axis, value);
if(wl_seat_interface.version >= 5)
pointer_handle_axis_common(input, SDL_FALSE, axis, value);
else
pointer_handle_axis_common_v1(input, time, axis, value);
}
static void
pointer_handle_frame(void *data, struct wl_pointer *pointer)
{
struct SDL_WaylandInput *input = data;
SDL_WindowData *window = input->pointer_focus;
float x = input->pointer_curr_axis_info.x, y = input->pointer_curr_axis_info.y;
/* clear pointer_curr_axis_info for next frame */
memset(&input->pointer_curr_axis_info, 0, sizeof input->pointer_curr_axis_info);
if(x == 0.0f && y == 0.0f)
return;
else
SDL_SendMouseWheel(window->sdlwindow, 0, x, y, SDL_MOUSEWHEEL_NORMAL);
}
static void
pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
uint32_t axis_source)
{
/* unimplemented */
}
static void
pointer_handle_axis_stop(void *data, struct wl_pointer *pointer,
uint32_t time, uint32_t axis)
{
/* unimplemented */
}
static void
pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
uint32_t axis, int32_t discrete)
{
struct SDL_WaylandInput *input = data;
pointer_handle_axis_common(input, SDL_TRUE, axis, wl_fixed_from_int(discrete));
}
static const struct wl_pointer_listener pointer_listener = {
pointer_handle_enter,
pointer_handle_leave,
pointer_handle_motion,
pointer_handle_button,
pointer_handle_axis,
NULL, /* frame */
NULL, /* axis_source */
NULL, /* axis_stop */
NULL, /* axis_discrete */
pointer_handle_frame, // Version 5
pointer_handle_axis_source, // Version 5
pointer_handle_axis_stop, // Version 5
pointer_handle_axis_discrete, // Version 5
};
static void
@ -604,13 +692,20 @@ keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
mods_locked, 0, 0, group);
}
static void
keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay)
{
/* unimplemented */
}
static const struct wl_keyboard_listener keyboard_listener = {
keyboard_handle_keymap,
keyboard_handle_enter,
keyboard_handle_leave,
keyboard_handle_key,
keyboard_handle_modifiers,
NULL, /* repeat_info */
keyboard_handle_repeat_info, // Version 4
};
static void
@ -621,6 +716,7 @@ seat_handle_capabilities(void *data, struct wl_seat *seat,
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
input->pointer = wl_seat_get_pointer(seat);
memset(&input->pointer_curr_axis_info, 0, sizeof input->pointer_curr_axis_info);
input->display->pointer = input->pointer;
wl_pointer_set_user_data(input->pointer, input);
wl_pointer_add_listener(input->pointer, &pointer_listener,
@ -654,9 +750,15 @@ seat_handle_capabilities(void *data, struct wl_seat *seat,
}
}
static void
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
{
/* unimplemented */
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
NULL, /* name */
seat_handle_name, // Version 2
};
static void
@ -906,7 +1008,10 @@ Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
return;
input->display = d;
input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
if (wl_seat_interface.version >= 5)
input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 5);
else
input->seat = wl_registry_bind(d->registry, id, &wl_seat_interface, 1);
input->sx_w = wl_fixed_from_int(0);
input->sy_w = wl_fixed_from_int(0);
d->input = input;