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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 5259769..72e902a 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;
@@ -387,24 +396,103 @@ 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;