Implemented SDL_CaptureMouse().
authorRyan C. Gordon <icculus@icculus.org>
Sat, 24 May 2014 01:30:37 -0400
changeset 8927be64f5daf64b
parent 8783 400f1d2b9e52
child 8928 f60f16d29e37
Implemented SDL_CaptureMouse().
include/SDL_mouse.h
include/SDL_video.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
src/events/SDL_keyboard.c
src/events/SDL_mouse.c
src/events/SDL_mouse_c.h
src/test/SDL_test_common.c
src/video/SDL_video.c
src/video/windows/SDL_windowsmouse.c
src/video/x11/SDL_x11mouse.c
     1.1 --- a/include/SDL_mouse.h	Sat May 24 01:27:19 2014 -0400
     1.2 +++ b/include/SDL_mouse.h	Sat May 24 01:30:37 2014 -0400
     1.3 @@ -117,6 +117,37 @@
     1.4  extern DECLSPEC int SDLCALL SDL_SetRelativeMouseMode(SDL_bool enabled);
     1.5  
     1.6  /**
     1.7 + *  \brief Capture the mouse, to track input outside an SDL window.
     1.8 + *
     1.9 + *  \param enabled Whether or not to enable capturing
    1.10 + *
    1.11 + *  Capturing enables your app to obtain mouse events globally, instead of
    1.12 + *  just within your window. Not all video targets support this function.
    1.13 + *  When capturing is enabled, the current window will get all mouse events,
    1.14 + *  but unlike relative mode, no change is made to the cursor and it is
    1.15 + *  not restrained to your window.
    1.16 + *
    1.17 + *  This function may also deny mouse input to other windows--both those in
    1.18 + *  your application and others on the system--so you should use this
    1.19 + *  function sparingly, and in small bursts. For example, you might want to
    1.20 + *  track the mouse while the user is dragging something, until the user
    1.21 + *  releases a mouse button. It is not recommended that you capture the mouse
    1.22 + *  for long periods of time, such as the entire time your app is running.
    1.23 + *
    1.24 + *  While captured, mouse events still report coordinates relative to the
    1.25 + *  current (foreground) window, but those coordinates may be outside the
    1.26 + *  bounds of the window (including negative values). Capturing is only
    1.27 + *  allowed for the foreground window. If the window loses focus while
    1.28 + *  capturing, the capture will be disabled automatically.
    1.29 + *
    1.30 + *  While capturing is enabled, the current window will have the
    1.31 + *  SDL_WINDOW_MOUSE_CAPTURE flag set.
    1.32 + *
    1.33 + *  \return 0 on success, or -1 if not supported.
    1.34 + */
    1.35 +extern DECLSPEC int SDLCALL SDL_CaptureMouse(SDL_bool enabled);
    1.36 +
    1.37 +/**
    1.38   *  \brief Query whether relative mouse mode is enabled.
    1.39   *
    1.40   *  \sa SDL_SetRelativeMouseMode()
     2.1 --- a/include/SDL_video.h	Sat May 24 01:27:19 2014 -0400
     2.2 +++ b/include/SDL_video.h	Sat May 24 01:30:37 2014 -0400
     2.3 @@ -108,7 +108,8 @@
     2.4      SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
     2.5      SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
     2.6      SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
     2.7 -    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000       /**< window should be created in high-DPI mode if supported */
     2.8 +    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported */
     2.9 +    SDL_WINDOW_MOUSE_CAPTURE = 0x00004000       /**< window has mouse captured (unrelated to INPUT_GRABBED) */
    2.10  } SDL_WindowFlags;
    2.11  
    2.12  /**
     3.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Sat May 24 01:27:19 2014 -0400
     3.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Sat May 24 01:30:37 2014 -0400
     3.3 @@ -579,3 +579,4 @@
     3.4  #define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL
     3.5  #define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
     3.6  #define SDL_WinRTRunApp SDL_WinRTRunApp_REAL
     3.7 +#define SDL_CaptureMouse SDL_CaptureMouse_REAL
     4.1 --- a/src/dynapi/SDL_dynapi_procs.h	Sat May 24 01:27:19 2014 -0400
     4.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Sat May 24 01:30:37 2014 -0400
     4.3 @@ -612,3 +612,4 @@
     4.4  SDL_DYNAPI_PROC(const char*,SDL_WinRTGetFSPathUTF8,(SDL_WinRT_Path a),(a),return)
     4.5  SDL_DYNAPI_PROC(int,SDL_WinRTRunApp,(int a, char **b, void *c),(a,b,c),return)
     4.6  #endif
     4.7 +SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
     5.1 --- a/src/events/SDL_keyboard.c	Sat May 24 01:27:19 2014 -0400
     5.2 +++ b/src/events/SDL_keyboard.c	Sat May 24 01:30:37 2014 -0400
     5.3 @@ -25,6 +25,7 @@
     5.4  #include "SDL_timer.h"
     5.5  #include "SDL_events.h"
     5.6  #include "SDL_events_c.h"
     5.7 +#include "SDL_assert.h"
     5.8  #include "../video/SDL_sysvideo.h"
     5.9  
    5.10  
    5.11 @@ -619,6 +620,16 @@
    5.12  
    5.13      /* See if the current window has lost focus */
    5.14      if (keyboard->focus && keyboard->focus != window) {
    5.15 +
    5.16 +        /* new window shouldn't think it has mouse captured. */
    5.17 +        SDL_assert(!window || !(window->flags & SDL_WINDOW_MOUSE_CAPTURE));
    5.18 +
    5.19 +        /* old window must lose an existing mouse capture. */
    5.20 +        if (keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE) {
    5.21 +            SDL_CaptureMouse(SDL_FALSE);  /* drop the capture. */
    5.22 +            SDL_assert(!(keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE));
    5.23 +        }
    5.24 +
    5.25          SDL_SendWindowEvent(keyboard->focus, SDL_WINDOWEVENT_FOCUS_LOST,
    5.26                              0, 0);
    5.27  
     6.1 --- a/src/events/SDL_mouse.c	Sat May 24 01:27:19 2014 -0400
     6.2 +++ b/src/events/SDL_mouse.c	Sat May 24 01:30:37 2014 -0400
     6.3 @@ -140,30 +140,17 @@
     6.4  SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
     6.5  {
     6.6      SDL_Mouse *mouse = SDL_GetMouse();
     6.7 -    int w, h;
     6.8 -    SDL_bool inWindow;
     6.9 +    SDL_bool inWindow = SDL_TRUE;
    6.10  
    6.11 -    SDL_GetWindowSize(window, &w, &h);
    6.12 -    if (x < 0 || y < 0 || x >= w || y >= h) {
    6.13 -        inWindow = SDL_FALSE;
    6.14 -    } else {
    6.15 -        inWindow = SDL_TRUE;
    6.16 +    if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
    6.17 +        int w, h;
    6.18 +        SDL_GetWindowSize(window, &w, &h);
    6.19 +        if (x < 0 || y < 0 || x >= w || y >= h) {
    6.20 +            inWindow = SDL_FALSE;
    6.21 +        }
    6.22      }
    6.23  
    6.24 -/* Linux doesn't give you mouse events outside your window unless you grab
    6.25 -   the pointer.
    6.26 -
    6.27 -   Windows doesn't give you mouse events outside your window unless you call
    6.28 -   SetCapture().
    6.29 -
    6.30 -   Both of these are slightly scary changes, so for now we'll punt and if the
    6.31 -   mouse leaves the window you'll lose mouse focus and reset button state.
    6.32 -*/
    6.33 -#ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
    6.34 -    if (!inWindow && !buttonstate) {
    6.35 -#else
    6.36      if (!inWindow) {
    6.37 -#endif
    6.38          if (window == mouse->focus) {
    6.39  #ifdef DEBUG_MOUSE
    6.40              printf("Mouse left window, synthesizing move & focus lost event\n");
    6.41 @@ -204,7 +191,6 @@
    6.42      int posted;
    6.43      int xrel;
    6.44      int yrel;
    6.45 -    int x_max = 0, y_max = 0;
    6.46  
    6.47      if (mouse->relative_mode_warp) {
    6.48          int center_x = 0, center_y = 0;
    6.49 @@ -246,24 +232,29 @@
    6.50          mouse->y += yrel;
    6.51      }
    6.52  
    6.53 -    /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */
    6.54 -    SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
    6.55 -    --x_max;
    6.56 -    --y_max;
    6.57 +    /* make sure that the pointers find themselves inside the windows,
    6.58 +       unless we have the mouse captured. */
    6.59 +    if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
    6.60 +        int x_max = 0, y_max = 0;
    6.61  
    6.62 -    /* make sure that the pointers find themselves inside the windows */
    6.63 -    if (mouse->x > x_max) {
    6.64 -        mouse->x = x_max;
    6.65 -    }
    6.66 -    if (mouse->x < 0) {
    6.67 -        mouse->x = 0;
    6.68 -    }
    6.69 +        // !!! FIXME: shouldn't this be (window) instead of (mouse->focus)?
    6.70 +        SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
    6.71 +        --x_max;
    6.72 +        --y_max;
    6.73  
    6.74 -    if (mouse->y > y_max) {
    6.75 -        mouse->y = y_max;
    6.76 -    }
    6.77 -    if (mouse->y < 0) {
    6.78 -        mouse->y = 0;
    6.79 +        if (mouse->x > x_max) {
    6.80 +            mouse->x = x_max;
    6.81 +        }
    6.82 +        if (mouse->x < 0) {
    6.83 +            mouse->x = 0;
    6.84 +        }
    6.85 +
    6.86 +        if (mouse->y > y_max) {
    6.87 +            mouse->y = y_max;
    6.88 +        }
    6.89 +        if (mouse->y < 0) {
    6.90 +            mouse->y = 0;
    6.91 +        }
    6.92      }
    6.93  
    6.94      mouse->xdelta += xrel;
    6.95 @@ -426,6 +417,7 @@
    6.96      SDL_Cursor *cursor, *next;
    6.97      SDL_Mouse *mouse = SDL_GetMouse();
    6.98  
    6.99 +    SDL_CaptureMouse(SDL_FALSE);
   6.100      SDL_SetRelativeMouseMode(SDL_FALSE);
   6.101      SDL_ShowCursor(1);
   6.102  
   6.103 @@ -572,6 +564,42 @@
   6.104      return mouse->relative_mode;
   6.105  }
   6.106  
   6.107 +int
   6.108 +SDL_CaptureMouse(SDL_bool enabled)
   6.109 +{
   6.110 +    SDL_Mouse *mouse = SDL_GetMouse();
   6.111 +    SDL_Window *focusWindow;
   6.112 +    SDL_bool isCaptured;
   6.113 +
   6.114 +    if (!mouse->CaptureMouse) {
   6.115 +        return SDL_Unsupported();
   6.116 +    }
   6.117 +
   6.118 +    focusWindow = SDL_GetKeyboardFocus();
   6.119 +
   6.120 +    isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
   6.121 +    if (isCaptured == enabled) {
   6.122 +        return 0;  /* already done! */
   6.123 +    }
   6.124 +
   6.125 +    if (enabled) {
   6.126 +        if (!focusWindow) {
   6.127 +            return SDL_SetError("No window has focus");
   6.128 +        } else if (mouse->CaptureMouse(focusWindow) == -1) {
   6.129 +            return -1;  /* CaptureMouse() should call SetError */
   6.130 +        }
   6.131 +        focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
   6.132 +    } else {
   6.133 +        if (mouse->CaptureMouse(NULL) == -1) {
   6.134 +            return -1;  /* CaptureMouse() should call SetError */
   6.135 +        }
   6.136 +        focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
   6.137 +    }
   6.138 +
   6.139 +    return 0;
   6.140 +}
   6.141 +
   6.142 +
   6.143  SDL_Cursor *
   6.144  SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
   6.145                   int w, int h, int hot_x, int hot_y)
     7.1 --- a/src/events/SDL_mouse_c.h	Sat May 24 01:27:19 2014 -0400
     7.2 +++ b/src/events/SDL_mouse_c.h	Sat May 24 01:30:37 2014 -0400
     7.3 @@ -63,6 +63,9 @@
     7.4      /* Set relative mode */
     7.5      int (*SetRelativeMouseMode) (SDL_bool enabled);
     7.6  
     7.7 +    /* Set mouse capture */
     7.8 +    int (*CaptureMouse) (SDL_Window * window);
     7.9 +
    7.10      /* Data common to all mice */
    7.11      SDL_MouseID mouseID;
    7.12      SDL_Window *focus;
     8.1 --- a/src/test/SDL_test_common.c	Sat May 24 01:27:19 2014 -0400
     8.2 +++ b/src/test/SDL_test_common.c	Sat May 24 01:30:37 2014 -0400
     8.3 @@ -999,10 +999,12 @@
     8.4  static void
     8.5  SDLTest_PrintEvent(SDL_Event * event)
     8.6  {
     8.7 +#if 0
     8.8      if ((event->type == SDL_MOUSEMOTION) || (event->type == SDL_FINGERMOTION)) {
     8.9          /* Mouse and finger motion are really spammy */
    8.10          return;
    8.11      }
    8.12 +#endif
    8.13  
    8.14      switch (event->type) {
    8.15      case SDL_WINDOWEVENT:
    8.16 @@ -1379,6 +1381,14 @@
    8.17                      }
    8.18                  }
    8.19              }
    8.20 +            if (withShift) {
    8.21 +                SDL_Window *current_win = SDL_GetKeyboardFocus();
    8.22 +                if (current_win) {
    8.23 +                    const SDL_bool shouldCapture = (SDL_GetWindowFlags(current_win) & SDL_WINDOW_MOUSE_CAPTURE) == 0;
    8.24 +                    const int rc = SDL_CaptureMouse(shouldCapture);
    8.25 +                    printf("%sapturing mouse %s!\n", shouldCapture ? "C" : "Unc", (rc == 0) ? "succeeded" : "failed");
    8.26 +                }
    8.27 +            }
    8.28              break;
    8.29          case SDLK_v:
    8.30              if (withControl) {
     9.1 --- a/src/video/SDL_video.c	Sat May 24 01:27:19 2014 -0400
     9.2 +++ b/src/video/SDL_video.c	Sat May 24 01:30:37 2014 -0400
     9.3 @@ -3269,12 +3269,17 @@
     9.4      int retval = -1;
     9.5      SDL_bool relative_mode;
     9.6      int show_cursor_prev;
     9.7 +    SDL_bool mouse_captured;
     9.8 +    SDL_Window *current_window;
     9.9  
    9.10      if (!messageboxdata) {
    9.11          return SDL_InvalidParamError("messageboxdata");
    9.12      }
    9.13  
    9.14 +    current_window = SDL_GetKeyboardFocus();
    9.15 +    mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0);
    9.16      relative_mode = SDL_GetRelativeMouseMode();
    9.17 +    SDL_CaptureMouse(SDL_FALSE);
    9.18      SDL_SetRelativeMouseMode(SDL_FALSE);
    9.19      show_cursor_prev = SDL_ShowCursor(1);
    9.20  
    9.21 @@ -3326,6 +3331,13 @@
    9.22          SDL_SetError("No message system available");
    9.23      }
    9.24  
    9.25 +    if (current_window) {
    9.26 +        SDL_RaiseWindow(current_window);
    9.27 +        if (mouse_captured) {
    9.28 +            SDL_CaptureMouse(SDL_TRUE);
    9.29 +        }
    9.30 +    }
    9.31 +
    9.32      SDL_ShowCursor(show_cursor_prev);
    9.33      SDL_SetRelativeMouseMode(relative_mode);
    9.34  
    10.1 --- a/src/video/windows/SDL_windowsmouse.c	Sat May 24 01:27:19 2014 -0400
    10.2 +++ b/src/video/windows/SDL_windowsmouse.c	Sat May 24 01:30:37 2014 -0400
    10.3 @@ -219,6 +219,19 @@
    10.4      return 0;
    10.5  }
    10.6  
    10.7 +static int
    10.8 +WIN_CaptureMouse(SDL_Window *window)
    10.9 +{
   10.10 +    if (!window) {
   10.11 +        ReleaseCapture();
   10.12 +    } else {
   10.13 +        const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   10.14 +        SetCapture(data->hwnd);
   10.15 +    }
   10.16 +
   10.17 +    return 0;
   10.18 +}
   10.19 +
   10.20  void
   10.21  WIN_InitMouse(_THIS)
   10.22  {
   10.23 @@ -230,6 +243,7 @@
   10.24      mouse->FreeCursor = WIN_FreeCursor;
   10.25      mouse->WarpMouse = WIN_WarpMouse;
   10.26      mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
   10.27 +    mouse->CaptureMouse = WIN_CaptureMouse;
   10.28  
   10.29      SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
   10.30  
    11.1 --- a/src/video/x11/SDL_x11mouse.c	Sat May 24 01:27:19 2014 -0400
    11.2 +++ b/src/video/x11/SDL_x11mouse.c	Sat May 24 01:30:37 2014 -0400
    11.3 @@ -330,6 +330,29 @@
    11.4      return -1;
    11.5  }
    11.6  
    11.7 +static int
    11.8 +X11_CaptureMouse(SDL_Window *window)
    11.9 +{
   11.10 +    Display *display = GetDisplay();
   11.11 +
   11.12 +    if (window) {
   11.13 +        SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   11.14 +        const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
   11.15 +        const int rc = X11_XGrabPointer(display, data->xwindow, False,
   11.16 +                                        mask, GrabModeAsync, GrabModeAsync,
   11.17 +                                        None, None, CurrentTime);
   11.18 +        if (rc != GrabSuccess) {
   11.19 +            return SDL_SetError("X server refused mouse capture");
   11.20 +        }
   11.21 +    } else {
   11.22 +        X11_XUngrabPointer(display, CurrentTime);
   11.23 +    }
   11.24 +
   11.25 +    X11_XSync(display, False);
   11.26 +
   11.27 +    return 0;
   11.28 +}
   11.29 +
   11.30  void
   11.31  X11_InitMouse(_THIS)
   11.32  {
   11.33 @@ -341,6 +364,7 @@
   11.34      mouse->FreeCursor = X11_FreeCursor;
   11.35      mouse->WarpMouse = X11_WarpMouse;
   11.36      mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
   11.37 +    mouse->CaptureMouse = X11_CaptureMouse;
   11.38  
   11.39      SDL_SetDefaultCursor(X11_CreateDefaultCursor());
   11.40  }