Skip to content

Commit ee1c942

Browse files
committed
mouse: Handle explicit warp events
The warp event in Wayland can occur without explicitly warping the mouse, such as if a window changes position under the cursor while the cursor is stationary. Introduce the internal SDL_SendMouseWarp() function to handle updating the absolute cursor position without generating relative motion for these cases.
1 parent 7301044 commit ee1c942

5 files changed

Lines changed: 66 additions & 24 deletions

File tree

src/events/SDL_mouse.c

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,31 @@ void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouse
662662
SDL_PrivateSendMouseMotion(timestamp, window, mouseID, relative, x, y);
663663
}
664664

665+
void SDL_SendMouseWarp(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y)
666+
{
667+
SDL_Mouse *mouse = SDL_GetMouse();
668+
669+
// Ignore the previous position when we warp, as warps don't generate relative motion.
670+
mouse->last_x = x;
671+
mouse->last_y = y;
672+
mouse->has_position = false;
673+
674+
if (mouse->relative_mode) {
675+
/* Sending motion events when warping while relative mode is active can confuse
676+
* clients that don't expect it, so just update the absolute position and don't
677+
* generate a motion event unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set.
678+
*/
679+
if (!mouse->relative_mode_warp_motion) {
680+
mouse->x = x;
681+
mouse->y = y;
682+
mouse->has_position = true;
683+
return;
684+
}
685+
}
686+
687+
SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y);
688+
}
689+
665690
static void ConstrainMousePosition(SDL_Mouse *mouse, SDL_Window *window, float *x, float *y)
666691
{
667692
/* make sure that the pointers find themselves inside the windows,
@@ -1263,24 +1288,29 @@ void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ign
12631288
return;
12641289
}
12651290

1266-
// Ignore the previous position when we warp
1267-
mouse->last_x = x;
1268-
mouse->last_y = y;
1269-
mouse->has_position = false;
1270-
1271-
if (mouse->relative_mode && !ignore_relative_mode) {
1272-
/* 2.0.22 made warping in relative mode actually functional, which
1273-
* surprised many applications that weren't expecting the additional
1274-
* mouse motion.
1275-
*
1276-
* So for now, warping in relative mode adjusts the absolution position
1277-
* but doesn't generate motion events, unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set.
1278-
*/
1279-
if (!mouse->relative_mode_warp_motion) {
1280-
mouse->x = x;
1281-
mouse->y = y;
1282-
mouse->has_position = true;
1283-
return;
1291+
/* If the backend sends explicit warp events, this will be taken care of if/when the pointer actually warps,
1292+
* Warps when in relative save the position to be applied when leaving relative mode.
1293+
*/
1294+
if (!mouse->have_explicit_warp_event || mouse->relative_mode) {
1295+
// Ignore the previous position when we warp, as warps don't generate relative motion.
1296+
mouse->last_x = x;
1297+
mouse->last_y = y;
1298+
mouse->has_position = false;
1299+
1300+
if (mouse->relative_mode && !ignore_relative_mode) {
1301+
/* 2.0.22 made warping in relative mode actually functional, which
1302+
* surprised many applications that weren't expecting the additional
1303+
* mouse motion.
1304+
*
1305+
* So for now, warping in relative mode adjusts the absolute position, but
1306+
* doesn't generate motion events, unless SDL_HINT_MOUSE_RELATIVE_WARP_MOTION is set.
1307+
*/
1308+
if (!mouse->relative_mode_warp_motion) {
1309+
mouse->x = x;
1310+
mouse->y = y;
1311+
mouse->has_position = true;
1312+
return;
1313+
}
12841314
}
12851315
}
12861316

src/events/SDL_mouse_c.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ typedef struct
136136
bool warp_emulation_hint;
137137
bool warp_emulation_active;
138138
bool warp_emulation_prohibited;
139+
bool have_explicit_warp_event;
139140
Uint64 last_center_warp_time_ns;
140141
bool enable_normal_speed_scale;
141142
float normal_speed_scale;
@@ -210,6 +211,9 @@ extern bool SDL_UpdateMouseCapture(bool force_release);
210211
// Send a mouse motion event
211212
extern void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y);
212213

214+
// Send a mouse motion event resulting from a pointer warp
215+
void SDL_SendMouseWarp(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y);
216+
213217
// Send a mouse button event
214218
extern void SDL_SendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down);
215219

src/video/wayland/SDL_waylandevents.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
640640
}
641641
}
642642

643-
static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat)
643+
static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat, bool warp)
644644
{
645645
SDL_WindowData *window_data = seat->pointer.focus;
646646
SDL_Window *window = window_data ? window_data->sdlwindow : NULL;
@@ -656,7 +656,11 @@ static void pointer_dispatch_absolute_motion(SDL_WaylandSeat *seat)
656656

657657
sx *= window_data->pointer_scale.x;
658658
sy *= window_data->pointer_scale.y;
659-
SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, false, (float)sx, (float)sy);
659+
if (!warp) {
660+
SDL_SendMouseMotion(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, false, (float)sx, (float)sy);
661+
} else {
662+
SDL_SendMouseWarp(seat->pointer.pending_frame.timestamp_ns, window_data->sdlwindow, seat->pointer.sdl_id, (float)sx, (float)sy);
663+
}
660664

661665
seat->pointer.last_motion.x = (int)SDL_floor(sx);
662666
seat->pointer.last_motion.y = (int)SDL_floor(sy);
@@ -763,7 +767,7 @@ static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
763767
}
764768
} else {
765769
seat->pointer.pending_frame.timestamp_ns = timestamp;
766-
pointer_dispatch_absolute_motion(seat);
770+
pointer_dispatch_absolute_motion(seat, false);
767771
}
768772
}
769773

@@ -790,7 +794,7 @@ static void pointer_dispatch_enter(SDL_WaylandSeat *seat)
790794
SDL_SetMouseFocus(window->sdlwindow);
791795

792796
// Send the initial position.
793-
pointer_dispatch_absolute_motion(seat);
797+
pointer_dispatch_absolute_motion(seat, false);
794798

795799
// Update the pointer grab state.
796800
Wayland_SeatUpdatePointerGrab(seat);
@@ -1280,7 +1284,7 @@ static void pointer_handle_frame(void *data, struct wl_pointer *pointer)
12801284
}
12811285

12821286
if (seat->pointer.pending_frame.have_absolute) {
1283-
pointer_dispatch_absolute_motion(seat);
1287+
pointer_dispatch_absolute_motion(seat, seat->pointer.pending_frame.have_warp);
12841288
}
12851289

12861290
if (seat->pointer.pending_frame.have_relative) {
@@ -1341,6 +1345,7 @@ static void pointer_handle_warp(void *data, struct wl_pointer *wl_pointer, wl_fi
13411345
SDL_WaylandSeat *seat = (SDL_WaylandSeat *)data;
13421346

13431347
seat->pointer.pending_frame.have_absolute = true;
1348+
seat->pointer.pending_frame.have_warp = true;
13441349
seat->pointer.pending_frame.absolute.sx = surface_x;
13451350
seat->pointer.pending_frame.absolute.sy = surface_y;
13461351
}

src/video/wayland/SDL_waylandevents_c.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ typedef struct SDL_WaylandSeat
205205
{
206206
bool have_absolute;
207207
bool have_relative;
208+
bool have_warp;
208209
bool have_axis;
209210

210211
Uint32 buttons_pressed;

src/video/wayland/SDL_waylandmouse.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@ void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float
11991199
}
12001200

12011201
if (wl_pointer_get_version(seat->pointer.wl_pointer) < WL_POINTER_WARP_SINCE_VERSION) {
1202-
SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y);
1202+
SDL_SendMouseWarp(0, window->sdlwindow, seat->pointer.sdl_id, x, y);
12031203
}
12041204
}
12051205
}
@@ -1383,6 +1383,8 @@ void Wayland_InitMouse(SDL_VideoData *data)
13831383
mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
13841384
mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
13851385

1386+
mouse->have_explicit_warp_event = true;
1387+
13861388
SDL_HitTestResult r = SDL_HITTEST_NORMAL;
13871389
while (r <= SDL_HITTEST_RESIZE_LEFT) {
13881390
switch (r) {

0 commit comments

Comments
 (0)