SDL: x11: Fix pointer warp on XWayland (c39f8)

From c39f86c28a54c3f108c31a58ff610fe9508d03c4 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.

(cherry picked from commit a845c7027e68ff1c75bf814819d8e6b22499d899)
(cherry picked from commit 863d4a0f2ad22ff1460285e3084ff225e44eb49c)
---
 src/video/x11/SDL_x11mouse.c | 17 ++++++++++++++++-
 src/video/x11/SDL_x11video.c |  8 ++++++++
 src/video/x11/SDL_x11video.h |  2 ++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c
index beb694178d348..0938de99f8233 100644
--- a/src/video/x11/SDL_x11mouse.c
+++ b/src/video/x11/SDL_x11mouse.c
@@ -317,8 +317,19 @@ static void WarpMouseInternal(Window xwindow, const int x, const int y)
 {
     SDL_VideoData *videodata = (SDL_VideoData *)SDL_GetVideoDevice()->driverdata;
     Display *display = videodata->display;
-#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
+    SDL_Mouse *mouse = SDL_GetMouse();
     int deviceid = 0;
+    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
     if (X11_Xinput2IsInitialized()) {
         /* It seems XIWarpPointer() doesn't work correctly on multi-head setups:
          * https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea
@@ -335,6 +346,10 @@ static void WarpMouseInternal(Window xwindow, const int x, const int y)
     {
         X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, x, 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 a9d7abe95d745..6a543c186c9be 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -145,6 +145,12 @@ static int X11_SafetyNetErrHandler(Display *d, XErrorEvent *e)
     return 0;
 }
 
+static SDL_bool X11_IsXWayland(Display *d)
+{
+    int opcode, event, error;
+    return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True;
+}
+
 static SDL_VideoDevice *X11_CreateDevice(void)
 {
     SDL_VideoDevice *device;
@@ -322,6 +328,8 @@ static SDL_VideoDevice *X11_CreateDevice(void)
     device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface;
 #endif
 
+    data->is_xwayland = X11_IsXWayland(x11_display);
+
     return device;
 }
 
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index 07cb01e3488e6..a12a34b968587 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -156,6 +156,8 @@ typedef struct SDL_VideoData
     SDL_bool is_steam_deck;
     SDL_bool steam_keyboard_open;
 
+    SDL_bool is_xwayland;
+
 } SDL_VideoData;
 
 extern SDL_bool X11_UseDirectColorVisuals(void);