From 67f4478867c7d6c50a755daefe06935a4c418ded Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 3 Feb 2020 00:57:12 -0800 Subject: [PATCH] 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. --- src/video/wayland/SDL_waylandevents.c | 125 +++++++++++++++++++++++--- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 52597694d..72e902a16 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -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;