Merged Ryan's SDL-gui-backend branch.
Adds three APIs, and implements them on X11, Cocoa, and Windows:
- SDL_CaptureMouse()
- SDL_GetGlobalMouseState()
- SDL_SetWindowHitTest()
--- a/.hgignore Wed Jun 25 02:08:37 2014 -0700
+++ b/.hgignore Wed Jun 25 17:06:12 2014 -0400
@@ -84,6 +84,7 @@
test/testgl2
test/testgles
test/testhaptic
+test/testhittesting
test/testiconv
test/testime
test/testintersections
--- a/include/SDL_mouse.h Wed Jun 25 02:08:37 2014 -0700
+++ b/include/SDL_mouse.h Wed Jun 25 17:06:12 2014 -0400
@@ -78,6 +78,31 @@
extern DECLSPEC Uint32 SDLCALL SDL_GetMouseState(int *x, int *y);
/**
+ * \brief Get the current state of the mouse, in relation to the desktop
+ *
+ * This works just like SDL_GetMouseState(), but the coordinates will be
+ * reported relative to the top-left of the desktop. This can be useful if
+ * you need to track the mouse outside of a specific window and
+ * SDL_CaptureMouse() doesn't fit your needs. For example, it could be
+ * useful if you need to track the mouse while dragging a window, where
+ * coordinates relative to a window might not be in sync at all times.
+ *
+ * \note SDL_GetMouseState() returns the mouse position as SDL understands
+ * it from the last pump of the event queue. This function, however,
+ * queries the OS for the current mouse position, and as such, might
+ * be a slightly less efficient function. Unless you know what you're
+ * doing and have a good reason to use this function, you probably want
+ * SDL_GetMouseState() instead.
+ *
+ * \param x Returns the current X coord, relative to the desktop. Can be NULL.
+ * \param y Returns the current Y coord, relative to the desktop. Can be NULL.
+ * \return The current button state as a bitmask, which can be tested using the SDL_BUTTON(X) macros.
+ *
+ * \sa SDL_GetMouseState
+ */
+extern DECLSPEC Uint32 SDLCALL SDL_GetGlobalMouseState(int *x, int *y);
+
+/**
* \brief Retrieve the relative state of the mouse.
*
* The current button state is returned as a button bitmask, which can
@@ -127,6 +152,37 @@
extern DECLSPEC int SDLCALL SDL_SetRelativeMouseMode(SDL_bool enabled);
/**
+ * \brief Capture the mouse, to track input outside an SDL window.
+ *
+ * \param enabled Whether or not to enable capturing
+ *
+ * Capturing enables your app to obtain mouse events globally, instead of
+ * just within your window. Not all video targets support this function.
+ * When capturing is enabled, the current window will get all mouse events,
+ * but unlike relative mode, no change is made to the cursor and it is
+ * not restrained to your window.
+ *
+ * This function may also deny mouse input to other windows--both those in
+ * your application and others on the system--so you should use this
+ * function sparingly, and in small bursts. For example, you might want to
+ * track the mouse while the user is dragging something, until the user
+ * releases a mouse button. It is not recommended that you capture the mouse
+ * for long periods of time, such as the entire time your app is running.
+ *
+ * While captured, mouse events still report coordinates relative to the
+ * current (foreground) window, but those coordinates may be outside the
+ * bounds of the window (including negative values). Capturing is only
+ * allowed for the foreground window. If the window loses focus while
+ * capturing, the capture will be disabled automatically.
+ *
+ * While capturing is enabled, the current window will have the
+ * SDL_WINDOW_MOUSE_CAPTURE flag set.
+ *
+ * \return 0 on success, or -1 if not supported.
+ */
+extern DECLSPEC int SDLCALL SDL_CaptureMouse(SDL_bool enabled);
+
+/**
* \brief Query whether relative mouse mode is enabled.
*
* \sa SDL_SetRelativeMouseMode()
--- a/include/SDL_rect.h Wed Jun 25 02:08:37 2014 -0700
+++ b/include/SDL_rect.h Wed Jun 25 17:06:12 2014 -0400
@@ -43,6 +43,7 @@
* \brief The structure that defines a point
*
* \sa SDL_EnclosePoints
+ * \sa SDL_PointInRect
*/
typedef struct SDL_Point
{
@@ -67,6 +68,15 @@
} SDL_Rect;
/**
+ * \brief Returns true if point resides inside a rectangle.
+ */
+SDL_FORCE_INLINE SDL_bool SDL_PointInRect(const SDL_Point *p, const SDL_Rect *r)
+{
+ return ( (p->x >= r->x) && (p->x < (r->x + r->w)) &&
+ (p->y >= r->y) && (p->y < (r->y + r->h)) ) ? SDL_TRUE : SDL_FALSE;
+}
+
+/**
* \brief Returns true if the rectangle has no area.
*/
SDL_FORCE_INLINE SDL_bool SDL_RectEmpty(const SDL_Rect *r)
--- a/include/SDL_video.h Wed Jun 25 02:08:37 2014 -0700
+++ b/include/SDL_video.h Wed Jun 25 17:06:12 2014 -0400
@@ -108,7 +108,8 @@
SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */
SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */
- SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000 /**< window should be created in high-DPI mode if supported */
+ SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported */
+ SDL_WINDOW_MOUSE_CAPTURE = 0x00004000 /**< window has mouse captured (unrelated to INPUT_GRABBED) */
} SDL_WindowFlags;
/**
@@ -791,6 +792,75 @@
Uint16 * blue);
/**
+ * \brief Possible return values from the SDL_HitTest callback.
+ *
+ * \sa SDL_HitTest
+ */
+typedef enum
+{
+ SDL_HITTEST_NORMAL, /**< Region is normal. No special properties. */
+ SDL_HITTEST_DRAGGABLE, /**< Region can drag entire window. */
+ SDL_HITTEST_RESIZE_TOPLEFT,
+ SDL_HITTEST_RESIZE_TOP,
+ SDL_HITTEST_RESIZE_TOPRIGHT,
+ SDL_HITTEST_RESIZE_RIGHT,
+ SDL_HITTEST_RESIZE_BOTTOMRIGHT,
+ SDL_HITTEST_RESIZE_BOTTOM,
+ SDL_HITTEST_RESIZE_BOTTOMLEFT,
+ SDL_HITTEST_RESIZE_LEFT
+} SDL_HitTestResult;
+
+/**
+ * \brief Callback used for hit-testing.
+ *
+ * \sa SDL_SetWindowHitTest
+ */
+typedef SDL_HitTestResult (SDLCALL *SDL_HitTest)(SDL_Window *win,
+ const SDL_Point *area,
+ void *data);
+
+/**
+ * \brief Provide a callback that decides if a window region has special properties.
+ *
+ * Normally windows are dragged and resized by decorations provided by the
+ * system window manager (a title bar, borders, etc), but for some apps, it
+ * makes sense to drag them from somewhere else inside the window itself; for
+ * example, one might have a borderless window that wants to be draggable
+ * from any part, or simulate its own title bar, etc.
+ *
+ * This function lets the app provide a callback that designates pieces of
+ * a given window as special. This callback is run during event processing
+ * if we need to tell the OS to treat a region of the window specially; the
+ * use of this callback is known as "hit testing."
+ *
+ * Mouse input may not be delivered to your application if it is within
+ * a special area; the OS will often apply that input to moving the window or
+ * resizing the window and not deliver it to the application.
+ *
+ * Specifying NULL for a callback disables hit-testing. Hit-testing is
+ * disabled by default.
+ *
+ * Platforms that don't support this functionality will return -1
+ * unconditionally, even if you're attempting to disable hit-testing.
+ *
+ * Your callback may fire at any time, and its firing does not indicate any
+ * specific behavior (for example, on Windows, this certainly might fire
+ * when the OS is deciding whether to drag your window, but it fires for lots
+ * of other reasons, too, some unrelated to anything you probably care about
+ * _and when the mouse isn't actually at the location it is testing_).
+ * Since this can fire at any time, you should try to keep your callback
+ * efficient, devoid of allocations, etc.
+ *
+ * \param window The window to set hit-testing on.
+ * \param callback The callback to call when doing a hit-test.
+ * \param callback_data An app-defined void pointer passed to the callback.
+ * \return 0 on success, -1 on error (including unsupported).
+ */
+extern DECLSPEC int SDLCALL SDL_SetWindowHitTest(SDL_Window * window,
+ SDL_HitTest callback,
+ void *callback_data);
+
+/**
* \brief Destroy a window.
*/
extern DECLSPEC void SDLCALL SDL_DestroyWindow(SDL_Window * window);
--- a/src/dynapi/SDL_dynapi_overrides.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/dynapi/SDL_dynapi_overrides.h Wed Jun 25 17:06:12 2014 -0400
@@ -584,3 +584,6 @@
#define SDL_sqrtf SDL_sqrtf_REAL
#define SDL_tan SDL_tan_REAL
#define SDL_tanf SDL_tanf_REAL
+#define SDL_CaptureMouse SDL_CaptureMouse_REAL
+#define SDL_SetWindowHitTest SDL_SetWindowHitTest_REAL
+#define SDL_GetGlobalMouseState SDL_GetGlobalMouseState_REAL
--- a/src/dynapi/SDL_dynapi_procs.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/dynapi/SDL_dynapi_procs.h Wed Jun 25 17:06:12 2014 -0400
@@ -616,3 +616,6 @@
SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return)
SDL_DYNAPI_PROC(double,SDL_tan,(double a),(a),return)
SDL_DYNAPI_PROC(float,SDL_tanf,(float a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_SetWindowHitTest,(SDL_Window *a, SDL_HitTest b, void *c),(a,b,c),return)
+SDL_DYNAPI_PROC(Uint32,SDL_GetGlobalMouseState,(int *a, int *b),(a,b),return)
--- a/src/events/SDL_keyboard.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/events/SDL_keyboard.c Wed Jun 25 17:06:12 2014 -0400
@@ -25,6 +25,7 @@
#include "SDL_timer.h"
#include "SDL_events.h"
#include "SDL_events_c.h"
+#include "SDL_assert.h"
#include "../video/SDL_sysvideo.h"
@@ -619,6 +620,16 @@
/* See if the current window has lost focus */
if (keyboard->focus && keyboard->focus != window) {
+
+ /* new window shouldn't think it has mouse captured. */
+ SDL_assert(!window || !(window->flags & SDL_WINDOW_MOUSE_CAPTURE));
+
+ /* old window must lose an existing mouse capture. */
+ if (keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE) {
+ SDL_CaptureMouse(SDL_FALSE); /* drop the capture. */
+ SDL_assert(!(keyboard->focus->flags & SDL_WINDOW_MOUSE_CAPTURE));
+ }
+
SDL_SendWindowEvent(keyboard->focus, SDL_WINDOWEVENT_FOCUS_LOST,
0, 0);
--- a/src/events/SDL_mouse.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/events/SDL_mouse.c Wed Jun 25 17:06:12 2014 -0400
@@ -140,14 +140,14 @@
SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
{
SDL_Mouse *mouse = SDL_GetMouse();
- int w, h;
- SDL_bool inWindow;
+ SDL_bool inWindow = SDL_TRUE;
- SDL_GetWindowSize(window, &w, &h);
- if (x < 0 || y < 0 || x >= w || y >= h) {
- inWindow = SDL_FALSE;
- } else {
- inWindow = SDL_TRUE;
+ if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
+ int w, h;
+ SDL_GetWindowSize(window, &w, &h);
+ if (x < 0 || y < 0 || x >= w || y >= h) {
+ inWindow = SDL_FALSE;
+ }
}
/* Linux doesn't give you mouse events outside your window unless you grab
@@ -246,24 +246,29 @@
mouse->y += yrel;
}
- /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */
- SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
- --x_max;
- --y_max;
+ /* make sure that the pointers find themselves inside the windows,
+ unless we have the mouse captured. */
+ if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
+ int x_max = 0, y_max = 0;
+
+ // !!! FIXME: shouldn't this be (window) instead of (mouse->focus)?
+ SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
+ --x_max;
+ --y_max;
- /* make sure that the pointers find themselves inside the windows */
- if (mouse->x > x_max) {
- mouse->x = x_max;
- }
- if (mouse->x < 0) {
- mouse->x = 0;
- }
+ if (mouse->x > x_max) {
+ mouse->x = x_max;
+ }
+ if (mouse->x < 0) {
+ mouse->x = 0;
+ }
- if (mouse->y > y_max) {
- mouse->y = y_max;
- }
- if (mouse->y < 0) {
- mouse->y = 0;
+ if (mouse->y > y_max) {
+ mouse->y = y_max;
+ }
+ if (mouse->y < 0) {
+ mouse->y = 0;
+ }
}
mouse->xdelta += xrel;
@@ -426,6 +431,7 @@
SDL_Cursor *cursor, *next;
SDL_Mouse *mouse = SDL_GetMouse();
+ SDL_CaptureMouse(SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(1);
@@ -477,16 +483,42 @@
return mouse->buttonstate;
}
+Uint32
+SDL_GetGlobalMouseState(int *x, int *y)
+{
+ SDL_Mouse *mouse = SDL_GetMouse();
+ int tmpx, tmpy;
+
+ /* make sure these are never NULL for the backend implementations... */
+ if (!x) {
+ x = &tmpx;
+ }
+ if (!y) {
+ y = &tmpy;
+ }
+
+ *x = *y = 0;
+
+ if (!mouse->GetGlobalMouseState) {
+ SDL_assert(0 && "This should really be implemented for every target.");
+ return 0;
+ }
+
+ return mouse->GetGlobalMouseState(x, y);
+}
+
void
SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
{
SDL_Mouse *mouse = SDL_GetMouse();
- if (window == NULL)
+ if (window == NULL) {
window = mouse->focus;
+ }
- if (window == NULL)
+ if (window == NULL) {
return;
+ }
if (mouse->WarpMouse) {
mouse->WarpMouse(window, x, y);
--- a/src/events/SDL_mouse_c.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/events/SDL_mouse_c.h Wed Jun 25 17:06:12 2014 -0400
@@ -66,6 +66,12 @@
/* Set relative mode */
int (*SetRelativeMouseMode) (SDL_bool enabled);
+ /* Set mouse capture */
+ int (*CaptureMouse) (SDL_Window * window);
+
+ /* Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call. */
+ Uint32 (*GetGlobalMouseState) (int *x, int *y);
+
/* Data common to all mice */
SDL_MouseID mouseID;
SDL_Window *focus;
--- a/src/test/SDL_test_common.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/test/SDL_test_common.c Wed Jun 25 17:06:12 2014 -0400
@@ -1379,6 +1379,14 @@
}
}
}
+ if (withShift) {
+ SDL_Window *current_win = SDL_GetKeyboardFocus();
+ if (current_win) {
+ const SDL_bool shouldCapture = (SDL_GetWindowFlags(current_win) & SDL_WINDOW_MOUSE_CAPTURE) == 0;
+ const int rc = SDL_CaptureMouse(shouldCapture);
+ SDL_Log("%sapturing mouse %s!\n", shouldCapture ? "C" : "Unc", (rc == 0) ? "succeeded" : "failed");
+ }
+ }
break;
case SDLK_v:
if (withControl) {
@@ -1478,6 +1486,19 @@
}
}
break;
+ case SDLK_a:
+ if (withControl) {
+ /* Ctrl-A reports absolute mouse position. */
+ int x, y;
+ const Uint32 mask = SDL_GetGlobalMouseState(&x, &y);
+ SDL_Log("ABSOLUTE MOUSE: (%d, %d)%s%s%s%s%s\n", x, y,
+ (mask & SDL_BUTTON_LMASK) ? " [LBUTTON]" : "",
+ (mask & SDL_BUTTON_MMASK) ? " [MBUTTON]" : "",
+ (mask & SDL_BUTTON_RMASK) ? " [RBUTTON]" : "",
+ (mask & SDL_BUTTON_X1MASK) ? " [X2BUTTON]" : "",
+ (mask & SDL_BUTTON_X2MASK) ? " [X2BUTTON]" : "");
+ }
+ break;
case SDLK_0:
if (withControl) {
SDL_Window *window = SDL_GetWindowFromID(event->key.windowID);
--- a/src/video/SDL_sysvideo.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/SDL_sysvideo.h Wed Jun 25 17:06:12 2014 -0400
@@ -97,6 +97,9 @@
SDL_WindowShaper *shaper;
+ SDL_HitTest hit_test;
+ void *hit_test_data;
+
SDL_WindowUserData *data;
void *driverdata;
@@ -261,6 +264,9 @@
/* MessageBox */
int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
+ /* Hit-testing */
+ int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
+
/* * * */
/* Data common to all drivers */
SDL_bool suspend_screensaver;
--- a/src/video/SDL_video.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/SDL_video.c Wed Jun 25 17:06:12 2014 -0400
@@ -1428,6 +1428,11 @@
SDL_SetWindowIcon(window, icon);
SDL_FreeSurface(icon);
}
+
+ if (window->hit_test) {
+ _this->SetWindowHitTest(window, SDL_TRUE);
+ }
+
SDL_FinishWindowCreation(window, flags);
return 0;
@@ -3292,12 +3297,17 @@
int retval = -1;
SDL_bool relative_mode;
int show_cursor_prev;
+ SDL_bool mouse_captured;
+ SDL_Window *current_window;
if (!messageboxdata) {
return SDL_InvalidParamError("messageboxdata");
}
+ current_window = SDL_GetKeyboardFocus();
+ mouse_captured = current_window && ((SDL_GetWindowFlags(current_window) & SDL_WINDOW_MOUSE_CAPTURE) != 0);
relative_mode = SDL_GetRelativeMouseMode();
+ SDL_CaptureMouse(SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
show_cursor_prev = SDL_ShowCursor(1);
@@ -3349,6 +3359,13 @@
SDL_SetError("No message system available");
}
+ if (current_window) {
+ SDL_RaiseWindow(current_window);
+ if (mouse_captured) {
+ SDL_CaptureMouse(SDL_TRUE);
+ }
+ }
+
SDL_ShowCursor(show_cursor_prev);
SDL_SetRelativeMouseMode(relative_mode);
@@ -3391,4 +3408,21 @@
return SDL_TRUE;
}
+int
+SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata)
+{
+ CHECK_WINDOW_MAGIC(window, -1);
+
+ if (!_this->SetWindowHitTest) {
+ return SDL_Unsupported();
+ } else if (_this->SetWindowHitTest(window, callback != NULL) == -1) {
+ return -1;
+ }
+
+ window->hit_test = callback;
+ window->hit_test_data = userdata;
+
+ return 0;
+}
+
/* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/cocoa/SDL_cocoamouse.m Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/cocoa/SDL_cocoamouse.m Wed Jun 25 17:06:12 2014 -0400
@@ -307,6 +307,39 @@
return 0;
}
+static int
+Cocoa_CaptureMouse(SDL_Window *window)
+{
+ /* our Cocoa event code already tracks the mouse outside the window,
+ so all we have to do here is say "okay" and do what we always do. */
+ return 0;
+}
+
+static Uint32
+Cocoa_GetGlobalMouseState(int *x, int *y)
+{
+ const NSUInteger cocoaButtons = [NSEvent pressedMouseButtons];
+ const NSPoint cocoaLocation = [NSEvent mouseLocation];
+ Uint32 retval = 0;
+
+ for (NSScreen *screen in [NSScreen screens]) {
+ NSRect frame = [screen frame];
+ if (NSPointInRect(cocoaLocation, frame)) {
+ *x = (int) cocoaLocation.x;
+ *y = (int) ((frame.origin.y + frame.size.height) - cocoaLocation.y);
+ break;
+ }
+ }
+
+ retval |= (cocoaButtons & (1 << 0)) ? SDL_BUTTON_LMASK : 0;
+ retval |= (cocoaButtons & (1 << 1)) ? SDL_BUTTON_RMASK : 0;
+ retval |= (cocoaButtons & (1 << 2)) ? SDL_BUTTON_MMASK : 0;
+ retval |= (cocoaButtons & (1 << 3)) ? SDL_BUTTON_X1MASK : 0;
+ retval |= (cocoaButtons & (1 << 4)) ? SDL_BUTTON_X2MASK : 0;
+
+ return retval;
+}
+
void
Cocoa_InitMouse(_THIS)
{
@@ -321,6 +354,8 @@
mouse->WarpMouse = Cocoa_WarpMouse;
mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
+ mouse->CaptureMouse = Cocoa_CaptureMouse;
+ mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());
--- a/src/video/cocoa/SDL_cocoavideo.m Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/cocoa/SDL_cocoavideo.m Wed Jun 25 17:06:12 2014 -0400
@@ -108,6 +108,7 @@
device->SetWindowGrab = Cocoa_SetWindowGrab;
device->DestroyWindow = Cocoa_DestroyWindow;
device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
+ device->SetWindowHitTest = Cocoa_SetWindowHitTest;
device->shape_driver.CreateShaper = Cocoa_CreateShaper;
device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;
--- a/src/video/cocoa/SDL_cocoawindow.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/cocoa/SDL_cocoawindow.h Wed Jun 25 17:06:12 2014 -0400
@@ -45,6 +45,7 @@
PendingWindowOperation pendingWindowOperation;
BOOL isMoving;
int pendingWindowWarpX, pendingWindowWarpY;
+ BOOL isDragAreaRunning;
}
-(void) listen:(SDL_WindowData *) data;
@@ -75,6 +76,9 @@
-(void) windowDidExitFullScreen:(NSNotification *) aNotification;
-(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions;
+/* See if event is in a drag area, toggle on window dragging. */
+-(BOOL) processHitTest:(NSEvent *)theEvent;
+
/* Window event handling */
-(void) mouseDown:(NSEvent *) theEvent;
-(void) rightMouseDown:(NSEvent *) theEvent;
@@ -138,8 +142,8 @@
extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
-extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window,
- struct SDL_SysWMinfo *info);
+extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
+extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_cocoawindow_h */
--- a/src/video/cocoa/SDL_cocoawindow.m Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/cocoa/SDL_cocoawindow.m Wed Jun 25 17:06:12 2014 -0400
@@ -192,6 +192,7 @@
inFullscreenTransition = NO;
pendingWindowOperation = PENDING_OPERATION_NONE;
isMoving = NO;
+ isDragAreaRunning = NO;
center = [NSNotificationCenter defaultCenter];
@@ -663,10 +664,48 @@
/*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
}
+/* We'll respond to selectors by doing nothing so we don't beep.
+ * The escape key gets converted to a "cancel" selector, etc.
+ */
+- (void)doCommandBySelector:(SEL)aSelector
+{
+ /*NSLog(@"doCommandBySelector: %@\n", NSStringFromSelector(aSelector));*/
+}
+
+- (BOOL)processHitTest:(NSEvent *)theEvent
+{
+ SDL_assert(isDragAreaRunning == [_data->nswindow isMovableByWindowBackground]);
+
+ if (_data->window->hit_test) { /* if no hit-test, skip this. */
+ const NSPoint location = [theEvent locationInWindow];
+ const SDL_Point point = { (int) location.x, _data->window->h - (((int) location.y)-1) };
+ const SDL_HitTestResult rc = _data->window->hit_test(_data->window, &point, _data->window->hit_test_data);
+ if (rc == SDL_HITTEST_DRAGGABLE) {
+ if (!isDragAreaRunning) {
+ isDragAreaRunning = YES;
+ [_data->nswindow setMovableByWindowBackground:YES];
+ }
+ return YES; /* dragging! */
+ }
+ }
+
+ if (isDragAreaRunning) {
+ isDragAreaRunning = NO;
+ [_data->nswindow setMovableByWindowBackground:NO];
+ return YES; /* was dragging, drop event. */
+ }
+
+ return NO; /* not a special area, carry on. */
+}
+
- (void)mouseDown:(NSEvent *)theEvent
{
int button;
+ if ([self processHitTest:theEvent]) {
+ return; /* dragging, drop event. */
+ }
+
switch ([theEvent buttonNumber]) {
case 0:
if (([theEvent modifierFlags] & NSControlKeyMask) &&
@@ -705,6 +744,10 @@
{
int button;
+ if ([self processHitTest:theEvent]) {
+ return; /* stopped dragging, drop event. */
+ }
+
switch ([theEvent buttonNumber]) {
case 0:
if (wasCtrlLeft) {
@@ -744,6 +787,10 @@
NSPoint point;
int x, y;
+ if ([self processHitTest:theEvent]) {
+ return; /* dragging, drop event. */
+ }
+
if (mouse->relative_mode) {
return;
}
@@ -752,8 +799,8 @@
x = (int)point.x;
y = (int)(window->h - point.y);
- if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
- if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
+ if (window->flags & SDL_WINDOW_INPUT_GRABBED) {
+ if (x < 0 || x >= window->w || y < 0 || y >= window->h) {
if (x < 0) {
x = 0;
} else if (x >= window->w) {
@@ -890,6 +937,7 @@
/* The default implementation doesn't pass rightMouseDown to responder chain */
- (void)rightMouseDown:(NSEvent *)theEvent;
+- (BOOL)mouseDownCanMoveWindow;
@end
@implementation SDLView
@@ -898,6 +946,14 @@
[[self nextResponder] rightMouseDown:theEvent];
}
+- (BOOL)mouseDownCanMoveWindow
+{
+ /* Always say YES, but this doesn't do anything until we call
+ -[NSWindow setMovableByWindowBackground:YES], which we ninja-toggle
+ during mouse events when we're using a drag area. */
+ return YES;
+}
+
- (void)resetCursorRects
{
[super resetCursorRects];
@@ -995,6 +1051,7 @@
/* All done! */
[pool release];
+ window->driverdata = data;
return 0;
}
@@ -1566,6 +1623,12 @@
return succeeded;
}
+int
+Cocoa_SetWindowHitTest(SDL_Window * window, SDL_bool enabled)
+{
+ return 0; /* just succeed, the real work is done elsewhere. */
+}
+
#endif /* SDL_VIDEO_DRIVER_COCOA */
/* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/windows/SDL_windowsevents.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/windows/SDL_windowsevents.c Wed Jun 25 17:06:12 2014 -0400
@@ -30,6 +30,7 @@
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_touch_c.h"
#include "../../events/scancodes_windows.h"
+#include "SDL_assert.h"
/* Dropfile support */
#include <shellapi.h>
@@ -438,33 +439,55 @@
HRAWINPUT hRawInput = (HRAWINPUT)lParam;
RAWINPUT inp;
UINT size = sizeof(inp);
+ const SDL_bool isRelative = mouse->relative_mode || mouse->relative_mode_warp;
+ const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
- if (!mouse->relative_mode || mouse->relative_mode_warp || mouse->focus != data->window) {
- break;
+ if (!isRelative || mouse->focus != data->window) {
+ if (!isCapture) {
+ break;
+ }
}
GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
/* Mouse data */
if (inp.header.dwType == RIM_TYPEMOUSE) {
- RAWMOUSE* mouse = &inp.data.mouse;
+ if (isRelative) {
+ RAWMOUSE* mouse = &inp.data.mouse;
- if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
- SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
- } else {
- /* synthesize relative moves from the abs position */
- static SDL_Point initialMousePoint;
- if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
+ if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
+ SDL_SendMouseMotion(data->window, 0, 1, (int)mouse->lLastX, (int)mouse->lLastY);
+ } else {
+ /* synthesize relative moves from the abs position */
+ static SDL_Point initialMousePoint;
+ if (initialMousePoint.x == 0 && initialMousePoint.y == 0) {
+ initialMousePoint.x = mouse->lLastX;
+ initialMousePoint.y = mouse->lLastY;
+ }
+
+ SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) );
+
initialMousePoint.x = mouse->lLastX;
initialMousePoint.y = mouse->lLastY;
}
-
- SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y));
-
- initialMousePoint.x = mouse->lLastX;
- initialMousePoint.y = mouse->lLastY;
+ WIN_CheckRawMouseButtons( mouse->usButtonFlags, data );
+ } else if (isCapture) {
+ /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */
+ POINT pt;
+ HWND hwnd = data->hwnd;
+ GetCursorPos(&pt);
+ if (WindowFromPoint(pt) != hwnd) { /* if in the window, WM_MOUSEMOVE, etc, will cover it. */
+ ScreenToClient(data->hwnd, &pt);
+ SDL_SendMouseMotion(data->window, 0, 0, (int) pt.x, (int) pt.y);
+ SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT);
+ SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT);
+ SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
+ SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
+ SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
+ }
+ } else {
+ SDL_assert(0 && "Shouldn't happen");
}
- WIN_CheckRawMouseButtons(mouse->usButtonFlags, data);
}
}
break;
@@ -509,7 +532,7 @@
#ifdef WM_MOUSELEAVE
case WM_MOUSELEAVE:
- if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode) {
+ if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
if (!IsIconic(hwnd)) {
POINT cursorPos;
GetCursorPos(&cursorPos);
@@ -863,6 +886,32 @@
return 0;
}
break;
+
+ case WM_NCHITTEST:
+ {
+ SDL_Window *window = data->window;
+ if (window->hit_test) {
+ POINT winpoint = { (int) LOWORD(lParam), (int) HIWORD(lParam) };
+ if (ScreenToClient(data->hwnd, &winpoint)) {
+ const SDL_Point point = { (int) winpoint.x, (int) winpoint.y };
+ const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
+ switch (rc) {
+ case SDL_HITTEST_DRAGGABLE: return HTCAPTION;
+ case SDL_HITTEST_RESIZE_TOPLEFT: return HTTOPLEFT;
+ case SDL_HITTEST_RESIZE_TOP: return HTTOP;
+ case SDL_HITTEST_RESIZE_TOPRIGHT: return HTTOPRIGHT;
+ case SDL_HITTEST_RESIZE_RIGHT: return HTRIGHT;
+ case SDL_HITTEST_RESIZE_BOTTOMRIGHT: return HTBOTTOMRIGHT;
+ case SDL_HITTEST_RESIZE_BOTTOM: return HTBOTTOM;
+ case SDL_HITTEST_RESIZE_BOTTOMLEFT: return HTBOTTOMLEFT;
+ case SDL_HITTEST_RESIZE_LEFT: return HTLEFT;
+ }
+ }
+ /* If we didn't return, this will call DefWindowProc below. */
+ }
+ }
+ break;
+
}
/* If there's a window proc, assume it's going to handle messages */
--- a/src/video/windows/SDL_windowsmouse.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/windows/SDL_windowsmouse.c Wed Jun 25 17:06:12 2014 -0400
@@ -30,6 +30,44 @@
HCURSOR SDL_cursor = NULL;
+static int rawInputEnableCount = 0;
+
+static int
+ToggleRawInput(SDL_bool enabled)
+{
+ RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
+
+ if (enabled) {
+ rawInputEnableCount++;
+ if (rawInputEnableCount > 1) {
+ return 0; /* already done. */
+ }
+ } else {
+ if (rawInputEnableCount == 0) {
+ return 0; /* already done. */
+ }
+ rawInputEnableCount--;
+ if (rawInputEnableCount > 0) {
+ return 0; /* not time to disable yet */
+ }
+ }
+
+ if (!enabled) {
+ rawMouse.dwFlags |= RIDEV_REMOVE;
+ }
+
+ /* (Un)register raw input for mice */
+ if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
+
+ /* Only return an error when registering. If we unregister and fail,
+ then it's probably that we unregistered twice. That's OK. */
+ if (enabled) {
+ return SDL_Unsupported();
+ }
+ }
+ return 0;
+}
+
static SDL_Cursor *
WIN_CreateDefaultCursor()
@@ -211,22 +249,43 @@
static int
WIN_SetRelativeMouseMode(SDL_bool enabled)
{
- RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */
+ return ToggleRawInput(enabled);
+}
- if (!enabled) {
- rawMouse.dwFlags |= RIDEV_REMOVE;
+static int
+WIN_CaptureMouse(SDL_Window *window)
+{
+ if (!window) {
+ SDL_Window *focusWin = SDL_GetKeyboardFocus();
+ if (focusWin) {
+ SDL_WindowData *data = (SDL_WindowData *)focusWin->driverdata;
+ WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin); /* make sure WM_MOUSELEAVE messages are (re)enabled. */
+ }
}
- /* (Un)register raw input for mice */
- if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
+ /* While we were thinking of SetCapture() when designing this API in SDL,
+ we didn't count on the fact that SetCapture() only tracks while the
+ left mouse button is held down! Instead, we listen for raw mouse input
+ and manually query the mouse when it leaves the window. :/ */
+ return ToggleRawInput(window != NULL);
+}
- /* Only return an error when registering. If we unregister and fail,
- then it's probably that we unregistered twice. That's OK. */
- if (enabled) {
- return SDL_Unsupported();
- }
- }
- return 0;
+static Uint32
+WIN_GetGlobalMouseState(int *x, int *y)
+{
+ Uint32 retval = 0;
+ POINT pt = { 0, 0 };
+ GetCursorPos(&pt);
+ *x = (int) pt.x;
+ *y = (int) pt.y;
+
+ retval |= GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0;
+ retval |= GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0;
+ retval |= GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_BUTTON_MMASK : 0;
+ retval |= GetAsyncKeyState(VK_X1BUTTON) & 0x8000 ? SDL_BUTTON_X1MASK : 0;
+ retval |= GetAsyncKeyState(VK_X2BUTTON) & 0x8000 ? SDL_BUTTON_X2MASK : 0;
+
+ return retval;
}
void
@@ -241,6 +300,8 @@
mouse->WarpMouse = WIN_WarpMouse;
mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
+ mouse->CaptureMouse = WIN_CaptureMouse;
+ mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
@@ -256,6 +317,11 @@
mouse->def_cursor = NULL;
mouse->cur_cursor = NULL;
}
+
+ if (rawInputEnableCount) { /* force RAWINPUT off here. */
+ rawInputEnableCount = 1;
+ ToggleRawInput(SDL_FALSE);
+ }
}
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
--- a/src/video/windows/SDL_windowsvideo.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/windows/SDL_windowsvideo.c Wed Jun 25 17:06:12 2014 -0400
@@ -144,6 +144,7 @@
device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
device->OnWindowEnter = WIN_OnWindowEnter;
+ device->SetWindowHitTest = WIN_SetWindowHitTest;
device->shape_driver.CreateShaper = Win32_CreateShaper;
device->shape_driver.SetWindowShape = Win32_SetWindowShape;
--- a/src/video/windows/SDL_windowswindow.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/windows/SDL_windowswindow.c Wed Jun 25 17:06:12 2014 -0400
@@ -785,6 +785,12 @@
}
}
+int
+WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
+{
+ return 0; /* just succeed, the real work is done elsewhere. */
+}
+
#endif /* SDL_VIDEO_DRIVER_WINDOWS */
/* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/windows/SDL_windowswindow.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/windows/SDL_windowswindow.h Wed Jun 25 17:06:12 2014 -0400
@@ -69,6 +69,7 @@
struct SDL_SysWMinfo *info);
extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
extern void WIN_UpdateClipCursor(SDL_Window *window);
+extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_windowswindow_h */
--- a/src/video/x11/SDL_x11events.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/x11/SDL_x11events.c Wed Jun 25 17:06:12 2014 -0400
@@ -40,6 +40,42 @@
#include <stdio.h>
+#ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_TOP
+#define _NET_WM_MOVERESIZE_SIZE_TOP 1
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_SIZE_LEFT
+#define _NET_WM_MOVERESIZE_SIZE_LEFT 7
+#endif
+
+#ifndef _NET_WM_MOVERESIZE_MOVE
+#define _NET_WM_MOVERESIZE_MOVE 8
+#endif
+
typedef struct {
unsigned char *data;
int format, count;
@@ -348,6 +384,124 @@
}
static void
+InitiateWindowMove(_THIS, const SDL_WindowData *data, const SDL_Point *point)
+{
+ SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
+ SDL_Window* window = data->window;
+ Display *display = viddata->display;
+
+ /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
+ X11_XUngrabPointer(display, 0L);
+ X11_XFlush(display);
+
+ XEvent evt;
+ evt.xclient.type = ClientMessage;
+ evt.xclient.window = data->xwindow;
+ evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
+ evt.xclient.format = 32;
+ evt.xclient.data.l[0] = window->x + point->x;
+ evt.xclient.data.l[1] = window->y + point->y;
+ evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
+ evt.xclient.data.l[3] = Button1;
+ evt.xclient.data.l[4] = 0;
+ X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
+
+ X11_XSync(display, 0);
+}
+
+static void
+InitiateWindowResize(_THIS, const SDL_WindowData *data, const SDL_Point *point, int direction)
+{
+ SDL_VideoData *viddata = (SDL_VideoData *) _this->driverdata;
+ SDL_Window* window = data->window;
+ Display *display = viddata->display;
+
+ if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT)
+ return;
+
+ /* !!! FIXME: we need to regrab this if necessary when the drag is done. */
+ X11_XUngrabPointer(display, 0L);
+ X11_XFlush(display);
+
+ XEvent evt;
+ evt.xclient.type = ClientMessage;
+ evt.xclient.window = data->xwindow;
+ evt.xclient.message_type = X11_XInternAtom(display, "_NET_WM_MOVERESIZE", True);
+ evt.xclient.format = 32;
+ evt.xclient.data.l[0] = window->x + point->x;
+ evt.xclient.data.l[1] = window->y + point->y;
+ evt.xclient.data.l[2] = direction;
+ evt.xclient.data.l[3] = Button1;
+ evt.xclient.data.l[4] = 0;
+ X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt);
+
+ X11_XSync(display, 0);
+}
+
+static SDL_bool
+ProcessHitTest(_THIS, const SDL_WindowData *data, const XEvent *xev)
+{
+ SDL_Window *window = data->window;
+ SDL_bool ret = SDL_FALSE;
+
+ if (window->hit_test) {
+ const SDL_Point point = { xev->xbutton.x, xev->xbutton.y };
+ const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data);
+ switch (rc) {
+ case SDL_HITTEST_DRAGGABLE: {
+ InitiateWindowMove(_this, data, &point);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_TOPLEFT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOPLEFT);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_TOP: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOP);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_TOPRIGHT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_TOPRIGHT);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_RIGHT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_RIGHT);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_BOTTOMRIGHT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_BOTTOM: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOM);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_BOTTOMLEFT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT);
+ ret = SDL_TRUE;
+ }
+ break;
+ case SDL_HITTEST_RESIZE_LEFT: {
+ InitiateWindowResize(_this, data, &point, _NET_WM_MOVERESIZE_SIZE_LEFT);
+ ret = SDL_TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void
X11_DispatchEvent(_THIS)
{
SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
@@ -820,6 +974,11 @@
if (X11_IsWheelEvent(display,&xevent,&ticks)) {
SDL_SendMouseWheel(data->window, 0, 0, ticks);
} else {
+ if(xevent.xbutton.button == Button1) {
+ if (ProcessHitTest(_this, data, &xevent)) {
+ break; /* don't pass this event on to app. */
+ }
+ }
SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
}
}
--- a/src/video/x11/SDL_x11mouse.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/x11/SDL_x11mouse.c Wed Jun 25 17:06:12 2014 -0400
@@ -339,6 +339,62 @@
return -1;
}
+static int
+X11_CaptureMouse(SDL_Window *window)
+{
+ Display *display = GetDisplay();
+
+ if (window) {
+ SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+ const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
+ const int rc = X11_XGrabPointer(display, data->xwindow, False,
+ mask, GrabModeAsync, GrabModeAsync,
+ None, None, CurrentTime);
+ if (rc != GrabSuccess) {
+ return SDL_SetError("X server refused mouse capture");
+ }
+ } else {
+ X11_XUngrabPointer(display, CurrentTime);
+ }
+
+ X11_XSync(display, False);
+
+ return 0;
+}
+
+static Uint32
+X11_GetGlobalMouseState(int *x, int *y)
+{
+ Display *display = GetDisplay();
+ const int num_screens = SDL_GetNumVideoDisplays();
+ int i;
+
+ /* !!! FIXME: should we XSync() here first? */
+
+ for (i = 0; i < num_screens; i++) {
+ SDL_DisplayData *data = (SDL_DisplayData *) SDL_GetDisplayDriverData(i);
+ if (data != NULL) {
+ Window root, child;
+ int rootx, rooty, winx, winy;
+ unsigned int mask;
+ if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
+ Uint32 retval = 0;
+ retval |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
+ retval |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
+ retval |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
+ *x = data->x + rootx;
+ *y = data->y + rooty;
+ return retval;
+ }
+ }
+ }
+
+ SDL_assert(0 && "The pointer wasn't on any X11 screen?!");
+
+ return 0;
+}
+
+
void
X11_InitMouse(_THIS)
{
@@ -351,6 +407,8 @@
mouse->WarpMouse = X11_WarpMouse;
mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
+ mouse->CaptureMouse = X11_CaptureMouse;
+ mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
SDL_SetDefaultCursor(X11_CreateDefaultCursor());
}
--- a/src/video/x11/SDL_x11video.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/x11/SDL_x11video.c Wed Jun 25 17:06:12 2014 -0400
@@ -243,6 +243,7 @@
device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
device->GetWindowWMInfo = X11_GetWindowWMInfo;
+ device->SetWindowHitTest = X11_SetWindowHitTest;
device->shape_driver.CreateShaper = X11_CreateShaper;
device->shape_driver.SetWindowShape = X11_SetWindowShape;
--- a/src/video/x11/SDL_x11window.c Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/x11/SDL_x11window.c Wed Jun 25 17:06:12 2014 -0400
@@ -1445,6 +1445,12 @@
}
}
+int
+X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
+{
+ return 0; /* just succeed, the real work is done elsewhere. */
+}
+
#endif /* SDL_VIDEO_DRIVER_X11 */
/* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/x11/SDL_x11window.h Wed Jun 25 02:08:37 2014 -0700
+++ b/src/video/x11/SDL_x11window.h Wed Jun 25 17:06:12 2014 -0400
@@ -93,6 +93,7 @@
extern void X11_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo *info);
+extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_x11window_h */
--- a/test/Makefile.in Wed Jun 25 02:08:37 2014 -0700
+++ b/test/Makefile.in Wed Jun 25 17:06:12 2014 -0400
@@ -23,6 +23,7 @@
testgles$(EXE) \
testgles2$(EXE) \
testhaptic$(EXE) \
+ testhittesting$(EXE) \
testrumble$(EXE) \
testhotplug$(EXE) \
testthread$(EXE) \
@@ -108,6 +109,9 @@
testrelative$(EXE): $(srcdir)/testrelative.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+testhittesting$(EXE): $(srcdir)/testhittesting.c
+ $(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
testdraw2$(EXE): $(srcdir)/testdraw2.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testhittesting.c Wed Jun 25 17:06:12 2014 -0400
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include "SDL.h"
+
+/* !!! FIXME: rewrite this to be wired in to test framework. */
+
+#define RESIZE_BORDER 20
+
+const SDL_Rect drag_areas[] = {
+ { 20, 20, 100, 100 },
+ { 200, 70, 100, 100 },
+ { 400, 90, 100, 100 }
+};
+
+static const SDL_Rect *areas = drag_areas;
+static int numareas = SDL_arraysize(drag_areas);
+
+static SDL_HitTestResult
+hitTest(SDL_Window *window, const SDL_Point *pt, void *data)
+{
+ int i;
+ int w, h;
+
+ for (i = 0; i < numareas; i++) {
+ if (SDL_PointInRect(pt, &areas[i])) {
+ SDL_Log("HIT-TEST: DRAGGABLE\n");
+ return SDL_HITTEST_DRAGGABLE;
+ }
+ }
+
+ SDL_GetWindowSize(window, &w, &h);
+
+ #define REPORT_RESIZE_HIT(name) { \
+ SDL_Log("HIT-TEST: RESIZE_" #name "\n"); \
+ return SDL_HITTEST_RESIZE_##name; \
+ }
+
+ if (pt->x < RESIZE_BORDER && pt->y < RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(TOPLEFT);
+ } else if (pt->x > RESIZE_BORDER && pt->x < w - RESIZE_BORDER && pt->y < RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(TOP);
+ } else if (pt->x > w - RESIZE_BORDER && pt->y < RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(TOPRIGHT);
+ } else if (pt->x > w - RESIZE_BORDER && pt->y > RESIZE_BORDER && pt->y < h - RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(RIGHT);
+ } else if (pt->x > w - RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(BOTTOMRIGHT);
+ } else if (pt->x < w - RESIZE_BORDER && pt->x > RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(BOTTOM);
+ } else if (pt->x < RESIZE_BORDER && pt->y > h - RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(BOTTOMLEFT);
+ } else if (pt->x < RESIZE_BORDER && pt->y < h - RESIZE_BORDER && pt->y > RESIZE_BORDER) {
+ REPORT_RESIZE_HIT(LEFT);
+ }
+
+ SDL_Log("HIT-TEST: NORMAL\n");
+ return SDL_HITTEST_NORMAL;
+}
+
+
+int main(int argc, char **argv)
+{
+ int done = 0;
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+
+ /* !!! FIXME: check for errors. */
+ SDL_Init(SDL_INIT_VIDEO);
+ window = SDL_CreateWindow("Drag the red boxes", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE);
+ renderer = SDL_CreateRenderer(window, -1, 0);
+
+ if (SDL_SetWindowHitTest(window, hitTest, NULL) == -1) {
+ SDL_Log("Enabling hit-testing failed!\n");
+ SDL_Quit();
+ return 1;
+ }
+
+ while (!done)
+ {
+ SDL_Event e;
+ int nothing_to_do = 1;
+
+ SDL_SetRenderDrawColor(renderer, 0, 0, 127, 255);
+ SDL_RenderClear(renderer);
+ SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
+ SDL_RenderFillRects(renderer, areas, SDL_arraysize(drag_areas));
+ SDL_RenderPresent(renderer);
+
+ while (SDL_PollEvent(&e)) {
+ nothing_to_do = 0;
+
+ switch (e.type)
+ {
+ case SDL_MOUSEBUTTONDOWN:
+ SDL_Log("button down!\n");
+ break;
+
+ case SDL_MOUSEBUTTONUP:
+ SDL_Log("button up!\n");
+ break;
+
+ case SDL_WINDOWEVENT:
+ if (e.window.event == SDL_WINDOWEVENT_MOVED) {
+ SDL_Log("Window event moved to (%d, %d)!\n", (int) e.window.data1, (int) e.window.data2);
+ }
+ break;
+
+ case SDL_KEYDOWN:
+ if (e.key.keysym.sym == SDLK_ESCAPE) {
+ done = 1;
+ } else if (e.key.keysym.sym == SDLK_x) {
+ if (!areas) {
+ areas = drag_areas;
+ numareas = SDL_arraysize(drag_areas);
+ } else {
+ areas = NULL;
+ numareas = 0;
+ }
+ }
+ break;
+
+ case SDL_QUIT:
+ done = 1;
+ break;
+ }
+ }
+
+ if (nothing_to_do) {
+ SDL_Delay(50);
+ }
+ }
+
+ SDL_Quit();
+ return 0;
+}