SDL: x11: Fix pointer warp on XWayland

From a845c7027e68ff1c75bf814819d8e6b22499d899 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 15 Apr 2024 13:46:32 -0400
Subject: [PATCH] x11: Fix pointer warp on XWayland

XWayland seems to require that the pointer be hidden when it is warped, so hide and show the pointer when warping, if required.

Note that XWayland still only allows warping within the window, so attempts to warp to global coordinates outside the window won't work.
---
 src/video/x11/SDL_x11mouse.c | 15 +++++++++++++++
 src/video/x11/SDL_x11video.c |  4 +++-
 src/video/x11/SDL_x11video.h |  2 ++
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c
index 079332ad7fe73..dc47353757925 100644
--- a/src/video/x11/SDL_x11mouse.c
+++ b/src/video/x11/SDL_x11mouse.c
@@ -318,6 +318,17 @@ static void X11_WarpMouseInternal(Window xwindow, float x, float y)
 {
     SDL_VideoData *videodata = SDL_GetVideoDevice()->driverdata;
     Display *display = videodata->display;
+    SDL_Mouse *mouse = SDL_GetMouse();
+    SDL_bool warp_hack = SDL_FALSE;
+
+    /* XWayland will only warp the cursor if it is hidden, so this workaround is required. */
+    if (videodata->is_xwayland && mouse && mouse->cursor_shown) {
+        warp_hack = SDL_TRUE;
+    }
+
+    if (warp_hack) {
+        X11_ShowCursor(NULL);
+    }
 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
     int deviceid = 0;
     if (X11_Xinput2IsInitialized()) {
@@ -336,6 +347,10 @@ static void X11_WarpMouseInternal(Window xwindow, float x, float y)
     {
         X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, (int)x, (int)y);
     }
+
+    if (warp_hack) {
+        X11_ShowCursor(SDL_GetCursor());
+    }
     X11_XSync(display, False);
     videodata->global_mouse_changed = SDL_TRUE;
 }
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 3aba223861dc0..d1736ecf3c26b 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -289,8 +289,10 @@ static SDL_VideoDevice *X11_CreateDevice(void)
     device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT |
                           VIDEO_DEVICE_CAPS_SENDS_FULLSCREEN_DIMENSIONS;
 
-    if (X11_IsXWayland(x11_display))
+    data->is_xwayland = X11_IsXWayland(x11_display);
+    if (data->is_xwayland) {
         device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED;
+    }
 
     return device;
 }
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index 71a2daf9d35b8..cafa035a07679 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -133,6 +133,8 @@ struct SDL_VideoData
     SDL_bool is_steam_deck;
     SDL_bool steam_keyboard_open;
 
+    SDL_bool is_xwayland;
+
 };
 
 extern SDL_bool X11_UseDirectColorVisuals(void);