From 9ff0438863554e8222f63f2ad7c08e63cccda9a7 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Fri, 23 May 2025 11:32:15 -0400
Subject: [PATCH] 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).
---
src/video/wayland/SDL_waylandevents.c | 259 +++++++++++++++---------
src/video/wayland/SDL_waylandevents_c.h | 43 +++-
2 files changed, 198 insertions(+), 104 deletions(-)
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 8d591481110f2..e0c07e0e4a3ee 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/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);
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 79a334e3187dd..b30246fa44e9d 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/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