Fixed up SDL_CaptureMouse() on Windows to work like I expected.
authorRyan C. Gordon <icculus@icculus.org>
Fri, 30 May 2014 01:51:13 -0400
changeset 89423d84839c97b2
parent 8941 9229a54e4952
child 8943 a743e8b89e72
Fixed up SDL_CaptureMouse() on Windows to work like I expected.

This would have been a one-line patch to the documentation (specifying that
captures only work as long as the left mouse button is pressed), but I didn't
like that, so I got a little crazy about this instead.
src/video/windows/SDL_windowsevents.c
src/video/windows/SDL_windowsmouse.c
     1.1 --- a/src/video/windows/SDL_windowsevents.c	Fri May 30 01:49:26 2014 -0400
     1.2 +++ b/src/video/windows/SDL_windowsevents.c	Fri May 30 01:51:13 2014 -0400
     1.3 @@ -30,6 +30,7 @@
     1.4  #include "../../events/SDL_events_c.h"
     1.5  #include "../../events/SDL_touch_c.h"
     1.6  #include "../../events/scancodes_windows.h"
     1.7 +#include "SDL_assert.h"
     1.8  
     1.9  /* Dropfile support */
    1.10  #include <shellapi.h>
    1.11 @@ -428,33 +429,55 @@
    1.12              HRAWINPUT hRawInput = (HRAWINPUT)lParam;
    1.13              RAWINPUT inp;
    1.14              UINT size = sizeof(inp);
    1.15 +            const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
    1.16 +            const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
    1.17  
    1.18 -            if (!mouse->relative_mode || mouse->relative_mode_warp || mouse->focus != data->window) {
    1.19 -                break;
    1.20 +            if (!isRelative || mouse->focus != data->window) {
    1.21 +                if (!isCapture) {
    1.22 +                    break;
    1.23 +                }
    1.24              }
    1.25  
    1.26              GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
    1.27  
    1.28              /* Mouse data */
    1.29              if (inp.header.dwType == RIM_TYPEMOUSE) {
    1.30 -                RAWMOUSE* mouse = &inp.data.mouse;
    1.31 +                if (isRelative) {
    1.32 +                    RAWMOUSE* mouse = &inp.data.mouse;
    1.33  
    1.34 -                if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
    1.35 -                    SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
    1.36 -                } else {
    1.37 -                    /* synthesize relative moves from the abs position */
    1.38 -                    static SDL_Point initialMousePoint;
    1.39 -                    if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
    1.40 +                    if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
    1.41 +                        SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
    1.42 +                    } else {
    1.43 +                        /* synthesize relative moves from the abs position */
    1.44 +                        static SDL_Point initialMousePoint;
    1.45 +                        if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
    1.46 +                            initialMousePoint.x = mouse->lLastX;
    1.47 +                            initialMousePoint.y = mouse->lLastY;
    1.48 +                        }
    1.49 +
    1.50 +                        SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) );
    1.51 +
    1.52                          initialMousePoint.x = mouse->lLastX;
    1.53                          initialMousePoint.y = mouse->lLastY;
    1.54                      }
    1.55 -
    1.56 -                    SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) );
    1.57 -
    1.58 -                    initialMousePoint.x = mouse->lLastX;
    1.59 -                    initialMousePoint.y = mouse->lLastY;
    1.60 +                    WIN_CheckRawMouseButtons( mouse->usButtonFlags, data );
    1.61 +                } else if (isCapture) {
    1.62 +                    /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
    1.63 +                    POINT pt;
    1.64 +                    HWND hwnd = data->hwnd;
    1.65 +                    GetCursorPos(&pt);
    1.66 +                    if (WindowFromPoint(pt) != hwnd) {  /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
    1.67 +                        ScreenToClient(data->hwnd, &pt);
    1.68 +                        SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
    1.69 +                        SDL_SendMouseButton(data->window, 0, GetKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
    1.70 +                        SDL_SendMouseButton(data->window, 0, GetKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
    1.71 +                        SDL_SendMouseButton(data->window, 0, GetKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
    1.72 +                        SDL_SendMouseButton(data->window, 0, GetKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
    1.73 +                        SDL_SendMouseButton(data->window, 0, GetKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
    1.74 +                    }
    1.75 +                } else {
    1.76 +                    SDL_assert(0 && "Shouldn't happen");
    1.77                  }
    1.78 -                WIN_CheckRawMouseButtons( mouse->usButtonFlags, data );
    1.79              }
    1.80          }
    1.81          break;
    1.82 @@ -499,7 +522,7 @@
    1.83  
    1.84  #ifdef WM_MOUSELEAVE
    1.85      case WM_MOUSELEAVE:
    1.86 -        if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode) {
    1.87 +        if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
    1.88              if (!IsIconic(hwnd)) {
    1.89                  POINT cursorPos;
    1.90                  GetCursorPos(&cursorPos);
     2.1 --- a/src/video/windows/SDL_windowsmouse.c	Fri May 30 01:49:26 2014 -0400
     2.2 +++ b/src/video/windows/SDL_windowsmouse.c	Fri May 30 01:51:13 2014 -0400
     2.3 @@ -30,6 +30,44 @@
     2.4  
     2.5  HCURSOR SDL_cursor = NULL;
     2.6  
     2.7 +static int rawInputEnableCount = 0;
     2.8 +
     2.9 +static int 
    2.10 +ToggleRawInput(SDL_bool enabled)
    2.11 +{
    2.12 +    RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
    2.13 +
    2.14 +    if (enabled) {
    2.15 +        rawInputEnableCount++;
    2.16 +        if (rawInputEnableCount > 1) {
    2.17 +            return 0;  /* already done. */
    2.18 +        }
    2.19 +    } else {
    2.20 +        if (rawInputEnableCount == 0) {
    2.21 +            return 0;  /* already done. */
    2.22 +        }
    2.23 +        rawInputEnableCount--;
    2.24 +        if (rawInputEnableCount > 0) {
    2.25 +            return 0;  /* not time to disable yet */
    2.26 +        }
    2.27 +    }
    2.28 +
    2.29 +    if (!enabled) {
    2.30 +        rawMouse.dwFlags |= RIDEV_REMOVE;
    2.31 +    }
    2.32 +
    2.33 +    /* (Un)register raw input for mice */
    2.34 +    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
    2.35 +
    2.36 +        /* Only return an error when registering. If we unregister and fail,
    2.37 +           then it's probably that we unregistered twice. That's OK. */
    2.38 +        if (enabled) {
    2.39 +            return SDL_Unsupported();
    2.40 +        }
    2.41 +    }
    2.42 +    return 0;
    2.43 +}
    2.44 +
    2.45  
    2.46  static SDL_Cursor *
    2.47  WIN_CreateDefaultCursor()
    2.48 @@ -201,35 +239,25 @@
    2.49  static int
    2.50  WIN_SetRelativeMouseMode(SDL_bool enabled)
    2.51  {
    2.52 -    RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
    2.53 -
    2.54 -    if (!enabled) {
    2.55 -        rawMouse.dwFlags |= RIDEV_REMOVE;
    2.56 -    }
    2.57 -
    2.58 -    /* (Un)register raw input for mice */
    2.59 -    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
    2.60 -
    2.61 -        /* Only return an error when registering. If we unregister and fail,
    2.62 -           then it's probably that we unregistered twice. That's OK. */
    2.63 -        if (enabled) {
    2.64 -            return SDL_Unsupported();
    2.65 -        }
    2.66 -    }
    2.67 -    return 0;
    2.68 +    return ToggleRawInput(enabled);
    2.69  }
    2.70  
    2.71  static int
    2.72  WIN_CaptureMouse(SDL_Window *window)
    2.73  {
    2.74      if (!window) {
    2.75 -        ReleaseCapture();
    2.76 -    } else {
    2.77 -        const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    2.78 -        SetCapture(data->hwnd);
    2.79 +        SDL_Window *focusWin = SDL_GetKeyboardFocus();
    2.80 +        if (focusWin) {
    2.81 +            SDL_WindowData *data = (SDL_WindowData *)focusWin->driverdata;
    2.82 +            WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin);  /* make sure WM_MOUSELEAVE messages are (re)enabled. */
    2.83 +        }
    2.84      }
    2.85  
    2.86 -    return 0;
    2.87 +    /* While we were thinking of SetCapture() when designing this API in SDL,
    2.88 +       we didn't count on the fact that SetCapture() only tracks while the
    2.89 +       left mouse button is held down! Instead, we listen for raw mouse input
    2.90 +       and manually query the mouse when it leaves the window. :/ */
    2.91 +    return ToggleRawInput(window != NULL);
    2.92  }
    2.93  
    2.94  void
    2.95 @@ -259,6 +287,11 @@
    2.96          mouse->def_cursor = NULL;
    2.97          mouse->cur_cursor = NULL;
    2.98      }
    2.99 +
   2.100 +    if (rawInputEnableCount) {  /* force RAWINPUT off here. */
   2.101 +        rawInputEnableCount = 1;
   2.102 +        ToggleRawInput(SDL_FALSE);
   2.103 +    }
   2.104  }
   2.105  
   2.106  #endif /* SDL_VIDEO_DRIVER_WINDOWS */