Fixed bug 2260 - SDL_SetCursorGrab() is buggy on Windows
authorSam Lantinga <slouken@libsdl.org>
Wed, 27 Nov 2013 10:29:38 -0800
changeset 8036 d485906bd74a
parent 8035 2cc220c7e899
child 8037 61f20fd1d1bf
Fixed bug 2260 - SDL_SetCursorGrab() is buggy on Windows BurnSpamAddress Steps to reproduce: 1. Grab the cursor with SDL_SetCursorGrab() 2. Alt-tab away from the window 3. Click on the titlebar of the window This will cause the window to disappear underneath the taskbar! This appears to be a general issue with ClipCursor() on windows, i.e. I am getting the same behavior if I call ClipCursor() directly. It is caused by a feedback loop between the ClipCursor function and the modal resize/move event loop that handles mouse-based sizing on Windows.
src/video/windows/SDL_windowsevents.c
src/video/windows/SDL_windowsmouse.c
--- a/src/video/windows/SDL_windowsevents.c	Wed Nov 27 10:29:32 2013 -0800
+++ b/src/video/windows/SDL_windowsevents.c	Wed Nov 27 10:29:38 2013 -0800
@@ -286,6 +286,45 @@
     return SDL_TRUE;
 }
 
+static void
+WIN_UpdateClipCursor(SDL_Window *window)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+
+    /* Don't clip the cursor while we're in the modal resize or move loop */
+    if (data->in_modal_loop) {
+        ClipCursor(NULL);
+        return;
+    }
+        
+    if (SDL_GetMouse()->relative_mode) {
+        LONG cx, cy;
+        RECT rect;
+        GetWindowRect(data->hwnd, &rect);
+
+        cx = (rect.left + rect.right) / 2;
+        cy = (rect.top + rect.bottom) / 2;
+
+        /* Make an absurdly small clip rect */
+        rect.left = cx-1;
+        rect.right = cx+1;
+        rect.top = cy-1;
+        rect.bottom = cy+1;
+
+        ClipCursor(&rect);
+    } else if ((window->flags & SDL_WINDOW_INPUT_GRABBED) &&
+               (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
+        RECT rect;
+        if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
+            ClientToScreen(data->hwnd, (LPPOINT) & rect);
+            ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
+            ClipCursor(&rect);
+        }
+    } else {
+        ClipCursor(NULL);
+    }
+}
+
 LRESULT CALLBACK
 WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
@@ -369,22 +408,7 @@
                 WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
                 data->mouse_button_flags = 0;
 
-                if(SDL_GetMouse()->relative_mode) {
-                    LONG cx, cy;
-                    RECT rect;
-                    GetWindowRect(hwnd, &rect);
-
-                    cx = (rect.left + rect.right) / 2;
-                    cy = (rect.top + rect.bottom) / 2;
-
-                    /* Make an absurdly small clip rect */
-                    rect.left = cx-1;
-                    rect.right = cx+1;
-                    rect.top = cy-1;
-                    rect.bottom = cy+1;
-
-                    ClipCursor(&rect);
-                }
+                WIN_UpdateClipCursor(data->window);
 
                 /*
                  * FIXME: Update keyboard state
@@ -585,6 +609,22 @@
         break;
 #endif /* WM_INPUTLANGCHANGE */
 
+    case WM_ENTERSIZEMOVE:
+    case WM_ENTERMENULOOP:
+        {
+            data->in_modal_loop = SDL_TRUE;
+            WIN_UpdateClipCursor(data->window);
+        }
+        break;
+
+    case WM_EXITSIZEMOVE:
+    case WM_EXITMENULOOP:
+        {
+            data->in_modal_loop = SDL_FALSE;
+            WIN_UpdateClipCursor(data->window);
+        }
+        break;
+
 #ifdef WM_GETMINMAXINFO
     case WM_GETMINMAXINFO:
         {
@@ -673,20 +713,14 @@
             RECT rect;
             int x, y;
             int w, h;
-            Uint32 window_flags;
 
-            if (!GetClientRect(hwnd, &rect) ||
-                (rect.right == rect.left && rect.bottom == rect.top)) {
+            if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
                 break;
             }
             ClientToScreen(hwnd, (LPPOINT) & rect);
             ClientToScreen(hwnd, (LPPOINT) & rect + 1);
 
-            window_flags = SDL_GetWindowFlags(data->window);
-            if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
-                (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
-                ClipCursor(&rect);
-            }
+            WIN_UpdateClipCursor(data->window);
 
             x = rect.left;
             y = rect.top;
--- a/src/video/windows/SDL_windowsmouse.c	Wed Nov 27 10:29:32 2013 -0800
+++ b/src/video/windows/SDL_windowsmouse.c	Wed Nov 27 10:29:38 2013 -0800
@@ -207,7 +207,7 @@
 
 
     /* (Un)register raw input for mice */
-    if(RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
+    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
 
         /* Only return an error when registering. If we unregister and fail, then
         it's probably that we unregistered twice. That's OK. */
@@ -216,7 +216,7 @@
         }
     }
 
-    if(enabled) {
+    if (enabled) {
         LONG cx, cy;
         RECT rect;
         GetWindowRect(hWnd, &rect);
@@ -231,10 +231,9 @@
         rect.bottom = cy+1;
 
         ClipCursor(&rect);
+    } else {
+        ClipCursor(NULL);
     }
-    else
-        ClipCursor(NULL);
-
     return 0;
 }