SDL: Fixed interactions between mouse capture and grab on X11

From 8acb4e45b3ab55842d4214ce1726afdfae51eb28 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Wed, 17 Aug 2022 14:25:30 -0700
Subject: [PATCH] Fixed interactions between mouse capture and grab on X11

Fixes https://github.com/libsdl-org/SDL/issues/6072
---
 src/video/x11/SDL_x11mouse.c  | 6 +++++-
 src/video/x11/SDL_x11window.c | 2 ++
 src/video/x11/SDL_x11window.h | 1 +
 3 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c
index 4fb0afa1a9d..4fd8fa9f708 100644
--- a/src/video/x11/SDL_x11mouse.c
+++ b/src/video/x11/SDL_x11mouse.c
@@ -358,16 +358,20 @@ static int
 X11_CaptureMouse(SDL_Window *window)
 {
     Display *display = GetDisplay();
+    SDL_Window *mouse_focus = SDL_GetMouseFocus();
 
     if (window) {
         SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
         const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
+        Window confined = (data->mouse_grabbed ? data->xwindow : None);
         const int rc = X11_XGrabPointer(display, data->xwindow, False,
                                         mask, GrabModeAsync, GrabModeAsync,
-                                        None, None, CurrentTime);
+                                        confined, None, CurrentTime);
         if (rc != GrabSuccess) {
             return SDL_SetError("X server refused mouse capture");
         }
+    } else if (mouse_focus) {
+        SDL_UpdateWindowGrab(mouse_focus);
     } else {
         X11_XUngrabPointer(display, CurrentTime);
     }
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index f7ec281b984..e8fee1c1217 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -1600,6 +1600,7 @@ X11_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
     if (data == NULL) {
         return;
     }
+    data->mouse_grabbed = SDL_FALSE;
 
     display = data->videodata->display;
 
@@ -1622,6 +1623,7 @@ X11_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
                 result = X11_XGrabPointer(display, data->xwindow, True, mask, GrabModeAsync,
                                  GrabModeAsync, data->xwindow, None, CurrentTime);
                 if (result == GrabSuccess) {
+                    data->mouse_grabbed = SDL_TRUE;
                     break;
                 }
                 SDL_Delay(50);
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 6f7560afed0..c32955b261d 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -59,6 +59,7 @@ typedef struct
     int border_right;
     int border_top;
     int border_bottom;
+    SDL_bool mouse_grabbed;
     Uint32 last_focus_event_time;
     PendingFocusEnum pending_focus;
     Uint32 pending_focus_time;