Răsfoiți Sursa

wayland: Send motion events in a frame

Pointer events in seat version 5 and higher should be grouped and sent together when a pointer frame event occurs. Store pending pointer motion events and dispatch them together when a frame event is received.

This helps avoid spurious pointer motion events that some compositors generate with no associated frame (e.g. from some tablet events).
Frank Praznik 10 luni în urmă
părinte
comite
9ff0438863
2 a modificat fișierele cu 198 adăugiri și 104 ștergeri
  1. 165 94
      src/video/wayland/SDL_waylandevents.c
  2. 33 10
      src/video/wayland/SDL_waylandevents_c.h

+ 165 - 94
src/video/wayland/SDL_waylandevents.c

@@ -237,14 +237,14 @@ static Uint64 Wayland_GetKeyboardTimestamp(SDL_WaylandSeat *seat, Uint32 wl_time
 
 static Uint64 Wayland_GetPointerTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms)
 {
-    const Uint64 adjustedTimestampMS = Wayland_EventTimestampMSToNS(wl_timestamp_ms);
-    return Wayland_AdjustEventTimestampBase(seat->pointer.timestamps ? seat->pointer.highres_timestamp_ns : adjustedTimestampMS);
+    const Uint64 adjustedTimestampNS = Wayland_EventTimestampMSToNS(wl_timestamp_ms);
+    return Wayland_AdjustEventTimestampBase(seat->pointer.timestamps ? seat->pointer.highres_timestamp_ns : adjustedTimestampNS);
 }
 
 Uint64 Wayland_GetTouchTimestamp(SDL_WaylandSeat *seat, Uint32 wl_timestamp_ms)
 {
-    const Uint64 adjustedTimestampMS = Wayland_EventTimestampMSToNS(wl_timestamp_ms);
-    return Wayland_AdjustEventTimestampBase(seat->touch.timestamps ? seat->touch.highres_timestamp_ns : adjustedTimestampMS);
+    const Uint64 adjustedTimestampNS = Wayland_EventTimestampMSToNS(wl_timestamp_ms);
+    return Wayland_AdjustEventTimestampBase(seat->touch.timestamps ? seat->touch.highres_timestamp_ns : adjustedTimestampNS);
 }
 
 static void input_timestamp_listener(void *data, struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1,
@@ -579,15 +579,15 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
     }
 }
 
-static void pointer_handle_motion_common(SDL_WaylandSeat *seat, Uint64 nsTimestamp, wl_fixed_t sx_w, wl_fixed_t sy_w)
+static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat)
 {
     SDL_WindowData *window_data = seat->pointer.focus;
     SDL_Window *window = window_data ? window_data->sdlwindow : NULL;
 
     if (window_data) {
-        const float sx = (float)(wl_fixed_to_double(sx_w) * window_data->pointer_scale.x);
-        const float sy = (float)(wl_fixed_to_double(sy_w) * window_data->pointer_scale.y);
-        SDL_SendMouseMotion(nsTimestamp, window_data->sdlwindow, seat->pointer.sdl_id, false, sx, sy);
+        const float sx = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sx) * window_data->pointer_scale.x);
+        const float sy = (float)(wl_fixed_to_double(seat->pointer.pending_frame.absolute.sy) * window_data->pointer_scale.y);
+        SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, false, sx, sy);
 
         seat->pointer.last_motion.x = (int)SDL_floorf(sx);
         seat->pointer.last_motion.y = (int)SDL_floorf(sy);
@@ -682,10 +682,26 @@ static void pointer_handle_motion_common(SDL_WaylandSeat *seat, Uint64 nsTimesta
 }
 
 static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
-                                  uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
+                                  uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
 {
     SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
-    pointer_handle_motion_common(seat, Wayland_GetPointerTimestamp(seat, time), sx_w, sy_w);
+
+    seat->pointer.pending_frame.have_absolute = true;
+    seat->pointer.pending_frame.absolute.sx = sx;
+    seat->pointer.pending_frame.absolute.sy = sy;
+
+    /* The relative pointer timestamp is higher resolution than the default millisecond timestamp,
+     * but lower than the highres timestamp. Use the best timer available for this frame, but still
+     * process the pending millisecond timestamp to update the offset value for other events.
+     */
+    const Uint64 timestamp = Wayland_GetPointerTimestamp(seat, time);
+    if (!seat->pointer.pending_frame.have_relative || seat->pointer.timestamps) {
+        seat->pointer.pending_frame.timestamp_ns = timestamp;
+    }
+
+    if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
+        pointer_dispatch_absolute_motion(seat);
+    }
 }
 
 static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
@@ -713,23 +729,36 @@ static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
      * event with no following motion event, but with the new coordinates
      * as part of the enter event.
      *
-     * FIXME: This causes a movement event with an anomalous timestamp when
-     *        the cursor enters the window.
+     * If another event with a real timestamp is part of this frame, use it.
+     * Otherwise, set it to 0 to use the current system timer.
      */
-    pointer_handle_motion_common(seat, 0, sx_w, sy_w);
+    if (!seat->pointer.pending_frame.have_absolute &&
+        !seat->pointer.pending_frame.have_relative &&
+        !seat->pointer.pending_frame.have_axis) {
+        seat->pointer.pending_frame.timestamp_ns = 0;
+    }
+    seat->pointer.pending_frame.absolute.sx = sx_w;
+    seat->pointer.pending_frame.absolute.sy = sy_w;
 
-    // Update the pointer grab state.
-    Wayland_SeatUpdatePointerGrab(seat);
+    seat->pointer.pending_frame.have_absolute = true;
+    seat->pointer.pending_frame.have_enter = true;
 
-    /* If the cursor was changed while our window didn't have pointer
-     * focus, we might need to trigger another call to
-     * wl_pointer_set_cursor() for the new cursor to be displayed.
-     *
-     * This will also update the cursor if a second pointer entered a
-     * window that already has focus, as the focus change sequence
-     * won't be run.
-     */
-    Wayland_SeatUpdateCursor(seat);
+    if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
+        pointer_dispatch_absolute_motion(seat);
+
+        // Update the pointer grab state.
+        Wayland_SeatUpdatePointerGrab(seat);
+
+        /* If the cursor was changed while our window didn't have pointer
+         * focus, we might need to trigger another call to
+         * wl_pointer_set_cursor() for the new cursor to be displayed.
+         *
+         * This will also update the cursor if a second pointer entered a
+         * window that already has focus, as the focus change sequence
+         * won't be run.
+         */
+        Wayland_SeatUpdateCursor(seat);
+    }
 }
 
 static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
@@ -969,65 +998,67 @@ static void pointer_handle_axis_common(SDL_WaylandSeat *seat, enum SDL_WaylandAx
     const enum wl_pointer_axis a = axis;
 
     if (seat->pointer.focus) {
+        seat->pointer.pending_frame.have_axis = true;
+
         switch (a) {
         case WL_POINTER_AXIS_VERTICAL_SCROLL:
             switch (type) {
-            case AXIS_EVENT_VALUE120:
+            case SDL_WAYLAND_AXIS_EVENT_VALUE120:
                 /*
                  * High resolution scroll event. The spec doesn't state that axis_value120
                  * events are limited to one per frame, so the values are accumulated.
                  */
-                if (seat->pointer.current_axis_info.y_axis_type != AXIS_EVENT_VALUE120) {
-                    seat->pointer.current_axis_info.y_axis_type = AXIS_EVENT_VALUE120;
-                    seat->pointer.current_axis_info.y = 0.0f;
+                if (seat->pointer.pending_frame.axis.y_axis_type != SDL_WAYLAND_AXIS_EVENT_VALUE120) {
+                    seat->pointer.pending_frame.axis.y_axis_type = SDL_WAYLAND_AXIS_EVENT_VALUE120;
+                    seat->pointer.pending_frame.axis.y = 0.0f;
                 }
-                seat->pointer.current_axis_info.y += 0 - (float)wl_fixed_to_double(value);
+                seat->pointer.pending_frame.axis.y += 0 - (float)wl_fixed_to_double(value);
                 break;
-            case AXIS_EVENT_DISCRETE:
+            case SDL_WAYLAND_AXIS_EVENT_DISCRETE:
                 /*
                  * This is a discrete axis event, so we process it and set the
                  * flag to ignore future continuous axis events in this frame.
                  */
-                if (seat->pointer.current_axis_info.y_axis_type != AXIS_EVENT_DISCRETE) {
-                    seat->pointer.current_axis_info.y_axis_type = AXIS_EVENT_DISCRETE;
-                    seat->pointer.current_axis_info.y = 0 - (float)wl_fixed_to_double(value);
+                if (seat->pointer.pending_frame.axis.y_axis_type != SDL_WAYLAND_AXIS_EVENT_DISCRETE) {
+                    seat->pointer.pending_frame.axis.y_axis_type = SDL_WAYLAND_AXIS_EVENT_DISCRETE;
+                    seat->pointer.pending_frame.axis.y = 0 - (float)wl_fixed_to_double(value);
                 }
                 break;
-            case AXIS_EVENT_CONTINUOUS:
+            case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS:
                 // Only process continuous events if no discrete events have been received.
-                if (seat->pointer.current_axis_info.y_axis_type == AXIS_EVENT_CONTINUOUS) {
-                    seat->pointer.current_axis_info.y = 0 - (float)wl_fixed_to_double(value);
+                if (seat->pointer.pending_frame.axis.y_axis_type == SDL_WAYLAND_AXIS_EVENT_CONTINUOUS) {
+                    seat->pointer.pending_frame.axis.y = 0 - (float)wl_fixed_to_double(value);
                 }
                 break;
             }
             break;
         case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
             switch (type) {
-            case AXIS_EVENT_VALUE120:
+            case SDL_WAYLAND_AXIS_EVENT_VALUE120:
                 /*
                  * High resolution scroll event. The spec doesn't state that axis_value120
                  * events are limited to one per frame, so the values are accumulated.
                  */
-                if (seat->pointer.current_axis_info.x_axis_type != AXIS_EVENT_VALUE120) {
-                    seat->pointer.current_axis_info.x_axis_type = AXIS_EVENT_VALUE120;
-                    seat->pointer.current_axis_info.x = 0.0f;
+                if (seat->pointer.pending_frame.axis.x_axis_type != SDL_WAYLAND_AXIS_EVENT_VALUE120) {
+                    seat->pointer.pending_frame.axis.x_axis_type = SDL_WAYLAND_AXIS_EVENT_VALUE120;
+                    seat->pointer.pending_frame.axis.x = 0.0f;
                 }
-                seat->pointer.current_axis_info.x += (float)wl_fixed_to_double(value);
+                seat->pointer.pending_frame.axis.x += (float)wl_fixed_to_double(value);
                 break;
-            case AXIS_EVENT_DISCRETE:
+            case SDL_WAYLAND_AXIS_EVENT_DISCRETE:
                 /*
                  * This is a discrete axis event, so we process it and set the
                  * flag to ignore future continuous axis events in this frame.
                  */
-                if (seat->pointer.current_axis_info.x_axis_type != AXIS_EVENT_DISCRETE) {
-                    seat->pointer.current_axis_info.x_axis_type = AXIS_EVENT_DISCRETE;
-                    seat->pointer.current_axis_info.x = (float)wl_fixed_to_double(value);
+                if (seat->pointer.pending_frame.axis.x_axis_type != SDL_WAYLAND_AXIS_EVENT_DISCRETE) {
+                    seat->pointer.pending_frame.axis.x_axis_type = SDL_WAYLAND_AXIS_EVENT_DISCRETE;
+                    seat->pointer.pending_frame.axis.x = (float)wl_fixed_to_double(value);
                 }
                 break;
-            case AXIS_EVENT_CONTINUOUS:
+            case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS:
                 // Only process continuous events if no discrete events have been received.
-                if (seat->pointer.current_axis_info.x_axis_type == AXIS_EVENT_CONTINUOUS) {
-                    seat->pointer.current_axis_info.x = (float)wl_fixed_to_double(value);
+                if (seat->pointer.pending_frame.axis.x_axis_type == SDL_WAYLAND_AXIS_EVENT_CONTINUOUS) {
+                    seat->pointer.pending_frame.axis.x = (float)wl_fixed_to_double(value);
                 }
                 break;
             }
@@ -1043,8 +1074,8 @@ static void pointer_handle_axis(void *data, struct wl_pointer *pointer,
     const Uint64 nsTimestamp = Wayland_GetPointerTimestamp(seat, time);
 
     if (wl_seat_get_version(seat->wl_seat) >= WL_POINTER_FRAME_SINCE_VERSION) {
-        seat->pointer.current_axis_info.timestamp_ns = nsTimestamp;
-        pointer_handle_axis_common(seat, AXIS_EVENT_CONTINUOUS, axis, value);
+        seat->pointer.pending_frame.timestamp_ns = nsTimestamp;
+        pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_CONTINUOUS, axis, value);
     } else {
         pointer_handle_axis_common_v1(seat, nsTimestamp, axis, value);
     }
@@ -1059,58 +1090,103 @@ static void pointer_handle_axis_relative_direction(void *data, struct wl_pointer
     }
     switch (axis_relative_direction) {
     case WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL:
-        seat->pointer.current_axis_info.direction = SDL_MOUSEWHEEL_NORMAL;
+        seat->pointer.pending_frame.axis.direction = SDL_MOUSEWHEEL_NORMAL;
         break;
     case WL_POINTER_AXIS_RELATIVE_DIRECTION_INVERTED:
-        seat->pointer.current_axis_info.direction = SDL_MOUSEWHEEL_FLIPPED;
+        seat->pointer.pending_frame.axis.direction = SDL_MOUSEWHEEL_FLIPPED;
         break;
     }
 }
 
-static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
+static void pointer_dispatch_relative_motion(SDL_WaylandSeat *seat)
 {
-    SDL_WaylandSeat *seat = data;
     SDL_WindowData *window = seat->pointer.focus;
+    SDL_Mouse *mouse = SDL_GetMouse();
+
+    double dx;
+    double dy;
+    if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
+        dx = wl_fixed_to_double(seat->pointer.pending_frame.relative.dx_unaccel);
+        dy = wl_fixed_to_double(seat->pointer.pending_frame.relative.dy_unaccel);
+    } else {
+        dx = wl_fixed_to_double(seat->pointer.pending_frame.relative.dx) * window->pointer_scale.x;
+        dy = wl_fixed_to_double(seat->pointer.pending_frame.relative.dy) * window->pointer_scale.y;
+    }
+
+    SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
+}
+
+static void pointer_dispatch_axis(SDL_WaylandSeat *seat)
+{
     float x, y;
-    SDL_MouseWheelDirection direction = seat->pointer.current_axis_info.direction;
+    SDL_MouseWheelDirection direction = seat->pointer.pending_frame.axis.direction;
 
-    switch (seat->pointer.current_axis_info.x_axis_type) {
-    case AXIS_EVENT_CONTINUOUS:
-        x = seat->pointer.current_axis_info.x / WAYLAND_WHEEL_AXIS_UNIT;
+    switch (seat->pointer.pending_frame.axis.x_axis_type) {
+    case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS:
+        x = seat->pointer.pending_frame.axis.x / WAYLAND_WHEEL_AXIS_UNIT;
         break;
-    case AXIS_EVENT_DISCRETE:
-        x = seat->pointer.current_axis_info.x;
+    case SDL_WAYLAND_AXIS_EVENT_DISCRETE:
+        x = seat->pointer.pending_frame.axis.x;
         break;
-    case AXIS_EVENT_VALUE120:
-        x = seat->pointer.current_axis_info.x / 120.0f;
+    case SDL_WAYLAND_AXIS_EVENT_VALUE120:
+        x = seat->pointer.pending_frame.axis.x / 120.0f;
         break;
     default:
         x = 0.0f;
         break;
     }
 
-    switch (seat->pointer.current_axis_info.y_axis_type) {
-    case AXIS_EVENT_CONTINUOUS:
-        y = seat->pointer.current_axis_info.y / WAYLAND_WHEEL_AXIS_UNIT;
+    switch (seat->pointer.pending_frame.axis.y_axis_type) {
+    case SDL_WAYLAND_AXIS_EVENT_CONTINUOUS:
+        y = seat->pointer.pending_frame.axis.y / WAYLAND_WHEEL_AXIS_UNIT;
         break;
-    case AXIS_EVENT_DISCRETE:
-        y = seat->pointer.current_axis_info.y;
+    case SDL_WAYLAND_AXIS_EVENT_DISCRETE:
+        y = seat->pointer.pending_frame.axis.y;
         break;
-    case AXIS_EVENT_VALUE120:
-        y = seat->pointer.current_axis_info.y / 120.0f;
+    case SDL_WAYLAND_AXIS_EVENT_VALUE120:
+        y = seat->pointer.pending_frame.axis.y / 120.0f;
         break;
     default:
         y = 0.0f;
         break;
     }
 
-    // clear pointer.current_axis_info for next frame
-    SDL_memset(&seat->pointer.current_axis_info, 0, sizeof(seat->pointer.current_axis_info));
+    SDL_SendMouseWheel(seat->pointer.pending_frame.timestamp_ns,
+                       seat->pointer.focus->sdlwindow, seat->pointer.sdl_id, x, y, direction);
+}
+
+static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
+{
+    SDL_WaylandSeat *seat = data;
+
+    if (seat->pointer.pending_frame.have_absolute) {
+        pointer_dispatch_absolute_motion(seat);
 
-    if (x != 0.0f || y != 0.0f) {
-        SDL_SendMouseWheel(seat->pointer.current_axis_info.timestamp_ns,
-                           window->sdlwindow, seat->pointer.sdl_id, x, y, direction);
+        if (seat->pointer.pending_frame.have_enter) {
+            // Update the pointer grab state.
+            Wayland_SeatUpdatePointerGrab(seat);
+
+            /* If the cursor was changed while our window didn't have pointer
+             * focus, we might need to trigger another call to
+             * wl_pointer_set_cursor() for the new cursor to be displayed.
+             *
+             * This will also update the cursor if a second pointer entered a
+             * window that already has focus, as the focus change sequence
+             * won't be run.
+             */
+            Wayland_SeatUpdateCursor(seat);
+        }
+    }
+
+    if (seat->pointer.pending_frame.have_relative) {
+        pointer_dispatch_relative_motion(seat);
     }
+
+    if (seat->pointer.pending_frame.have_axis) {
+        pointer_dispatch_axis(seat);
+    }
+
+    SDL_zero(seat->pointer.pending_frame);
 }
 
 static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer,
@@ -1130,7 +1206,7 @@ static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer,
 {
     SDL_WaylandSeat *seat = data;
 
-    pointer_handle_axis_common(seat, AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));
+    pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));
 }
 
 static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer,
@@ -1138,7 +1214,7 @@ static void pointer_handle_axis_value120(void *data, struct wl_pointer *pointer,
 {
     SDL_WaylandSeat *seat = data;
 
-    pointer_handle_axis_common(seat, AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));
+    pointer_handle_axis_common(seat, SDL_WAYLAND_AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));
 }
 
 static const struct wl_pointer_listener pointer_listener = {
@@ -1159,29 +1235,24 @@ static void relative_pointer_handle_relative_motion(void *data,
                                                     struct zwp_relative_pointer_v1 *pointer,
                                                     uint32_t time_hi,
                                                     uint32_t time_lo,
-                                                    wl_fixed_t dx_w,
-                                                    wl_fixed_t dy_w,
-                                                    wl_fixed_t dx_unaccel_w,
-                                                    wl_fixed_t dy_unaccel_w)
+                                                    wl_fixed_t dx,
+                                                    wl_fixed_t dy,
+                                                    wl_fixed_t dx_unaccel,
+                                                    wl_fixed_t dy_unaccel)
 {
     SDL_WaylandSeat *seat = data;
-    SDL_WindowData *window = seat->pointer.focus;
-    SDL_Mouse *mouse = SDL_GetMouse();
 
     // Relative pointer event times are in microsecond granularity.
-    const Uint64 timestamp = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
+    seat->pointer.pending_frame.have_relative = true;
+    seat->pointer.pending_frame.relative.dx = dx;
+    seat->pointer.pending_frame.relative.dy = dy;
+    seat->pointer.pending_frame.relative.dx_unaccel = dx;
+    seat->pointer.pending_frame.relative.dy_unaccel = dy;
+    seat->pointer.pending_frame.timestamp_ns = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
 
-    double dx;
-    double dy;
-    if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
-        dx = wl_fixed_to_double(dx_unaccel_w);
-        dy = wl_fixed_to_double(dy_unaccel_w);
-    } else {
-        dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
-        dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
+    if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_FRAME_SINCE_VERSION) {
+        pointer_dispatch_relative_motion(seat);
     }
-
-    SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
 }
 
 static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
@@ -2214,7 +2285,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w
 
     if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !seat->pointer.wl_pointer) {
         seat->pointer.wl_pointer = wl_seat_get_pointer(wl_seat);
-        SDL_memset(&seat->pointer.current_axis_info, 0, sizeof(seat->pointer.current_axis_info));
+        SDL_memset(&seat->pointer.pending_frame.axis, 0, sizeof(seat->pointer.pending_frame.axis));
 
         Wayland_SeatCreateCursorShape(seat);
 

+ 33 - 10
src/video/wayland/SDL_waylandevents_c.h

@@ -35,9 +35,9 @@
 
 enum SDL_WaylandAxisEvent
 {
-    AXIS_EVENT_CONTINUOUS = 0,
-    AXIS_EVENT_DISCRETE,
-    AXIS_EVENT_VALUE120
+    SDL_WAYLAND_AXIS_EVENT_CONTINUOUS = 0,
+    SDL_WAYLAND_AXIS_EVENT_DISCRETE,
+    SDL_WAYLAND_AXIS_EVENT_VALUE120
 };
 
 typedef struct
@@ -135,16 +135,39 @@ typedef struct SDL_WaylandSeat
         // Information about axis events on the current frame
         struct
         {
-            enum SDL_WaylandAxisEvent x_axis_type;
-            float x;
-
-            enum SDL_WaylandAxisEvent y_axis_type;
-            float y;
+            bool have_absolute;
+            bool have_relative;
+            bool have_axis;
+            bool have_enter;
+
+            struct
+            {
+                wl_fixed_t sx;
+                wl_fixed_t sy;
+            } absolute;
+
+            struct
+            {
+                wl_fixed_t dx;
+                wl_fixed_t dy;
+                wl_fixed_t dx_unaccel;
+                wl_fixed_t dy_unaccel;
+            } relative;
+
+            struct
+            {
+                enum SDL_WaylandAxisEvent x_axis_type;
+                float x;
+
+                enum SDL_WaylandAxisEvent y_axis_type;
+                float y;
+
+                SDL_MouseWheelDirection direction;
+            } axis;
 
             // Event timestamp in nanoseconds
             Uint64 timestamp_ns;
-            SDL_MouseWheelDirection direction;
-        } current_axis_info;
+        } pending_frame;
 
         // Cursor state
         struct