Merged Ryan's SDL-gui-backend branch.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 25 Jun 2014 17:06:12 -0400
changeset 8953dc80dc0bd22e
parent 8926 38e89ab78465
parent 8952 4bb098814ec4
child 8954 7bcc8257809d
Merged Ryan's SDL-gui-backend branch.

Adds three APIs, and implements them on X11, Cocoa, and Windows:

- SDL_CaptureMouse()
- SDL_GetGlobalMouseState()
- SDL_SetWindowHitTest()
.hgignore
include/SDL_mouse.h
src/dynapi/SDL_dynapi_overrides.h
src/dynapi/SDL_dynapi_procs.h
src/events/SDL_mouse.c
src/events/SDL_mouse_c.h
src/test/SDL_test_common.c
src/video/SDL_sysvideo.h
src/video/SDL_video.c
src/video/cocoa/SDL_cocoamouse.m
src/video/cocoa/SDL_cocoawindow.m
src/video/windows/SDL_windowsevents.c
src/video/windows/SDL_windowsmouse.c
src/video/windows/SDL_windowsvideo.c
src/video/windows/SDL_windowswindow.c
src/video/windows/SDL_windowswindow.h
src/video/x11/SDL_x11events.c
src/video/x11/SDL_x11mouse.c
src/video/x11/SDL_x11video.c
src/video/x11/SDL_x11window.c
     1.1 --- a/.hgignore	Wed Jun 25 02:08:37 2014 -0700
     1.2 +++ b/.hgignore	Wed Jun 25 17:06:12 2014 -0400
     1.3 @@ -84,6 +84,7 @@
     1.4  test/testgl2
     1.5  test/testgles
     1.6  test/testhaptic
     1.7 +test/testhittesting
     1.8  test/testiconv
     1.9  test/testime
    1.10  test/testintersections
     2.1 --- a/include/SDL_mouse.h	Wed Jun 25 02:08:37 2014 -0700
     2.2 +++ b/include/SDL_mouse.h	Wed Jun 25 17:06:12 2014 -0400
     2.3 @@ -78,6 +78,31 @@
     2.4  extern DECLSPEC Uint32 SDLCALL SDL_GetMouseState(int *x, int *y);
     2.5  
     2.6  /**
     2.7 + *  \brief Get the current state of the mouse, in relation to the desktop
     2.8 + *
     2.9 + *  This works just like SDL_GetMouseState(), but the coordinates will be
    2.10 + *  reported relative to the top-left of the desktop. This can be useful if
    2.11 + *  you need to track the mouse outside of a specific window and
    2.12 + *  SDL_CaptureMouse() doesn't fit your needs. For example, it could be
    2.13 + *  useful if you need to track the mouse while dragging a window, where
    2.14 + *  coordinates relative to a window might not be in sync at all times.
    2.15 + *
    2.16 + *  \note SDL_GetMouseState() returns the mouse position as SDL understands
    2.17 + *        it from the last pump of the event queue. This function, however,
    2.18 + *        queries the OS for the current mouse position, and as such, might
    2.19 + *        be a slightly less efficient function. Unless you know what you're
    2.20 + *        doing and have a good reason to use this function, you probably want
    2.21 + *        SDL_GetMouseState() instead.
    2.22 + *
    2.23 + *  \param x Returns the current X coord, relative to the desktop. Can be NULL.
    2.24 + *  \param y Returns the current Y coord, relative to the desktop. Can be NULL.
    2.25 + *  \return The current button state as a bitmask, which can be tested using the SDL_BUTTON(X) macros.
    2.26 + *
    2.27 + *  \sa SDL_GetMouseState
    2.28 + */
    2.29 +extern DECLSPEC Uint32 SDLCALL SDL_GetGlobalMouseState(int *x, int *y);
    2.30 +
    2.31 +/**
    2.32   *  \brief Retrieve the relative state of the mouse.
    2.33   *
    2.34   *  The current button state is returned as a button bitmask, which can
    2.35 @@ -127,6 +152,37 @@
    2.36  extern DECLSPEC int SDLCALL SDL_SetRelativeMouseMode(SDL_bool enabled);
    2.37  
    2.38  /**
    2.39 + *  \brief Capture the mouse, to track input outside an SDL window.
    2.40 + *
    2.41 + *  \param enabled Whether or not to enable capturing
    2.42 + *
    2.43 + *  Capturing enables your app to obtain mouse events globally, instead of
    2.44 + *  just within your window. Not all video targets support this function.
    2.45 + *  When capturing is enabled, the current window will get all mouse events,
    2.46 + *  but unlike relative mode, no change is made to the cursor and it is
    2.47 + *  not restrained to your window.
    2.48 + *
    2.49 + *  This function may also deny mouse input to other windows--both those in
    2.50 + *  your application and others on the system--so you should use this
    2.51 + *  function sparingly, and in small bursts. For example, you might want to
    2.52 + *  track the mouse while the user is dragging something, until the user
    2.53 + *  releases a mouse button. It is not recommended that you capture the mouse
    2.54 + *  for long periods of time, such as the entire time your app is running.
    2.55 + *
    2.56 + *  While captured, mouse events still report coordinates relative to the
    2.57 + *  current (foreground) window, but those coordinates may be outside the
    2.58 + *  bounds of the window (including negative values). Capturing is only
    2.59 + *  allowed for the foreground window. If the window loses focus while
    2.60 + *  capturing, the capture will be disabled automatically.
    2.61 + *
    2.62 + *  While capturing is enabled, the current window will have the
    2.63 + *  SDL_WINDOW_MOUSE_CAPTURE flag set.
    2.64 + *
    2.65 + *  \return 0 on success, or -1 if not supported.
    2.66 + */
    2.67 +extern DECLSPEC int SDLCALL SDL_CaptureMouse(SDL_bool enabled);
    2.68 +
    2.69 +/**
    2.70   *  \brief Query whether relative mouse mode is enabled.
    2.71   *
    2.72   *  \sa SDL_SetRelativeMouseMode()
     3.1 --- a/include/SDL_rect.h	Wed Jun 25 02:08:37 2014 -0700
     3.2 +++ b/include/SDL_rect.h	Wed Jun 25 17:06:12 2014 -0400
     3.3 @@ -43,6 +43,7 @@
     3.4   *  \brief  The structure that defines a point
     3.5   *
     3.6   *  \sa SDL_EnclosePoints
     3.7 + *  \sa SDL_PointInRect
     3.8   */
     3.9  typedef struct SDL_Point
    3.10  {
    3.11 @@ -67,6 +68,15 @@
    3.12  } SDL_Rect;
    3.13  
    3.14  /**
    3.15 + *  \brief Returns true if point resides inside a rectangle.
    3.16 + */
    3.17 +SDL_FORCE_INLINE SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
    3.18 +{
    3.19 +    return ( (p->x >= r->x) && (p->x < (r->x + r->w)) &&
    3.20 +             (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? SDL_TRUE : SDL_FALSE;
    3.21 +}
    3.22 +
    3.23 +/**
    3.24   *  \brief Returns true if the rectangle has no area.
    3.25   */
    3.26  SDL_FORCE_INLINE SDL_bool SDL_RectEmpty(const SDL_Rect *r)
     4.1 --- a/include/SDL_video.h	Wed Jun 25 02:08:37 2014 -0700
     4.2 +++ b/include/SDL_video.h	Wed Jun 25 17:06:12 2014 -0400
     4.3 @@ -108,7 +108,8 @@
     4.4      SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
     4.5      SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
     4.6      SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
     4.7 -    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000       /**< window should be created in high-DPI mode if supported */
     4.8 +    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported */
     4.9 +    SDL_WINDOW_MOUSE_CAPTURE = 0x00004000       /**< window has mouse captured (unrelated to INPUT_GRABBED) */
    4.10  } SDL_WindowFlags;
    4.11  
    4.12  /**
    4.13 @@ -791,6 +792,75 @@
    4.14                                                     Uint16 * blue);
    4.15  
    4.16  /**
    4.17 + *  \brief Possible return values from the SDL_HitTest callback.
    4.18 + *
    4.19 + *  \sa SDL_HitTest
    4.20 + */
    4.21 +typedef enum
    4.22 +{
    4.23 +    SDL_HITTEST_NORMAL,  /**< Region is normal. No special properties. */
    4.24 +    SDL_HITTEST_DRAGGABLE,  /**< Region can drag entire window. */
    4.25 +    SDL_HITTEST_RESIZE_TOPLEFT,
    4.26 +    SDL_HITTEST_RESIZE_TOP,
    4.27 +    SDL_HITTEST_RESIZE_TOPRIGHT,
    4.28 +    SDL_HITTEST_RESIZE_RIGHT,
    4.29 +    SDL_HITTEST_RESIZE_BOTTOMRIGHT,
    4.30 +    SDL_HITTEST_RESIZE_BOTTOM,
    4.31 +    SDL_HITTEST_RESIZE_BOTTOMLEFT,
    4.32 +    SDL_HITTEST_RESIZE_LEFT
    4.33 +} SDL_HitTestResult;
    4.34 +
    4.35 +/**
    4.36 + *  \brief Callback used for hit-testing.
    4.37 + *
    4.38 + *  \sa SDL_SetWindowHitTest
    4.39 + */
    4.40 +typedef SDL_HitTestResult (SDLCALL *SDL_HitTest)(SDL_Window *win,
    4.41 +                                                 const SDL_Point *area,
    4.42 +                                                 void *data);
    4.43 +
    4.44 +/**
    4.45 + *  \brief Provide a callback that decides if a window region has special properties.
    4.46 + *
    4.47 + *  Normally windows are dragged and resized by decorations provided by the
    4.48 + *  system window manager (a title bar, borders, etc), but for some apps, it
    4.49 + *  makes sense to drag them from somewhere else inside the window itself; for
    4.50 + *  example, one might have a borderless window that wants to be draggable
    4.51 + *  from any part, or simulate its own title bar, etc.
    4.52 + *
    4.53 + *  This function lets the app provide a callback that designates pieces of
    4.54 + *  a given window as special. This callback is run during event processing
    4.55 + *  if we need to tell the OS to treat a region of the window specially; the
    4.56 + *  use of this callback is known as "hit testing."
    4.57 + *
    4.58 + *  Mouse input may not be delivered to your application if it is within
    4.59 + *  a special area; the OS will often apply that input to moving the window or
    4.60 + *  resizing the window and not deliver it to the application.
    4.61 + *
    4.62 + *  Specifying NULL for a callback disables hit-testing. Hit-testing is
    4.63 + *  disabled by default.
    4.64 + *
    4.65 + *  Platforms that don't support this functionality will return -1
    4.66 + *  unconditionally, even if you're attempting to disable hit-testing.
    4.67 + *
    4.68 + *  Your callback may fire at any time, and its firing does not indicate any
    4.69 + *  specific behavior (for example, on Windows, this certainly might fire
    4.70 + *  when the OS is deciding whether to drag your window, but it fires for lots
    4.71 + *  of other reasons, too, some unrelated to anything you probably care about
    4.72 + *  _and when the mouse isn't actually at the location it is testing_).
    4.73 + *  Since this can fire at any time, you should try to keep your callback
    4.74 + *  efficient, devoid of allocations, etc.
    4.75 + *
    4.76 + *  \param window The window to set hit-testing on.
    4.77 + *  \param callback The callback to call when doing a hit-test.
    4.78 + *  \param callback_data An app-defined void pointer passed to the callback.
    4.79 + *  \return 0 on success, -1 on error (including unsupported).
    4.80 + */
    4.81 +extern DECLSPEC int SDLCALL SDL_SetWindowHitTest(SDL_Window * window,
    4.82 +                                                 SDL_HitTest callback,
    4.83 +                                                 void *callback_data);
    4.84 +
    4.85 +/**
    4.86   *  \brief Destroy a window.
    4.87   */
    4.88  extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);
     5.1 --- a/src/dynapi/SDL_dynapi_overrides.h	Wed Jun 25 02:08:37 2014 -0700
     5.2 +++ b/src/dynapi/SDL_dynapi_overrides.h	Wed Jun 25 17:06:12 2014 -0400
     5.3 @@ -584,3 +584,6 @@
     5.4  #define SDL_sqrtf SDL_sqrtf_REAL
     5.5  #define SDL_tan SDL_tan_REAL
     5.6  #define SDL_tanf SDL_tanf_REAL
     5.7 +#define SDL_CaptureMouse SDL_CaptureMouse_REAL
     5.8 +#define SDL_SetWindowHitTest SDL_SetWindowHitTest_REAL
     5.9 +#define SDL_GetGlobalMouseState SDL_GetGlobalMouseState_REAL
     6.1 --- a/src/dynapi/SDL_dynapi_procs.h	Wed Jun 25 02:08:37 2014 -0700
     6.2 +++ b/src/dynapi/SDL_dynapi_procs.h	Wed Jun 25 17:06:12 2014 -0400
     6.3 @@ -616,3 +616,6 @@
     6.4  SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return)
     6.5  SDL_DYNAPI_PROC(double,SDL_tan,(double a),(a),return)
     6.6  SDL_DYNAPI_PROC(float,SDL_tanf,(float a),(a),return)
     6.7 +SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
     6.8 +SDL_DYNAPI_PROC(int,SDL_SetWindowHitTest,(SDL_Window *a, SDL_HitTest b, void *c),(a,b,c),return)
     6.9 +SDL_DYNAPI_PROC(Uint32,SDL_GetGlobalMouseState,(int *a, int *b),(a,b),return)
     7.1 --- a/src/events/SDL_keyboard.c	Wed Jun 25 02:08:37 2014 -0700
     7.2 +++ b/src/events/SDL_keyboard.c	Wed Jun 25 17:06:12 2014 -0400
     7.3 @@ -25,6 +25,7 @@
     7.4  #include "SDL_timer.h"
     7.5  #include "SDL_events.h"
     7.6  #include "SDL_events_c.h"
     7.7 +#include "SDL_assert.h"
     7.8  #include "../video/SDL_sysvideo.h"
     7.9  
    7.10  
    7.11 @@ -619,6 +620,16 @@
    7.12  
    7.13      /* See if the current window has lost focus */
    7.14      if (keyboard->focus && keyboard->focus != window) {
    7.15 +
    7.16 +        /* new window shouldn't think it has mouse captured. */
    7.17 +        SDL_assert(!window || !(window->flags & SDL_WINDOW_MOUSE_CAPTURE));
    7.18 +
    7.19 +        /* old window must lose an existing mouse capture. */
    7.20 +        if (keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE) {
    7.21 +            SDL_CaptureMouse(SDL_FALSE);  /* drop the capture. */
    7.22 +            SDL_assert(!(keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE));
    7.23 +        }
    7.24 +
    7.25          SDL_SendWindowEvent(keyboard->focus, SDL_WINDOWEVENT_FOCUS_LOST,
    7.26                              0, 0);
    7.27  
     8.1 --- a/src/events/SDL_mouse.c	Wed Jun 25 02:08:37 2014 -0700
     8.2 +++ b/src/events/SDL_mouse.c	Wed Jun 25 17:06:12 2014 -0400
     8.3 @@ -140,14 +140,14 @@
     8.4  SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
     8.5  {
     8.6      SDL_Mouse *mouse = SDL_GetMouse();
     8.7 -    int w, h;
     8.8 -    SDL_bool inWindow;
     8.9 +    SDL_bool inWindow = SDL_TRUE;
    8.10  
    8.11 -    SDL_GetWindowSize(window, &w, &h);
    8.12 -    if (x < 0 || y < 0 || x >= w || y >= h) {
    8.13 -        inWindow = SDL_FALSE;
    8.14 -    } else {
    8.15 -        inWindow = SDL_TRUE;
    8.16 +    if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
    8.17 +        int w, h;
    8.18 +        SDL_GetWindowSize(window, &w, &h);
    8.19 +        if (x < 0 || y < 0 || x >= w || y >= h) {
    8.20 +            inWindow = SDL_FALSE;
    8.21 +        }
    8.22      }
    8.23  
    8.24  /* Linux doesn't give you mouse events outside your window unless you grab
    8.25 @@ -246,24 +246,29 @@
    8.26          mouse->y += yrel;
    8.27      }
    8.28  
    8.29 -    /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */
    8.30 -    SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
    8.31 -    --x_max;
    8.32 -    --y_max;
    8.33 +    /* make sure that the pointers find themselves inside the windows,
    8.34 +       unless we have the mouse captured. */
    8.35 +    if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
    8.36 +        int x_max = 0, y_max = 0;
    8.37  
    8.38 -    /* make sure that the pointers find themselves inside the windows */
    8.39 -    if (mouse->x > x_max) {
    8.40 -        mouse->x = x_max;
    8.41 -    }
    8.42 -    if (mouse->x < 0) {
    8.43 -        mouse->x = 0;
    8.44 -    }
    8.45 +        // !!! FIXME: shouldn't this be (window) instead of (mouse->focus)?
    8.46 +        SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
    8.47 +        --x_max;
    8.48 +        --y_max;
    8.49  
    8.50 -    if (mouse->y > y_max) {
    8.51 -        mouse->y = y_max;
    8.52 -    }
    8.53 -    if (mouse->y < 0) {
    8.54 -        mouse->y = 0;
    8.55 +        if (mouse->x > x_max) {
    8.56 +            mouse->x = x_max;
    8.57 +        }
    8.58 +        if (mouse->x < 0) {
    8.59 +            mouse->x = 0;
    8.60 +        }
    8.61 +
    8.62 +        if (mouse->y > y_max) {
    8.63 +            mouse->y = y_max;
    8.64 +        }
    8.65 +        if (mouse->y < 0) {
    8.66 +            mouse->y = 0;
    8.67 +        }
    8.68      }
    8.69  
    8.70      mouse->xdelta += xrel;
    8.71 @@ -426,6 +431,7 @@
    8.72      SDL_Cursor *cursor, *next;
    8.73      SDL_Mouse *mouse = SDL_GetMouse();
    8.74  
    8.75 +    SDL_CaptureMouse(SDL_FALSE);
    8.76      SDL_SetRelativeMouseMode(SDL_FALSE);
    8.77      SDL_ShowCursor(1);
    8.78  
    8.79 @@ -477,16 +483,42 @@
    8.80      return mouse->buttonstate;
    8.81  }
    8.82  
    8.83 +Uint32
    8.84 +SDL_GetGlobalMouseState(int *x, int *y)
    8.85 +{
    8.86 +    SDL_Mouse *mouse = SDL_GetMouse();
    8.87 +    int tmpx, tmpy;
    8.88 +
    8.89 +    /* make sure these are never NULL for the backend implementations... */
    8.90 +    if (!x) {
    8.91 +        x = &tmpx;
    8.92 +    }
    8.93 +    if (!y) {
    8.94 +        y = &tmpy;
    8.95 +    }
    8.96 +
    8.97 +    *x = *y = 0;
    8.98 +
    8.99 +    if (!mouse->GetGlobalMouseState) {
   8.100 +        SDL_assert(0 && "This should really be implemented for every target.");
   8.101 +        return 0;
   8.102 +    }
   8.103 +
   8.104 +    return mouse->GetGlobalMouseState(x, y);
   8.105 +}
   8.106 +
   8.107  void
   8.108  SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
   8.109  {
   8.110      SDL_Mouse *mouse = SDL_GetMouse();
   8.111  
   8.112 -    if (window == NULL)
   8.113 +    if (window == NULL) {
   8.114          window = mouse->focus;
   8.115 +    }
   8.116  
   8.117 -    if (window == NULL)
   8.118 +    if (window == NULL) {
   8.119          return;
   8.120 +    }
   8.121  
   8.122      if (mouse->WarpMouse) {
   8.123          mouse->WarpMouse(window, x, y);
     9.1 --- a/src/events/SDL_mouse_c.h	Wed Jun 25 02:08:37 2014 -0700
     9.2 +++ b/src/events/SDL_mouse_c.h	Wed Jun 25 17:06:12 2014 -0400
     9.3 @@ -66,6 +66,12 @@
     9.4      /* Set relative mode */
     9.5      int (*SetRelativeMouseMode) (SDL_bool enabled);
     9.6  
     9.7 +    /* Set mouse capture */
     9.8 +    int (*CaptureMouse) (SDL_Window * window);
     9.9 +
    9.10 +    /* Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call. */
    9.11 +    Uint32 (*GetGlobalMouseState) (int *x, int *y);
    9.12 +
    9.13      /* Data common to all mice */
    9.14      SDL_MouseID mouseID;
    9.15      SDL_Window *focus;
    10.1 --- a/src/test/SDL_test_common.c	Wed Jun 25 02:08:37 2014 -0700
    10.2 +++ b/src/test/SDL_test_common.c	Wed Jun 25 17:06:12 2014 -0400
    10.3 @@ -1379,6 +1379,14 @@
    10.4                      }
    10.5                  }
    10.6              }
    10.7 +            if (withShift) {
    10.8 +                SDL_Window *current_win = SDL_GetKeyboardFocus();
    10.9 +                if (current_win) {
   10.10 +                    const SDL_bool shouldCapture = (SDL_GetWindowFlags(current_win) & SDL_WINDOW_MOUSE_CAPTURE) == 0;
   10.11 +                    const int rc = SDL_CaptureMouse(shouldCapture);
   10.12 +                    SDL_Log("%sapturing mouse %s!\n", shouldCapture ? "C" : "Unc", (rc == 0) ? "succeeded" : "failed");
   10.13 +                }
   10.14 +            }
   10.15              break;
   10.16          case SDLK_v:
   10.17              if (withControl) {
   10.18 @@ -1478,6 +1486,19 @@
   10.19                  }
   10.20              }
   10.21              break;
   10.22 +        case SDLK_a:
   10.23 +            if (withControl) {
   10.24 +                /* Ctrl-A reports absolute mouse position. */
   10.25 +                int x, y;
   10.26 +                const Uint32 mask = SDL_GetGlobalMouseState(&x, &y);
   10.27 +                SDL_Log("ABSOLUTE MOUSE: (%d, %d)%s%s%s%s%s\n", x, y,
   10.28 +                        (mask & SDL_BUTTON_LMASK) ? " [LBUTTON]" : "",
   10.29 +                        (mask & SDL_BUTTON_MMASK) ? " [MBUTTON]" : "",
   10.30 +                        (mask & SDL_BUTTON_RMASK) ? " [RBUTTON]" : "",
   10.31 +                        (mask & SDL_BUTTON_X1MASK) ? " [X2BUTTON]" : "",
   10.32 +                        (mask & SDL_BUTTON_X2MASK) ? " [X2BUTTON]" : "");
   10.33 +            }
   10.34 +            break;
   10.35          case SDLK_0:
   10.36              if (withControl) {
   10.37                  SDL_Window *window = SDL_GetWindowFromID(event->key.windowID);
    11.1 --- a/src/video/SDL_sysvideo.h	Wed Jun 25 02:08:37 2014 -0700
    11.2 +++ b/src/video/SDL_sysvideo.h	Wed Jun 25 17:06:12 2014 -0400
    11.3 @@ -97,6 +97,9 @@
    11.4  
    11.5      SDL_WindowShaper *shaper;
    11.6  
    11.7 +    SDL_HitTest hit_test;
    11.8 +    void *hit_test_data;
    11.9 +
   11.10      SDL_WindowUserData *data;
   11.11  
   11.12      void *driverdata;
   11.13 @@ -261,6 +264,9 @@
   11.14      /* MessageBox */
   11.15      int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
   11.16  
   11.17 +    /* Hit-testing */
   11.18 +    int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
   11.19 +
   11.20      /* * * */
   11.21      /* Data common to all drivers */
   11.22      SDL_bool suspend_screensaver;
    12.1 --- a/src/video/SDL_video.c	Wed Jun 25 02:08:37 2014 -0700
    12.2 +++ b/src/video/SDL_video.c	Wed Jun 25 17:06:12 2014 -0400
    12.3 @@ -1428,6 +1428,11 @@
    12.4          SDL_SetWindowIcon(window, icon);
    12.5          SDL_FreeSurface(icon);
    12.6      }
    12.7 +
    12.8 +    if (window->hit_test) {
    12.9 +        _this->SetWindowHitTest(window, SDL_TRUE);
   12.10 +    }
   12.11 +
   12.12      SDL_FinishWindowCreation(window, flags);
   12.13  
   12.14      return 0;
   12.15 @@ -3292,12 +3297,17 @@
   12.16      int retval = -1;
   12.17      SDL_bool relative_mode;
   12.18      int show_cursor_prev;
   12.19 +    SDL_bool mouse_captured;
   12.20 +    SDL_Window *current_window;
   12.21  
   12.22      if (!messageboxdata) {
   12.23          return SDL_InvalidParamError("messageboxdata");
   12.24      }
   12.25  
   12.26 +    current_window = SDL_GetKeyboardFocus();
   12.27 +    mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0);
   12.28      relative_mode = SDL_GetRelativeMouseMode();
   12.29 +    SDL_CaptureMouse(SDL_FALSE);
   12.30      SDL_SetRelativeMouseMode(SDL_FALSE);
   12.31      show_cursor_prev = SDL_ShowCursor(1);
   12.32  
   12.33 @@ -3349,6 +3359,13 @@
   12.34          SDL_SetError("No message system available");
   12.35      }
   12.36  
   12.37 +    if (current_window) {
   12.38 +        SDL_RaiseWindow(current_window);
   12.39 +        if (mouse_captured) {
   12.40 +            SDL_CaptureMouse(SDL_TRUE);
   12.41 +        }
   12.42 +    }
   12.43 +
   12.44      SDL_ShowCursor(show_cursor_prev);
   12.45      SDL_SetRelativeMouseMode(relative_mode);
   12.46  
   12.47 @@ -3391,4 +3408,21 @@
   12.48      return SDL_TRUE;
   12.49  }
   12.50  
   12.51 +int
   12.52 +SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata)
   12.53 +{
   12.54 +    CHECK_WINDOW_MAGIC(window, -1);
   12.55 +
   12.56 +    if (!_this->SetWindowHitTest) {
   12.57 +        return SDL_Unsupported();
   12.58 +    } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) {
   12.59 +        return -1;
   12.60 +    }
   12.61 +
   12.62 +    window->hit_test = callback;
   12.63 +    window->hit_test_data = userdata;
   12.64 +
   12.65 +    return 0;
   12.66 +}
   12.67 +
   12.68  /* vi: set ts=4 sw=4 expandtab: */
    13.1 --- a/src/video/cocoa/SDL_cocoamouse.m	Wed Jun 25 02:08:37 2014 -0700
    13.2 +++ b/src/video/cocoa/SDL_cocoamouse.m	Wed Jun 25 17:06:12 2014 -0400
    13.3 @@ -307,6 +307,39 @@
    13.4      return 0;
    13.5  }
    13.6  
    13.7 +static int
    13.8 +Cocoa_CaptureMouse(SDL_Window *window)
    13.9 +{
   13.10 +    /* our Cocoa event code already tracks the mouse outside the window,
   13.11 +        so all we have to do here is say "okay" and do what we always do. */
   13.12 +    return 0;
   13.13 +}
   13.14 +
   13.15 +static Uint32
   13.16 +Cocoa_GetGlobalMouseState(int *x, int *y)
   13.17 +{
   13.18 +    const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
   13.19 +    const NSPoint cocoaLocation = [NSEvent mouseLocation];
   13.20 +    Uint32 retval = 0;
   13.21 +
   13.22 +    for (NSScreen *screen in [NSScreen screens]) {
   13.23 +        NSRect frame = [screen frame];
   13.24 +        if (NSPointInRect(cocoaLocation, frame)) {
   13.25 +            *x = (int) cocoaLocation.x;
   13.26 +            *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
   13.27 +            break;
   13.28 +        }
   13.29 +    }
   13.30 +
   13.31 +    retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
   13.32 +    retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
   13.33 +    retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
   13.34 +    retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
   13.35 +    retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
   13.36 +
   13.37 +    return retval;
   13.38 +}
   13.39 +
   13.40  void
   13.41  Cocoa_InitMouse(_THIS)
   13.42  {
   13.43 @@ -321,6 +354,8 @@
   13.44      mouse->WarpMouse = Cocoa_WarpMouse;
   13.45      mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
   13.46      mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
   13.47 +    mouse->CaptureMouse = Cocoa_CaptureMouse;
   13.48 +    mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
   13.49  
   13.50      SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
   13.51  
    14.1 --- a/src/video/cocoa/SDL_cocoavideo.m	Wed Jun 25 02:08:37 2014 -0700
    14.2 +++ b/src/video/cocoa/SDL_cocoavideo.m	Wed Jun 25 17:06:12 2014 -0400
    14.3 @@ -108,6 +108,7 @@
    14.4      device->SetWindowGrab = Cocoa_SetWindowGrab;
    14.5      device->DestroyWindow = Cocoa_DestroyWindow;
    14.6      device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
    14.7 +    device->SetWindowHitTest = Cocoa_SetWindowHitTest;
    14.8  
    14.9      device->shape_driver.CreateShaper = Cocoa_CreateShaper;
   14.10      device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
    15.1 --- a/src/video/cocoa/SDL_cocoawindow.h	Wed Jun 25 02:08:37 2014 -0700
    15.2 +++ b/src/video/cocoa/SDL_cocoawindow.h	Wed Jun 25 17:06:12 2014 -0400
    15.3 @@ -45,6 +45,7 @@
    15.4      PendingWindowOperation pendingWindowOperation;
    15.5      BOOL isMoving;
    15.6      int pendingWindowWarpX, pendingWindowWarpY;
    15.7 +    BOOL isDragAreaRunning;
    15.8  }
    15.9  
   15.10  -(void) listen:(SDL_WindowData *) data;
   15.11 @@ -75,6 +76,9 @@
   15.12  -(void) windowDidExitFullScreen:(NSNotification *) aNotification;
   15.13  -(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
   15.14  
   15.15 +/* See if event is in a drag area, toggle on window dragging. */
   15.16 +-(BOOL) processHitTest:(NSEvent *)theEvent;
   15.17 +
   15.18  /* Window event handling */
   15.19  -(void) mouseDown:(NSEvent *) theEvent;
   15.20  -(void) rightMouseDown:(NSEvent *) theEvent;
   15.21 @@ -138,8 +142,8 @@
   15.22  extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
   15.23  extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
   15.24  extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
   15.25 -extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window,
   15.26 -                                      struct SDL_SysWMinfo *info);
   15.27 +extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
   15.28 +extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
   15.29  
   15.30  #endif /* _SDL_cocoawindow_h */
   15.31  
    16.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Wed Jun 25 02:08:37 2014 -0700
    16.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Wed Jun 25 17:06:12 2014 -0400
    16.3 @@ -192,6 +192,7 @@
    16.4      inFullscreenTransition = NO;
    16.5      pendingWindowOperation = PENDING_OPERATION_NONE;
    16.6      isMoving = NO;
    16.7 +    isDragAreaRunning = NO;
    16.8  
    16.9      center = [NSNotificationCenter defaultCenter];
   16.10  
   16.11 @@ -663,10 +664,48 @@
   16.12      /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
   16.13  }
   16.14  
   16.15 +/* We'll respond to selectors by doing nothing so we don't beep.
   16.16 + * The escape key gets converted to a "cancel" selector, etc.
   16.17 + */
   16.18 +- (void)doCommandBySelector:(SEL)aSelector
   16.19 +{
   16.20 +    /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
   16.21 +}
   16.22 +
   16.23 +- (BOOL)processHitTest:(NSEvent *)theEvent
   16.24 +{
   16.25 +    SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
   16.26 +
   16.27 +    if (_data->window->hit_test) {  /* if no hit-test, skip this. */
   16.28 +        const NSPoint location = [theEvent locationInWindow];
   16.29 +        const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
   16.30 +        const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
   16.31 +        if (rc == SDL_HITTEST_DRAGGABLE) {
   16.32 +            if (!isDragAreaRunning) {
   16.33 +                isDragAreaRunning = YES;
   16.34 +                [_data->nswindow setMovableByWindowBackground:YES];
   16.35 +            }
   16.36 +            return YES;  /* dragging! */
   16.37 +        }
   16.38 +    }
   16.39 +
   16.40 +    if (isDragAreaRunning) {
   16.41 +        isDragAreaRunning = NO;
   16.42 +        [_data->nswindow setMovableByWindowBackground:NO];
   16.43 +        return YES;  /* was dragging, drop event. */
   16.44 +    }
   16.45 +
   16.46 +    return NO;  /* not a special area, carry on. */
   16.47 +}
   16.48 +
   16.49  - (void)mouseDown:(NSEvent *)theEvent
   16.50  {
   16.51      int button;
   16.52  
   16.53 +    if ([self processHitTest:theEvent]) {
   16.54 +        return;  /* dragging, drop event. */
   16.55 +    }
   16.56 +
   16.57      switch ([theEvent buttonNumber]) {
   16.58      case 0:
   16.59          if (([theEvent modifierFlags] & NSControlKeyMask) &&
   16.60 @@ -705,6 +744,10 @@
   16.61  {
   16.62      int button;
   16.63  
   16.64 +    if ([self processHitTest:theEvent]) {
   16.65 +        return;  /* stopped dragging, drop event. */
   16.66 +    }
   16.67 +
   16.68      switch ([theEvent buttonNumber]) {
   16.69      case 0:
   16.70          if (wasCtrlLeft) {
   16.71 @@ -744,6 +787,10 @@
   16.72      NSPoint point;
   16.73      int x, y;
   16.74  
   16.75 +    if ([self processHitTest:theEvent]) {
   16.76 +        return;  /* dragging, drop event. */
   16.77 +    }
   16.78 +
   16.79      if (mouse->relative_mode) {
   16.80          return;
   16.81      }
   16.82 @@ -752,8 +799,8 @@
   16.83      x = (int)point.x;
   16.84      y = (int)(window->h - point.y);
   16.85  
   16.86 -    if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   16.87 -        if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   16.88 +    if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
   16.89 +        if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
   16.90              if (x < 0) {
   16.91                  x = 0;
   16.92              } else if (x >= window->w) {
   16.93 @@ -890,6 +937,7 @@
   16.94  
   16.95  /* The default implementation doesn't pass rightMouseDown to responder chain */
   16.96  - (void)rightMouseDown:(NSEvent *)theEvent;
   16.97 +- (BOOL)mouseDownCanMoveWindow;
   16.98  @end
   16.99  
  16.100  @implementation SDLView
  16.101 @@ -898,6 +946,14 @@
  16.102      [[self nextResponder] rightMouseDown:theEvent];
  16.103  }
  16.104  
  16.105 +- (BOOL)mouseDownCanMoveWindow
  16.106 +{
  16.107 +    /* Always say YES, but this doesn't do anything until we call
  16.108 +       -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
  16.109 +       during mouse events when we're using a drag area. */
  16.110 +    return YES;
  16.111 +}
  16.112 +
  16.113  - (void)resetCursorRects
  16.114  {
  16.115      [super resetCursorRects];
  16.116 @@ -995,6 +1051,7 @@
  16.117  
  16.118      /* All done! */
  16.119      [pool release];
  16.120 +    window->driverdata = data;
  16.121      return 0;
  16.122  }
  16.123  
  16.124 @@ -1566,6 +1623,12 @@
  16.125      return succeeded;
  16.126  }
  16.127  
  16.128 +int
  16.129 +Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
  16.130 +{
  16.131 +    return 0;  /* just succeed, the real work is done elsewhere. */
  16.132 +}
  16.133 +
  16.134  #endif /* SDL_VIDEO_DRIVER_COCOA */
  16.135  
  16.136  /* vi: set ts=4 sw=4 expandtab: */
    17.1 --- a/src/video/windows/SDL_windowsevents.c	Wed Jun 25 02:08:37 2014 -0700
    17.2 +++ b/src/video/windows/SDL_windowsevents.c	Wed Jun 25 17:06:12 2014 -0400
    17.3 @@ -30,6 +30,7 @@
    17.4  #include "../../events/SDL_events_c.h"
    17.5  #include "../../events/SDL_touch_c.h"
    17.6  #include "../../events/scancodes_windows.h"
    17.7 +#include "SDL_assert.h"
    17.8  
    17.9  /* Dropfile support */
   17.10  #include <shellapi.h>
   17.11 @@ -438,33 +439,55 @@
   17.12              HRAWINPUT hRawInput = (HRAWINPUT)lParam;
   17.13              RAWINPUT inp;
   17.14              UINT size = sizeof(inp);
   17.15 +            const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
   17.16 +            const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
   17.17  
   17.18 -            if (!mouse->relative_mode || mouse->relative_mode_warp || mouse->focus != data->window) {
   17.19 -                break;
   17.20 +            if (!isRelative || mouse->focus != data->window) {
   17.21 +                if (!isCapture) {
   17.22 +                    break;
   17.23 +                }
   17.24              }
   17.25  
   17.26              GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
   17.27  
   17.28              /* Mouse data */
   17.29              if (inp.header.dwType == RIM_TYPEMOUSE) {
   17.30 -                RAWMOUSE* mouse = &inp.data.mouse;
   17.31 +                if (isRelative) {
   17.32 +                    RAWMOUSE* mouse = &inp.data.mouse;
   17.33  
   17.34 -                if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
   17.35 -                    SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
   17.36 -                } else {
   17.37 -                    /* synthesize relative moves from the abs position */
   17.38 -                    static SDL_Point initialMousePoint;
   17.39 -                    if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
   17.40 +                    if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
   17.41 +                        SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
   17.42 +                    } else {
   17.43 +                        /* synthesize relative moves from the abs position */
   17.44 +                        static SDL_Point initialMousePoint;
   17.45 +                        if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
   17.46 +                            initialMousePoint.x = mouse->lLastX;
   17.47 +                            initialMousePoint.y = mouse->lLastY;
   17.48 +                        }
   17.49 +
   17.50 +                        SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) );
   17.51 +
   17.52                          initialMousePoint.x = mouse->lLastX;
   17.53                          initialMousePoint.y = mouse->lLastY;
   17.54                      }
   17.55 -
   17.56 -                    SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y));
   17.57 -
   17.58 -                    initialMousePoint.x = mouse->lLastX;
   17.59 -                    initialMousePoint.y = mouse->lLastY;
   17.60 +                    WIN_CheckRawMouseButtons( mouse->usButtonFlags, data );
   17.61 +                } else if (isCapture) {
   17.62 +                    /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
   17.63 +                    POINT pt;
   17.64 +                    HWND hwnd = data->hwnd;
   17.65 +                    GetCursorPos(&pt);
   17.66 +                    if (WindowFromPoint(pt) != hwnd) {  /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
   17.67 +                        ScreenToClient(data->hwnd, &pt);
   17.68 +                        SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
   17.69 +                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
   17.70 +                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
   17.71 +                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
   17.72 +                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
   17.73 +                        SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
   17.74 +                    }
   17.75 +                } else {
   17.76 +                    SDL_assert(0 && "Shouldn't happen");
   17.77                  }
   17.78 -                WIN_CheckRawMouseButtons(mouse->usButtonFlags, data);
   17.79              }
   17.80          }
   17.81          break;
   17.82 @@ -509,7 +532,7 @@
   17.83  
   17.84  #ifdef WM_MOUSELEAVE
   17.85      case WM_MOUSELEAVE:
   17.86 -        if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode) {
   17.87 +        if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
   17.88              if (!IsIconic(hwnd)) {
   17.89                  POINT cursorPos;
   17.90                  GetCursorPos(&cursorPos);
   17.91 @@ -863,6 +886,32 @@
   17.92              return 0;
   17.93          }
   17.94          break;
   17.95 +
   17.96 +    case WM_NCHITTEST:
   17.97 +        {
   17.98 +            SDL_Window *window = data->window;
   17.99 +            if (window->hit_test) {
  17.100 +                POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
  17.101 +                if (ScreenToClient(data->hwnd, &winpoint)) {
  17.102 +                    const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
  17.103 +                    const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
  17.104 +                    switch (rc) {
  17.105 +                        case SDL_HITTEST_DRAGGABLE: return HTCAPTION;
  17.106 +                        case SDL_HITTEST_RESIZE_TOPLEFT: return HTTOPLEFT;
  17.107 +                        case SDL_HITTEST_RESIZE_TOP: return HTTOP;
  17.108 +                        case SDL_HITTEST_RESIZE_TOPRIGHT: return HTTOPRIGHT;
  17.109 +                        case SDL_HITTEST_RESIZE_RIGHT: return HTRIGHT;
  17.110 +                        case SDL_HITTEST_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT;
  17.111 +                        case SDL_HITTEST_RESIZE_BOTTOM: return HTBOTTOM;
  17.112 +                        case SDL_HITTEST_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT;
  17.113 +                        case SDL_HITTEST_RESIZE_LEFT: return HTLEFT;
  17.114 +                    }
  17.115 +                }
  17.116 +                /* If we didn't return, this will call DefWindowProc below. */
  17.117 +            }
  17.118 +        }
  17.119 +        break;
  17.120 +
  17.121      }
  17.122  
  17.123      /* If there's a window proc, assume it's going to handle messages */
    18.1 --- a/src/video/windows/SDL_windowsmouse.c	Wed Jun 25 02:08:37 2014 -0700
    18.2 +++ b/src/video/windows/SDL_windowsmouse.c	Wed Jun 25 17:06:12 2014 -0400
    18.3 @@ -30,6 +30,44 @@
    18.4  
    18.5  HCURSOR SDL_cursor = NULL;
    18.6  
    18.7 +static int rawInputEnableCount = 0;
    18.8 +
    18.9 +static int 
   18.10 +ToggleRawInput(SDL_bool enabled)
   18.11 +{
   18.12 +    RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
   18.13 +
   18.14 +    if (enabled) {
   18.15 +        rawInputEnableCount++;
   18.16 +        if (rawInputEnableCount > 1) {
   18.17 +            return 0;  /* already done. */
   18.18 +        }
   18.19 +    } else {
   18.20 +        if (rawInputEnableCount == 0) {
   18.21 +            return 0;  /* already done. */
   18.22 +        }
   18.23 +        rawInputEnableCount--;
   18.24 +        if (rawInputEnableCount > 0) {
   18.25 +            return 0;  /* not time to disable yet */
   18.26 +        }
   18.27 +    }
   18.28 +
   18.29 +    if (!enabled) {
   18.30 +        rawMouse.dwFlags |= RIDEV_REMOVE;
   18.31 +    }
   18.32 +
   18.33 +    /* (Un)register raw input for mice */
   18.34 +    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
   18.35 +
   18.36 +        /* Only return an error when registering. If we unregister and fail,
   18.37 +           then it's probably that we unregistered twice. That's OK. */
   18.38 +        if (enabled) {
   18.39 +            return SDL_Unsupported();
   18.40 +        }
   18.41 +    }
   18.42 +    return 0;
   18.43 +}
   18.44 +
   18.45  
   18.46  static SDL_Cursor *
   18.47  WIN_CreateDefaultCursor()
   18.48 @@ -211,22 +249,43 @@
   18.49  static int
   18.50  WIN_SetRelativeMouseMode(SDL_bool enabled)
   18.51  {
   18.52 -    RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
   18.53 +    return ToggleRawInput(enabled);
   18.54 +}
   18.55  
   18.56 -    if (!enabled) {
   18.57 -        rawMouse.dwFlags |= RIDEV_REMOVE;
   18.58 +static int
   18.59 +WIN_CaptureMouse(SDL_Window *window)
   18.60 +{
   18.61 +    if (!window) {
   18.62 +        SDL_Window *focusWin = SDL_GetKeyboardFocus();
   18.63 +        if (focusWin) {
   18.64 +            SDL_WindowData *data = (SDL_WindowData *)focusWin->driverdata;
   18.65 +            WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin);  /* make sure WM_MOUSELEAVE messages are (re)enabled. */
   18.66 +        }
   18.67      }
   18.68  
   18.69 -    /* (Un)register raw input for mice */
   18.70 -    if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
   18.71 +    /* While we were thinking of SetCapture() when designing this API in SDL,
   18.72 +       we didn't count on the fact that SetCapture() only tracks while the
   18.73 +       left mouse button is held down! Instead, we listen for raw mouse input
   18.74 +       and manually query the mouse when it leaves the window. :/ */
   18.75 +    return ToggleRawInput(window != NULL);
   18.76 +}
   18.77  
   18.78 -        /* Only return an error when registering. If we unregister and fail,
   18.79 -           then it's probably that we unregistered twice. That's OK. */
   18.80 -        if (enabled) {
   18.81 -            return SDL_Unsupported();
   18.82 -        }
   18.83 -    }
   18.84 -    return 0;
   18.85 +static Uint32
   18.86 +WIN_GetGlobalMouseState(int *x, int *y)
   18.87 +{
   18.88 +    Uint32 retval = 0;
   18.89 +    POINT pt = { 0, 0 };
   18.90 +    GetCursorPos(&pt);
   18.91 +    *x = (int) pt.x;
   18.92 +    *y = (int) pt.y;
   18.93 +
   18.94 +    retval |= GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
   18.95 +    retval |= GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
   18.96 +    retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
   18.97 +    retval |= GetAsyncKeyState(VK_X1BUTTON) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
   18.98 +    retval |= GetAsyncKeyState(VK_X2BUTTON) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
   18.99 +
  18.100 +    return retval;
  18.101  }
  18.102  
  18.103  void
  18.104 @@ -241,6 +300,8 @@
  18.105      mouse->WarpMouse = WIN_WarpMouse;
  18.106      mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
  18.107      mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
  18.108 +    mouse->CaptureMouse = WIN_CaptureMouse;
  18.109 +    mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
  18.110  
  18.111      SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
  18.112  
  18.113 @@ -256,6 +317,11 @@
  18.114          mouse->def_cursor = NULL;
  18.115          mouse->cur_cursor = NULL;
  18.116      }
  18.117 +
  18.118 +    if (rawInputEnableCount) {  /* force RAWINPUT off here. */
  18.119 +        rawInputEnableCount = 1;
  18.120 +        ToggleRawInput(SDL_FALSE);
  18.121 +    }
  18.122  }
  18.123  
  18.124  #endif /* SDL_VIDEO_DRIVER_WINDOWS */
    19.1 --- a/src/video/windows/SDL_windowsvideo.c	Wed Jun 25 02:08:37 2014 -0700
    19.2 +++ b/src/video/windows/SDL_windowsvideo.c	Wed Jun 25 17:06:12 2014 -0400
    19.3 @@ -144,6 +144,7 @@
    19.4      device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
    19.5      device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
    19.6      device->OnWindowEnter = WIN_OnWindowEnter;
    19.7 +    device->SetWindowHitTest = WIN_SetWindowHitTest;
    19.8  
    19.9      device->shape_driver.CreateShaper = Win32_CreateShaper;
   19.10      device->shape_driver.SetWindowShape = Win32_SetWindowShape;
    20.1 --- a/src/video/windows/SDL_windowswindow.c	Wed Jun 25 02:08:37 2014 -0700
    20.2 +++ b/src/video/windows/SDL_windowswindow.c	Wed Jun 25 17:06:12 2014 -0400
    20.3 @@ -785,6 +785,12 @@
    20.4      }
    20.5  }
    20.6  
    20.7 +int
    20.8 +WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
    20.9 +{
   20.10 +    return 0;  /* just succeed, the real work is done elsewhere. */
   20.11 +}
   20.12 +
   20.13  #endif /* SDL_VIDEO_DRIVER_WINDOWS */
   20.14  
   20.15  /* vi: set ts=4 sw=4 expandtab: */
    21.1 --- a/src/video/windows/SDL_windowswindow.h	Wed Jun 25 02:08:37 2014 -0700
    21.2 +++ b/src/video/windows/SDL_windowswindow.h	Wed Jun 25 17:06:12 2014 -0400
    21.3 @@ -69,6 +69,7 @@
    21.4                                      struct SDL_SysWMinfo *info);
    21.5  extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
    21.6  extern void WIN_UpdateClipCursor(SDL_Window *window);
    21.7 +extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
    21.8  
    21.9  #endif /* _SDL_windowswindow_h */
   21.10  
    22.1 --- a/src/video/x11/SDL_x11events.c	Wed Jun 25 02:08:37 2014 -0700
    22.2 +++ b/src/video/x11/SDL_x11events.c	Wed Jun 25 17:06:12 2014 -0400
    22.3 @@ -40,6 +40,42 @@
    22.4  
    22.5  #include <stdio.h>
    22.6  
    22.7 +#ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
    22.8 +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
    22.9 +#endif
   22.10 +
   22.11 +#ifndef _NET_WM_MOVERESIZE_SIZE_TOP
   22.12 +#define _NET_WM_MOVERESIZE_SIZE_TOP          1
   22.13 +#endif
   22.14 +
   22.15 +#ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
   22.16 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
   22.17 +#endif
   22.18 +
   22.19 +#ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
   22.20 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
   22.21 +#endif
   22.22 +
   22.23 +#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
   22.24 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
   22.25 +#endif
   22.26 +
   22.27 +#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
   22.28 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
   22.29 +#endif
   22.30 +
   22.31 +#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
   22.32 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
   22.33 +#endif
   22.34 +
   22.35 +#ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
   22.36 +#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
   22.37 +#endif
   22.38 +
   22.39 +#ifndef _NET_WM_MOVERESIZE_MOVE
   22.40 +#define _NET_WM_MOVERESIZE_MOVE              8
   22.41 +#endif
   22.42 +
   22.43  typedef struct {
   22.44      unsigned char *data;
   22.45      int format, count;
   22.46 @@ -348,6 +384,124 @@
   22.47  }
   22.48  
   22.49  static void
   22.50 +InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point)
   22.51 +{
   22.52 +    SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   22.53 +    SDL_Window* window = data->window;
   22.54 +    Display *display = viddata->display;
   22.55 +
   22.56 +    /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
   22.57 +    X11_XUngrabPointer(display, 0L);
   22.58 +    X11_XFlush(display);
   22.59 +
   22.60 +    XEvent evt;
   22.61 +    evt.xclient.type = ClientMessage;
   22.62 +    evt.xclient.window = data->xwindow;
   22.63 +    evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
   22.64 +    evt.xclient.format = 32;
   22.65 +    evt.xclient.data.l[0] = window->x + point->x;
   22.66 +    evt.xclient.data.l[1] = window->y + point->y;
   22.67 +    evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
   22.68 +    evt.xclient.data.l[3] = Button1;
   22.69 +    evt.xclient.data.l[4] = 0;
   22.70 +    X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
   22.71 +
   22.72 +    X11_XSync(display, 0);
   22.73 +}
   22.74 +
   22.75 +static void
   22.76 +InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction)
   22.77 +{
   22.78 +    SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
   22.79 +    SDL_Window* window = data->window;
   22.80 +    Display *display = viddata->display;
   22.81 +
   22.82 +    if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT)
   22.83 +        return;
   22.84 +
   22.85 +    /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
   22.86 +    X11_XUngrabPointer(display, 0L);
   22.87 +    X11_XFlush(display);
   22.88 +
   22.89 +    XEvent evt;
   22.90 +    evt.xclient.type = ClientMessage;
   22.91 +    evt.xclient.window = data->xwindow;
   22.92 +    evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
   22.93 +    evt.xclient.format = 32;
   22.94 +    evt.xclient.data.l[0] = window->x + point->x;
   22.95 +    evt.xclient.data.l[1] = window->y + point->y;
   22.96 +    evt.xclient.data.l[2] = direction;
   22.97 +    evt.xclient.data.l[3] = Button1;
   22.98 +    evt.xclient.data.l[4] = 0;
   22.99 +    X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
  22.100 +
  22.101 +    X11_XSync(display, 0);
  22.102 +}
  22.103 +
  22.104 +static SDL_bool
  22.105 +ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev)
  22.106 +{
  22.107 +    SDL_Window *window = data->window;
  22.108 +    SDL_bool ret = SDL_FALSE;
  22.109 +
  22.110 +    if (window->hit_test) {
  22.111 +        const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
  22.112 +        const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
  22.113 +        switch (rc) {
  22.114 +            case SDL_HITTEST_DRAGGABLE: {
  22.115 +                    InitiateWindowMove(_this, data, &point);
  22.116 +                    ret = SDL_TRUE;
  22.117 +                }
  22.118 +                break;
  22.119 +            case SDL_HITTEST_RESIZE_TOPLEFT: {
  22.120 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOPLEFT);
  22.121 +                    ret = SDL_TRUE;
  22.122 +                }
  22.123 +                break;
  22.124 +            case SDL_HITTEST_RESIZE_TOP: {
  22.125 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOP);
  22.126 +                    ret = SDL_TRUE;
  22.127 +                }
  22.128 +                break;
  22.129 +            case SDL_HITTEST_RESIZE_TOPRIGHT: {
  22.130 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
  22.131 +                    ret = SDL_TRUE;
  22.132 +                }
  22.133 +                break;
  22.134 +            case SDL_HITTEST_RESIZE_RIGHT: {
  22.135 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_RIGHT);
  22.136 +                    ret = SDL_TRUE;
  22.137 +                }
  22.138 +                break;
  22.139 +            case SDL_HITTEST_RESIZE_BOTTOMRIGHT: {
  22.140 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
  22.141 +                    ret = SDL_TRUE;
  22.142 +                }
  22.143 +                break;
  22.144 +            case SDL_HITTEST_RESIZE_BOTTOM: {
  22.145 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOM);
  22.146 +                    ret = SDL_TRUE;
  22.147 +                }
  22.148 +                break;
  22.149 +            case SDL_HITTEST_RESIZE_BOTTOMLEFT: {
  22.150 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
  22.151 +                    ret = SDL_TRUE;
  22.152 +                }
  22.153 +                break;
  22.154 +            case SDL_HITTEST_RESIZE_LEFT: {
  22.155 +                    InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_LEFT);
  22.156 +                    ret = SDL_TRUE;
  22.157 +                }
  22.158 +                break;
  22.159 +            default:
  22.160 +                break;
  22.161 +        }
  22.162 +    }
  22.163 +
  22.164 +    return ret;
  22.165 +}
  22.166 +
  22.167 +static void
  22.168  X11_DispatchEvent(_THIS)
  22.169  {
  22.170      SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
  22.171 @@ -820,6 +974,11 @@
  22.172              if (X11_IsWheelEvent(display,&xevent,&ticks)) {
  22.173                  SDL_SendMouseWheel(data->window, 0, 0, ticks);
  22.174              } else {
  22.175 +                if(xevent.xbutton.button == Button1) {
  22.176 +                    if (ProcessHitTest(_this, data, &xevent)) {
  22.177 +                        break;  /* don't pass this event on to app. */
  22.178 +                    }
  22.179 +                }
  22.180                  SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
  22.181              }
  22.182          }
    23.1 --- a/src/video/x11/SDL_x11mouse.c	Wed Jun 25 02:08:37 2014 -0700
    23.2 +++ b/src/video/x11/SDL_x11mouse.c	Wed Jun 25 17:06:12 2014 -0400
    23.3 @@ -339,6 +339,62 @@
    23.4      return -1;
    23.5  }
    23.6  
    23.7 +static int
    23.8 +X11_CaptureMouse(SDL_Window *window)
    23.9 +{
   23.10 +    Display *display = GetDisplay();
   23.11 +
   23.12 +    if (window) {
   23.13 +        SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
   23.14 +        const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
   23.15 +        const int rc = X11_XGrabPointer(display, data->xwindow, False,
   23.16 +                                        mask, GrabModeAsync, GrabModeAsync,
   23.17 +                                        None, None, CurrentTime);
   23.18 +        if (rc != GrabSuccess) {
   23.19 +            return SDL_SetError("X server refused mouse capture");
   23.20 +        }
   23.21 +    } else {
   23.22 +        X11_XUngrabPointer(display, CurrentTime);
   23.23 +    }
   23.24 +
   23.25 +    X11_XSync(display, False);
   23.26 +
   23.27 +    return 0;
   23.28 +}
   23.29 +
   23.30 +static Uint32
   23.31 +X11_GetGlobalMouseState(int *x, int *y)
   23.32 +{
   23.33 +    Display *display = GetDisplay();
   23.34 +    const int num_screens = SDL_GetNumVideoDisplays();
   23.35 +    int i;
   23.36 +
   23.37 +    /* !!! FIXME: should we XSync() here first? */
   23.38 +
   23.39 +    for (i = 0; i < num_screens; i++) {
   23.40 +        SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
   23.41 +        if (data != NULL) {
   23.42 +            Window root, child;
   23.43 +            int rootx, rooty, winx, winy;
   23.44 +            unsigned int mask;
   23.45 +            if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
   23.46 +                Uint32 retval = 0;
   23.47 +                retval |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
   23.48 +                retval |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
   23.49 +                retval |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
   23.50 +                *x = data->x + rootx;
   23.51 +                *y = data->y + rooty;
   23.52 +                return retval;
   23.53 +            }
   23.54 +        }
   23.55 +    }
   23.56 +
   23.57 +    SDL_assert(0 && "The pointer wasn't on any X11 screen?!");
   23.58 +
   23.59 +    return 0;
   23.60 +}
   23.61 +
   23.62 +
   23.63  void
   23.64  X11_InitMouse(_THIS)
   23.65  {
   23.66 @@ -351,6 +407,8 @@
   23.67      mouse->WarpMouse = X11_WarpMouse;
   23.68      mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
   23.69      mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
   23.70 +    mouse->CaptureMouse = X11_CaptureMouse;
   23.71 +    mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
   23.72  
   23.73      SDL_SetDefaultCursor(X11_CreateDefaultCursor());
   23.74  }
    24.1 --- a/src/video/x11/SDL_x11video.c	Wed Jun 25 02:08:37 2014 -0700
    24.2 +++ b/src/video/x11/SDL_x11video.c	Wed Jun 25 17:06:12 2014 -0400
    24.3 @@ -243,6 +243,7 @@
    24.4      device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
    24.5      device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
    24.6      device->GetWindowWMInfo = X11_GetWindowWMInfo;
    24.7 +    device->SetWindowHitTest = X11_SetWindowHitTest;
    24.8  
    24.9      device->shape_driver.CreateShaper = X11_CreateShaper;
   24.10      device->shape_driver.SetWindowShape = X11_SetWindowShape;
    25.1 --- a/src/video/x11/SDL_x11window.c	Wed Jun 25 02:08:37 2014 -0700
    25.2 +++ b/src/video/x11/SDL_x11window.c	Wed Jun 25 17:06:12 2014 -0400
    25.3 @@ -1445,6 +1445,12 @@
    25.4      }
    25.5  }
    25.6  
    25.7 +int
    25.8 +X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
    25.9 +{
   25.10 +    return 0;  /* just succeed, the real work is done elsewhere. */
   25.11 +}
   25.12 +
   25.13  #endif /* SDL_VIDEO_DRIVER_X11 */
   25.14  
   25.15  /* vi: set ts=4 sw=4 expandtab: */
    26.1 --- a/src/video/x11/SDL_x11window.h	Wed Jun 25 02:08:37 2014 -0700
    26.2 +++ b/src/video/x11/SDL_x11window.h	Wed Jun 25 17:06:12 2014 -0400
    26.3 @@ -93,6 +93,7 @@
    26.4  extern void X11_DestroyWindow(_THIS, SDL_Window * window);
    26.5  extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
    26.6                                      struct SDL_SysWMinfo *info);
    26.7 +extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
    26.8  
    26.9  #endif /* _SDL_x11window_h */
   26.10  
    27.1 --- a/test/Makefile.in	Wed Jun 25 02:08:37 2014 -0700
    27.2 +++ b/test/Makefile.in	Wed Jun 25 17:06:12 2014 -0400
    27.3 @@ -23,6 +23,7 @@
    27.4  	testgles$(EXE) \
    27.5  	testgles2$(EXE) \
    27.6  	testhaptic$(EXE) \
    27.7 +	testhittesting$(EXE) \
    27.8  	testrumble$(EXE) \
    27.9  	testhotplug$(EXE) \
   27.10  	testthread$(EXE) \
   27.11 @@ -108,6 +109,9 @@
   27.12  testrelative$(EXE): $(srcdir)/testrelative.c
   27.13  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   27.14  
   27.15 +testhittesting$(EXE): $(srcdir)/testhittesting.c
   27.16 +	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   27.17 +
   27.18  testdraw2$(EXE): $(srcdir)/testdraw2.c
   27.19  	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
   27.20  
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/test/testhittesting.c	Wed Jun 25 17:06:12 2014 -0400
    28.3 @@ -0,0 +1,134 @@
    28.4 +#include <stdio.h>
    28.5 +#include "SDL.h"
    28.6 +
    28.7 +/* !!! FIXME: rewrite this to be wired in to test framework. */
    28.8 +
    28.9 +#define RESIZE_BORDER 20
   28.10 +
   28.11 +const SDL_Rect drag_areas[] = {
   28.12 +    { 20, 20, 100, 100 },
   28.13 +    { 200, 70, 100, 100 },
   28.14 +    { 400, 90, 100, 100 }
   28.15 +};
   28.16 +
   28.17 +static const SDL_Rect *areas = drag_areas;
   28.18 +static int numareas = SDL_arraysize(drag_areas);
   28.19 +
   28.20 +static SDL_HitTestResult
   28.21 +hitTest(SDL_Window *window, const SDL_Point *pt, void *data)
   28.22 +{
   28.23 +    int i;
   28.24 +    int w, h;
   28.25 +
   28.26 +    for (i = 0; i < numareas; i++) {
   28.27 +        if (SDL_PointInRect(pt, &areas[i])) {
   28.28 +            SDL_Log("HIT-TEST: DRAGGABLE\n");
   28.29 +            return SDL_HITTEST_DRAGGABLE;
   28.30 +        }
   28.31 +    }
   28.32 +
   28.33 +    SDL_GetWindowSize(window, &w, &h);
   28.34 +
   28.35 +    #define REPORT_RESIZE_HIT(name) { \
   28.36 +        SDL_Log("HIT-TEST: RESIZE_" #name "\n"); \
   28.37 +        return SDL_HITTEST_RESIZE_##name; \
   28.38 +    }
   28.39 +
   28.40 +    if (pt->x < RESIZE_BORDER && pt->y < RESIZE_BORDER) {
   28.41 +        REPORT_RESIZE_HIT(TOPLEFT);
   28.42 +    } else if (pt->x > RESIZE_BORDER && pt->x < w - RESIZE_BORDER && pt->y < RESIZE_BORDER) {
   28.43 +        REPORT_RESIZE_HIT(TOP);
   28.44 +    } else if (pt->x > w - RESIZE_BORDER && pt->y < RESIZE_BORDER) {
   28.45 +        REPORT_RESIZE_HIT(TOPRIGHT);
   28.46 +    } else if (pt->x > w - RESIZE_BORDER && pt->y > RESIZE_BORDER && pt->y < h - RESIZE_BORDER) {
   28.47 +        REPORT_RESIZE_HIT(RIGHT);
   28.48 +    } else if (pt->x > w - RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
   28.49 +        REPORT_RESIZE_HIT(BOTTOMRIGHT);
   28.50 +    } else if (pt->x < w - RESIZE_BORDER && pt->x > RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
   28.51 +        REPORT_RESIZE_HIT(BOTTOM);
   28.52 +    } else if (pt->x < RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
   28.53 +        REPORT_RESIZE_HIT(BOTTOMLEFT);
   28.54 +    } else if (pt->x < RESIZE_BORDER && pt->y < h - RESIZE_BORDER && pt->y > RESIZE_BORDER) {
   28.55 +        REPORT_RESIZE_HIT(LEFT);
   28.56 +    }
   28.57 +
   28.58 +    SDL_Log("HIT-TEST: NORMAL\n");
   28.59 +    return SDL_HITTEST_NORMAL;
   28.60 +}
   28.61 +
   28.62 +
   28.63 +int main(int argc, char **argv)
   28.64 +{
   28.65 +    int done = 0;
   28.66 +    SDL_Window *window;
   28.67 +    SDL_Renderer *renderer;
   28.68 +
   28.69 +    /* !!! FIXME: check for errors. */
   28.70 +    SDL_Init(SDL_INIT_VIDEO);
   28.71 +    window = SDL_CreateWindow("Drag the red boxes", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE);
   28.72 +    renderer = SDL_CreateRenderer(window, -1, 0);
   28.73 +
   28.74 +    if (SDL_SetWindowHitTest(window, hitTest, NULL) == -1) {
   28.75 +        SDL_Log("Enabling hit-testing failed!\n");
   28.76 +        SDL_Quit();
   28.77 +        return 1;
   28.78 +    }
   28.79 +
   28.80 +    while (!done)
   28.81 +    {
   28.82 +        SDL_Event e;
   28.83 +        int nothing_to_do = 1;
   28.84 +
   28.85 +        SDL_SetRenderDrawColor(renderer, 0, 0, 127, 255);
   28.86 +        SDL_RenderClear(renderer);
   28.87 +        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
   28.88 +        SDL_RenderFillRects(renderer, areas, SDL_arraysize(drag_areas));
   28.89 +        SDL_RenderPresent(renderer);
   28.90 +
   28.91 +        while (SDL_PollEvent(&e)) {
   28.92 +            nothing_to_do = 0;
   28.93 +
   28.94 +            switch (e.type)
   28.95 +            {
   28.96 +                case SDL_MOUSEBUTTONDOWN:
   28.97 +                    SDL_Log("button down!\n");
   28.98 +                    break;
   28.99 +
  28.100 +                case SDL_MOUSEBUTTONUP:
  28.101 +                    SDL_Log("button up!\n");
  28.102 +                    break;
  28.103 +
  28.104 +                case SDL_WINDOWEVENT:
  28.105 +                    if (e.window.event == SDL_WINDOWEVENT_MOVED) {
  28.106 +                        SDL_Log("Window event moved to (%d, %d)!\n", (int) e.window.data1, (int) e.window.data2);
  28.107 +                    }
  28.108 +                    break;
  28.109 +
  28.110 +                case SDL_KEYDOWN:
  28.111 +                    if (e.key.keysym.sym == SDLK_ESCAPE) {
  28.112 +                        done = 1;
  28.113 +                    } else if (e.key.keysym.sym == SDLK_x) {
  28.114 +                        if (!areas) {
  28.115 +                            areas = drag_areas;
  28.116 +                            numareas = SDL_arraysize(drag_areas);
  28.117 +                        } else {
  28.118 +                            areas = NULL;
  28.119 +                            numareas = 0;
  28.120 +                        }
  28.121 +                    }
  28.122 +                    break;
  28.123 +
  28.124 +                case SDL_QUIT:
  28.125 +                    done = 1;
  28.126 +                    break;
  28.127 +            }
  28.128 +        }
  28.129 +
  28.130 +        if (nothing_to_do) {
  28.131 +            SDL_Delay(50);
  28.132 +        }
  28.133 +    }
  28.134 +
  28.135 +    SDL_Quit();
  28.136 +    return 0;
  28.137 +}