Fixed bug 2260 - SDL_SetCursorGrab() is buggy on Windows
authorSam Lantinga <slouken@libsdl.org>
Wed, 27 Nov 2013 10:29:38 -0800
changeset 8036d485906bd74a
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
     1.1 --- a/src/video/windows/SDL_windowsevents.c	Wed Nov 27 10:29:32 2013 -0800
     1.2 +++ b/src/video/windows/SDL_windowsevents.c	Wed Nov 27 10:29:38 2013 -0800
     1.3 @@ -286,6 +286,45 @@
     1.4      return SDL_TRUE;
     1.5  }
     1.6  
     1.7 +static void
     1.8 +WIN_UpdateClipCursor(SDL_Window *window)
     1.9 +{
    1.10 +    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    1.11 +
    1.12 +    /* Don't clip the cursor while we're in the modal resize or move loop */
    1.13 +    if (data->in_modal_loop) {
    1.14 +        ClipCursor(NULL);
    1.15 +        return;
    1.16 +    }
    1.17 +        
    1.18 +    if (SDL_GetMouse()->relative_mode) {
    1.19 +        LONG cx, cy;
    1.20 +        RECT rect;
    1.21 +        GetWindowRect(data->hwnd, &rect);
    1.22 +
    1.23 +        cx = (rect.left + rect.right) / 2;
    1.24 +        cy = (rect.top + rect.bottom) / 2;
    1.25 +
    1.26 +        /* Make an absurdly small clip rect */
    1.27 +        rect.left = cx-1;
    1.28 +        rect.right = cx+1;
    1.29 +        rect.top = cy-1;
    1.30 +        rect.bottom = cy+1;
    1.31 +
    1.32 +        ClipCursor(&rect);
    1.33 +    } else if ((window->flags & SDL_WINDOW_INPUT_GRABBED) &&
    1.34 +               (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
    1.35 +        RECT rect;
    1.36 +        if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) {
    1.37 +            ClientToScreen(data->hwnd, (LPPOINT) & rect);
    1.38 +            ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
    1.39 +            ClipCursor(&rect);
    1.40 +        }
    1.41 +    } else {
    1.42 +        ClipCursor(NULL);
    1.43 +    }
    1.44 +}
    1.45 +
    1.46  LRESULT CALLBACK
    1.47  WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    1.48  {
    1.49 @@ -369,22 +408,7 @@
    1.50                  WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
    1.51                  data->mouse_button_flags = 0;
    1.52  
    1.53 -                if(SDL_GetMouse()->relative_mode) {
    1.54 -                    LONG cx, cy;
    1.55 -                    RECT rect;
    1.56 -                    GetWindowRect(hwnd, &rect);
    1.57 -
    1.58 -                    cx = (rect.left + rect.right) / 2;
    1.59 -                    cy = (rect.top + rect.bottom) / 2;
    1.60 -
    1.61 -                    /* Make an absurdly small clip rect */
    1.62 -                    rect.left = cx-1;
    1.63 -                    rect.right = cx+1;
    1.64 -                    rect.top = cy-1;
    1.65 -                    rect.bottom = cy+1;
    1.66 -
    1.67 -                    ClipCursor(&rect);
    1.68 -                }
    1.69 +                WIN_UpdateClipCursor(data->window);
    1.70  
    1.71                  /*
    1.72                   * FIXME: Update keyboard state
    1.73 @@ -585,6 +609,22 @@
    1.74          break;
    1.75  #endif /* WM_INPUTLANGCHANGE */
    1.76  
    1.77 +    case WM_ENTERSIZEMOVE:
    1.78 +    case WM_ENTERMENULOOP:
    1.79 +        {
    1.80 +            data->in_modal_loop = SDL_TRUE;
    1.81 +            WIN_UpdateClipCursor(data->window);
    1.82 +        }
    1.83 +        break;
    1.84 +
    1.85 +    case WM_EXITSIZEMOVE:
    1.86 +    case WM_EXITMENULOOP:
    1.87 +        {
    1.88 +            data->in_modal_loop = SDL_FALSE;
    1.89 +            WIN_UpdateClipCursor(data->window);
    1.90 +        }
    1.91 +        break;
    1.92 +
    1.93  #ifdef WM_GETMINMAXINFO
    1.94      case WM_GETMINMAXINFO:
    1.95          {
    1.96 @@ -673,20 +713,14 @@
    1.97              RECT rect;
    1.98              int x, y;
    1.99              int w, h;
   1.100 -            Uint32 window_flags;
   1.101  
   1.102 -            if (!GetClientRect(hwnd, &rect) ||
   1.103 -                (rect.right == rect.left && rect.bottom == rect.top)) {
   1.104 +            if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
   1.105                  break;
   1.106              }
   1.107              ClientToScreen(hwnd, (LPPOINT) & rect);
   1.108              ClientToScreen(hwnd, (LPPOINT) & rect + 1);
   1.109  
   1.110 -            window_flags = SDL_GetWindowFlags(data->window);
   1.111 -            if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
   1.112 -                (window_flags & SDL_WINDOW_INPUT_FOCUS)) {
   1.113 -                ClipCursor(&rect);
   1.114 -            }
   1.115 +            WIN_UpdateClipCursor(data->window);
   1.116  
   1.117              x = rect.left;
   1.118              y = rect.top;
     2.1 --- a/src/video/windows/SDL_windowsmouse.c	Wed Nov 27 10:29:32 2013 -0800
     2.2 +++ b/src/video/windows/SDL_windowsmouse.c	Wed Nov 27 10:29:38 2013 -0800
     2.3 @@ -207,7 +207,7 @@
     2.4  
     2.5  
     2.6      /* (Un)register raw input for mice */
     2.7 -    if(RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
     2.8 +    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
     2.9  
    2.10          /* Only return an error when registering. If we unregister and fail, then
    2.11          it's probably that we unregistered twice. That's OK. */
    2.12 @@ -216,7 +216,7 @@
    2.13          }
    2.14      }
    2.15  
    2.16 -    if(enabled) {
    2.17 +    if (enabled) {
    2.18          LONG cx, cy;
    2.19          RECT rect;
    2.20          GetWindowRect(hWnd, &rect);
    2.21 @@ -231,10 +231,9 @@
    2.22          rect.bottom = cy+1;
    2.23  
    2.24          ClipCursor(&rect);
    2.25 +    } else {
    2.26 +        ClipCursor(NULL);
    2.27      }
    2.28 -    else
    2.29 -        ClipCursor(NULL);
    2.30 -
    2.31      return 0;
    2.32  }
    2.33