Merged Ryan's SDL-gui-backend branch.

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

- SDL_CaptureMouse()
- SDL_GetGlobalMouseState()
- SDL_SetWindowHitTest()
This commit is contained in:
Ryan C. Gordon 2014-06-25 17:06:12 -04:00
commit b273873297
28 changed files with 901 additions and 59 deletions

View file

@ -84,6 +84,7 @@ test/testgesture
test/testgl2 test/testgl2
test/testgles test/testgles
test/testhaptic test/testhaptic
test/testhittesting
test/testiconv test/testiconv
test/testime test/testime
test/testintersections test/testintersections

View file

@ -77,6 +77,31 @@ extern DECLSPEC SDL_Window * SDLCALL SDL_GetMouseFocus(void);
*/ */
extern DECLSPEC Uint32 SDLCALL SDL_GetMouseState(int *x, int *y); 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. * \brief Retrieve the relative state of the mouse.
* *
@ -126,6 +151,37 @@ extern DECLSPEC void SDLCALL SDL_WarpMouseGlobal(int x, int y);
*/ */
extern DECLSPEC int SDLCALL SDL_SetRelativeMouseMode(SDL_bool enabled); 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. * \brief Query whether relative mouse mode is enabled.
* *

View file

@ -43,6 +43,7 @@ extern "C" {
* \brief The structure that defines a point * \brief The structure that defines a point
* *
* \sa SDL_EnclosePoints * \sa SDL_EnclosePoints
* \sa SDL_PointInRect
*/ */
typedef struct SDL_Point typedef struct SDL_Point
{ {
@ -66,6 +67,15 @@ typedef struct SDL_Rect
int w, h; int w, h;
} SDL_Rect; } 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. * \brief Returns true if the rectangle has no area.
*/ */

View file

@ -108,7 +108,8 @@ typedef enum
SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */ SDL_WINDOW_MOUSE_FOCUS = 0x00000400, /**< window has mouse focus */
SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ 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; } SDL_WindowFlags;
/** /**
@ -790,6 +791,75 @@ extern DECLSPEC int SDLCALL SDL_GetWindowGammaRamp(SDL_Window * window,
Uint16 * green, Uint16 * green,
Uint16 * blue); 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. * \brief Destroy a window.
*/ */

View file

@ -584,3 +584,6 @@
#define SDL_sqrtf SDL_sqrtf_REAL #define SDL_sqrtf SDL_sqrtf_REAL
#define SDL_tan SDL_tan_REAL #define SDL_tan SDL_tan_REAL
#define SDL_tanf SDL_tanf_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

View file

@ -616,3 +616,6 @@ SDL_DYNAPI_PROC(void,SDL_WarpMouseGlobal,(int a, int b),(a,b),)
SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return) SDL_DYNAPI_PROC(float,SDL_sqrtf,(float a),(a),return)
SDL_DYNAPI_PROC(double,SDL_tan,(double 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(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)

View file

@ -25,6 +25,7 @@
#include "SDL_timer.h" #include "SDL_timer.h"
#include "SDL_events.h" #include "SDL_events.h"
#include "SDL_events_c.h" #include "SDL_events_c.h"
#include "SDL_assert.h"
#include "../video/SDL_sysvideo.h" #include "../video/SDL_sysvideo.h"
@ -619,6 +620,16 @@ SDL_SetKeyboardFocus(SDL_Window * window)
/* See if the current window has lost focus */ /* See if the current window has lost focus */
if (keyboard->focus && keyboard->focus != window) { 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, SDL_SendWindowEvent(keyboard->focus, SDL_WINDOWEVENT_FOCUS_LOST,
0, 0); 0, 0);

View file

@ -140,14 +140,14 @@ static SDL_bool
SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate) SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
int w, h; SDL_bool inWindow = SDL_TRUE;
SDL_bool inWindow;
if ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0) {
int w, h;
SDL_GetWindowSize(window, &w, &h); SDL_GetWindowSize(window, &w, &h);
if (x < 0 || y < 0 || x >= w || y >= h) { if (x < 0 || y < 0 || x >= w || y >= h) {
inWindow = SDL_FALSE; inWindow = SDL_FALSE;
} else { }
inWindow = SDL_TRUE;
} }
/* Linux doesn't give you mouse events outside your window unless you grab /* Linux doesn't give you mouse events outside your window unless you grab
@ -246,12 +246,16 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
mouse->y += yrel; mouse->y += yrel;
} }
/* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */ /* 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); SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
--x_max; --x_max;
--y_max; --y_max;
/* make sure that the pointers find themselves inside the windows */
if (mouse->x > x_max) { if (mouse->x > x_max) {
mouse->x = x_max; mouse->x = x_max;
} }
@ -265,6 +269,7 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
if (mouse->y < 0) { if (mouse->y < 0) {
mouse->y = 0; mouse->y = 0;
} }
}
mouse->xdelta += xrel; mouse->xdelta += xrel;
mouse->ydelta += yrel; mouse->ydelta += yrel;
@ -426,6 +431,7 @@ SDL_MouseQuit(void)
SDL_Cursor *cursor, *next; SDL_Cursor *cursor, *next;
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
SDL_CaptureMouse(SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(1); SDL_ShowCursor(1);
@ -477,16 +483,42 @@ SDL_GetRelativeMouseState(int *x, int *y)
return mouse->buttonstate; 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 void
SDL_WarpMouseInWindow(SDL_Window * window, int x, int y) SDL_WarpMouseInWindow(SDL_Window * window, int x, int y)
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
if (window == NULL) if (window == NULL) {
window = mouse->focus; window = mouse->focus;
}
if (window == NULL) if (window == NULL) {
return; return;
}
if (mouse->WarpMouse) { if (mouse->WarpMouse) {
mouse->WarpMouse(window, x, y); mouse->WarpMouse(window, x, y);

View file

@ -66,6 +66,12 @@ typedef struct
/* Set relative mode */ /* Set relative mode */
int (*SetRelativeMouseMode) (SDL_bool enabled); 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 */ /* Data common to all mice */
SDL_MouseID mouseID; SDL_MouseID mouseID;
SDL_Window *focus; SDL_Window *focus;

View file

@ -1379,6 +1379,14 @@ SDLTest_CommonEvent(SDLTest_CommonState * state, SDL_Event * event, int *done)
} }
} }
} }
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; break;
case SDLK_v: case SDLK_v:
if (withControl) { if (withControl) {
@ -1478,6 +1486,19 @@ SDLTest_CommonEvent(SDLTest_CommonState * state, SDL_Event * event, int *done)
} }
} }
break; 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: case SDLK_0:
if (withControl) { if (withControl) {
SDL_Window *window = SDL_GetWindowFromID(event->key.windowID); SDL_Window *window = SDL_GetWindowFromID(event->key.windowID);

View file

@ -97,6 +97,9 @@ struct SDL_Window
SDL_WindowShaper *shaper; SDL_WindowShaper *shaper;
SDL_HitTest hit_test;
void *hit_test_data;
SDL_WindowUserData *data; SDL_WindowUserData *data;
void *driverdata; void *driverdata;
@ -261,6 +264,9 @@ struct SDL_VideoDevice
/* MessageBox */ /* MessageBox */
int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid); int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
/* Hit-testing */
int (*SetWindowHitTest)(SDL_Window * window, SDL_bool enabled);
/* * * */ /* * * */
/* Data common to all drivers */ /* Data common to all drivers */
SDL_bool suspend_screensaver; SDL_bool suspend_screensaver;

View file

@ -1428,6 +1428,11 @@ SDL_RecreateWindow(SDL_Window * window, Uint32 flags)
SDL_SetWindowIcon(window, icon); SDL_SetWindowIcon(window, icon);
SDL_FreeSurface(icon); SDL_FreeSurface(icon);
} }
if (window->hit_test) {
_this->SetWindowHitTest(window, SDL_TRUE);
}
SDL_FinishWindowCreation(window, flags); SDL_FinishWindowCreation(window, flags);
return 0; return 0;
@ -3292,12 +3297,17 @@ SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
int retval = -1; int retval = -1;
SDL_bool relative_mode; SDL_bool relative_mode;
int show_cursor_prev; int show_cursor_prev;
SDL_bool mouse_captured;
SDL_Window *current_window;
if (!messageboxdata) { if (!messageboxdata) {
return SDL_InvalidParamError("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(); relative_mode = SDL_GetRelativeMouseMode();
SDL_CaptureMouse(SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
show_cursor_prev = SDL_ShowCursor(1); show_cursor_prev = SDL_ShowCursor(1);
@ -3349,6 +3359,13 @@ SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
SDL_SetError("No message system available"); 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_ShowCursor(show_cursor_prev);
SDL_SetRelativeMouseMode(relative_mode); SDL_SetRelativeMouseMode(relative_mode);
@ -3391,4 +3408,21 @@ SDL_ShouldAllowTopmost(void)
return SDL_TRUE; 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: */ /* vi: set ts=4 sw=4 expandtab: */

View file

@ -307,6 +307,39 @@ Cocoa_SetRelativeMouseMode(SDL_bool enabled)
return 0; 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 void
Cocoa_InitMouse(_THIS) Cocoa_InitMouse(_THIS)
{ {
@ -321,6 +354,8 @@ Cocoa_InitMouse(_THIS)
mouse->WarpMouse = Cocoa_WarpMouse; mouse->WarpMouse = Cocoa_WarpMouse;
mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal; mouse->WarpMouseGlobal = Cocoa_WarpMouseGlobal;
mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode; mouse->SetRelativeMouseMode = Cocoa_SetRelativeMouseMode;
mouse->CaptureMouse = Cocoa_CaptureMouse;
mouse->GetGlobalMouseState = Cocoa_GetGlobalMouseState;
SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor()); SDL_SetDefaultCursor(Cocoa_CreateDefaultCursor());

View file

@ -108,6 +108,7 @@ Cocoa_CreateDevice(int devindex)
device->SetWindowGrab = Cocoa_SetWindowGrab; device->SetWindowGrab = Cocoa_SetWindowGrab;
device->DestroyWindow = Cocoa_DestroyWindow; device->DestroyWindow = Cocoa_DestroyWindow;
device->GetWindowWMInfo = Cocoa_GetWindowWMInfo; device->GetWindowWMInfo = Cocoa_GetWindowWMInfo;
device->SetWindowHitTest = Cocoa_SetWindowHitTest;
device->shape_driver.CreateShaper = Cocoa_CreateShaper; device->shape_driver.CreateShaper = Cocoa_CreateShaper;
device->shape_driver.SetWindowShape = Cocoa_SetWindowShape; device->shape_driver.SetWindowShape = Cocoa_SetWindowShape;

View file

@ -45,6 +45,7 @@ typedef enum
PendingWindowOperation pendingWindowOperation; PendingWindowOperation pendingWindowOperation;
BOOL isMoving; BOOL isMoving;
int pendingWindowWarpX, pendingWindowWarpY; int pendingWindowWarpX, pendingWindowWarpY;
BOOL isDragAreaRunning;
} }
-(void) listen:(SDL_WindowData *) data; -(void) listen:(SDL_WindowData *) data;
@ -75,6 +76,9 @@ typedef enum
-(void) windowDidExitFullScreen:(NSNotification *) aNotification; -(void) windowDidExitFullScreen:(NSNotification *) aNotification;
-(NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions; -(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 */ /* Window event handling */
-(void) mouseDown:(NSEvent *) theEvent; -(void) mouseDown:(NSEvent *) theEvent;
-(void) rightMouseDown:(NSEvent *) theEvent; -(void) rightMouseDown:(NSEvent *) theEvent;
@ -138,8 +142,8 @@ extern int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * r
extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp); extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed); extern void Cocoa_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window); extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
struct SDL_SysWMinfo *info); extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_cocoawindow_h */ #endif /* _SDL_cocoawindow_h */

View file

@ -192,6 +192,7 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
inFullscreenTransition = NO; inFullscreenTransition = NO;
pendingWindowOperation = PENDING_OPERATION_NONE; pendingWindowOperation = PENDING_OPERATION_NONE;
isMoving = NO; isMoving = NO;
isDragAreaRunning = NO;
center = [NSNotificationCenter defaultCenter]; center = [NSNotificationCenter defaultCenter];
@ -663,10 +664,48 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
/*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/ /*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 - (void)mouseDown:(NSEvent *)theEvent
{ {
int button; int button;
if ([self processHitTest:theEvent]) {
return; /* dragging, drop event. */
}
switch ([theEvent buttonNumber]) { switch ([theEvent buttonNumber]) {
case 0: case 0:
if (([theEvent modifierFlags] & NSControlKeyMask) && if (([theEvent modifierFlags] & NSControlKeyMask) &&
@ -705,6 +744,10 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
{ {
int button; int button;
if ([self processHitTest:theEvent]) {
return; /* stopped dragging, drop event. */
}
switch ([theEvent buttonNumber]) { switch ([theEvent buttonNumber]) {
case 0: case 0:
if (wasCtrlLeft) { if (wasCtrlLeft) {
@ -744,6 +787,10 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
NSPoint point; NSPoint point;
int x, y; int x, y;
if ([self processHitTest:theEvent]) {
return; /* dragging, drop event. */
}
if (mouse->relative_mode) { if (mouse->relative_mode) {
return; return;
} }
@ -752,8 +799,8 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
x = (int)point.x; x = (int)point.x;
y = (int)(window->h - point.y); 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) { if (x < 0) {
x = 0; x = 0;
} else if (x >= window->w) { } else if (x >= window->w) {
@ -890,6 +937,7 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
/* The default implementation doesn't pass rightMouseDown to responder chain */ /* The default implementation doesn't pass rightMouseDown to responder chain */
- (void)rightMouseDown:(NSEvent *)theEvent; - (void)rightMouseDown:(NSEvent *)theEvent;
- (BOOL)mouseDownCanMoveWindow;
@end @end
@implementation SDLView @implementation SDLView
@ -898,6 +946,14 @@ SetWindowStyle(SDL_Window * window, unsigned int style)
[[self nextResponder] rightMouseDown:theEvent]; [[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 - (void)resetCursorRects
{ {
[super resetCursorRects]; [super resetCursorRects];
@ -995,6 +1051,7 @@ SetupWindowData(_THIS, SDL_Window * window, NSWindow *nswindow, SDL_bool created
/* All done! */ /* All done! */
[pool release]; [pool release];
window->driverdata = data;
return 0; return 0;
} }
@ -1566,6 +1623,12 @@ Cocoa_SetWindowFullscreenSpace(SDL_Window * window, SDL_bool state)
return succeeded; 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 */ #endif /* SDL_VIDEO_DRIVER_COCOA */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View file

@ -30,6 +30,7 @@
#include "../../events/SDL_events_c.h" #include "../../events/SDL_events_c.h"
#include "../../events/SDL_touch_c.h" #include "../../events/SDL_touch_c.h"
#include "../../events/scancodes_windows.h" #include "../../events/scancodes_windows.h"
#include "SDL_assert.h"
/* Dropfile support */ /* Dropfile support */
#include <shellapi.h> #include <shellapi.h>
@ -438,15 +439,20 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
HRAWINPUT hRawInput = (HRAWINPUT)lParam; HRAWINPUT hRawInput = (HRAWINPUT)lParam;
RAWINPUT inp; RAWINPUT inp;
UINT size = sizeof(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) { if (!isRelative || mouse->focus != data->window) {
if (!isCapture) {
break; break;
} }
}
GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
/* Mouse data */ /* Mouse data */
if (inp.header.dwType == RIM_TYPEMOUSE) { if (inp.header.dwType == RIM_TYPEMOUSE) {
if (isRelative) {
RAWMOUSE* mouse = &inp.data.mouse; RAWMOUSE* mouse = &inp.data.mouse;
if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) { if ((mouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) {
@ -465,6 +471,23 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
initialMousePoint.y = mouse->lLastY; initialMousePoint.y = mouse->lLastY;
} }
WIN_CheckRawMouseButtons( mouse->usButtonFlags, data ); 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");
}
} }
} }
break; break;
@ -509,7 +532,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
#ifdef WM_MOUSELEAVE #ifdef WM_MOUSELEAVE
case 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)) { if (!IsIconic(hwnd)) {
POINT cursorPos; POINT cursorPos;
GetCursorPos(&cursorPos); GetCursorPos(&cursorPos);
@ -863,6 +886,32 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
return 0; return 0;
} }
break; 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 */ /* If there's a window proc, assume it's going to handle messages */

View file

@ -30,6 +30,44 @@
HCURSOR SDL_cursor = NULL; 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 * static SDL_Cursor *
WIN_CreateDefaultCursor() WIN_CreateDefaultCursor()
@ -211,22 +249,43 @@ WIN_WarpMouseGlobal(int x, int y)
static int static int
WIN_SetRelativeMouseMode(SDL_bool enabled) 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;
} }
/* (Un)register raw input for mice */ static int
if (RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) { 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. */
}
}
/* Only return an error when registering. If we unregister and fail, /* While we were thinking of SetCapture() when designing this API in SDL,
then it's probably that we unregistered twice. That's OK. */ we didn't count on the fact that SetCapture() only tracks while the
if (enabled) { left mouse button is held down! Instead, we listen for raw mouse input
return SDL_Unsupported(); and manually query the mouse when it leaves the window. :/ */
return ToggleRawInput(window != NULL);
} }
}
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 void
@ -241,6 +300,8 @@ WIN_InitMouse(_THIS)
mouse->WarpMouse = WIN_WarpMouse; mouse->WarpMouse = WIN_WarpMouse;
mouse->WarpMouseGlobal = WIN_WarpMouseGlobal; mouse->WarpMouseGlobal = WIN_WarpMouseGlobal;
mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode; mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
mouse->CaptureMouse = WIN_CaptureMouse;
mouse->GetGlobalMouseState = WIN_GetGlobalMouseState;
SDL_SetDefaultCursor(WIN_CreateDefaultCursor()); SDL_SetDefaultCursor(WIN_CreateDefaultCursor());
@ -256,6 +317,11 @@ WIN_QuitMouse(_THIS)
mouse->def_cursor = NULL; mouse->def_cursor = NULL;
mouse->cur_cursor = NULL; mouse->cur_cursor = NULL;
} }
if (rawInputEnableCount) { /* force RAWINPUT off here. */
rawInputEnableCount = 1;
ToggleRawInput(SDL_FALSE);
}
} }
#endif /* SDL_VIDEO_DRIVER_WINDOWS */ #endif /* SDL_VIDEO_DRIVER_WINDOWS */

View file

@ -144,6 +144,7 @@ WIN_CreateDevice(int devindex)
device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer; device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer; device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
device->OnWindowEnter = WIN_OnWindowEnter; device->OnWindowEnter = WIN_OnWindowEnter;
device->SetWindowHitTest = WIN_SetWindowHitTest;
device->shape_driver.CreateShaper = Win32_CreateShaper; device->shape_driver.CreateShaper = Win32_CreateShaper;
device->shape_driver.SetWindowShape = Win32_SetWindowShape; device->shape_driver.SetWindowShape = Win32_SetWindowShape;

View file

@ -785,6 +785,12 @@ WIN_UpdateClipCursor(SDL_Window *window)
} }
} }
int
WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
{
return 0; /* just succeed, the real work is done elsewhere. */
}
#endif /* SDL_VIDEO_DRIVER_WINDOWS */ #endif /* SDL_VIDEO_DRIVER_WINDOWS */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View file

@ -69,6 +69,7 @@ extern SDL_bool WIN_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo *info); struct SDL_SysWMinfo *info);
extern void WIN_OnWindowEnter(_THIS, SDL_Window * window); extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
extern void WIN_UpdateClipCursor(SDL_Window *window); extern void WIN_UpdateClipCursor(SDL_Window *window);
extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_windowswindow_h */ #endif /* _SDL_windowswindow_h */

View file

@ -40,6 +40,42 @@
#include <stdio.h> #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 { typedef struct {
unsigned char *data; unsigned char *data;
int format, count; int format, count;
@ -347,6 +383,124 @@ X11_DispatchUnmapNotify(SDL_WindowData *data)
SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
} }
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 static void
X11_DispatchEvent(_THIS) X11_DispatchEvent(_THIS)
{ {
@ -820,6 +974,11 @@ X11_DispatchEvent(_THIS)
if (X11_IsWheelEvent(display,&xevent,&ticks)) { if (X11_IsWheelEvent(display,&xevent,&ticks)) {
SDL_SendMouseWheel(data->window, 0, 0, ticks); SDL_SendMouseWheel(data->window, 0, 0, ticks);
} else { } 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); SDL_SendMouseButton(data->window, 0, SDL_PRESSED, xevent.xbutton.button);
} }
} }

View file

@ -339,6 +339,62 @@ X11_SetRelativeMouseMode(SDL_bool enabled)
return -1; 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 void
X11_InitMouse(_THIS) X11_InitMouse(_THIS)
{ {
@ -351,6 +407,8 @@ X11_InitMouse(_THIS)
mouse->WarpMouse = X11_WarpMouse; mouse->WarpMouse = X11_WarpMouse;
mouse->WarpMouseGlobal = X11_WarpMouseGlobal; mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode; mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
mouse->CaptureMouse = X11_CaptureMouse;
mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
SDL_SetDefaultCursor(X11_CreateDefaultCursor()); SDL_SetDefaultCursor(X11_CreateDefaultCursor());
} }

View file

@ -243,6 +243,7 @@ X11_CreateDevice(int devindex)
device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer; device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer; device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
device->GetWindowWMInfo = X11_GetWindowWMInfo; device->GetWindowWMInfo = X11_GetWindowWMInfo;
device->SetWindowHitTest = X11_SetWindowHitTest;
device->shape_driver.CreateShaper = X11_CreateShaper; device->shape_driver.CreateShaper = X11_CreateShaper;
device->shape_driver.SetWindowShape = X11_SetWindowShape; device->shape_driver.SetWindowShape = X11_SetWindowShape;

View file

@ -1445,6 +1445,12 @@ X11_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info)
} }
} }
int
X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
{
return 0; /* just succeed, the real work is done elsewhere. */
}
#endif /* SDL_VIDEO_DRIVER_X11 */ #endif /* SDL_VIDEO_DRIVER_X11 */
/* vi: set ts=4 sw=4 expandtab: */ /* vi: set ts=4 sw=4 expandtab: */

View file

@ -93,6 +93,7 @@ extern void X11_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void X11_DestroyWindow(_THIS, SDL_Window * window); extern void X11_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window, extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo *info); struct SDL_SysWMinfo *info);
extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
#endif /* _SDL_x11window_h */ #endif /* _SDL_x11window_h */

View file

@ -23,6 +23,7 @@ TARGETS = \
testgles$(EXE) \ testgles$(EXE) \
testgles2$(EXE) \ testgles2$(EXE) \
testhaptic$(EXE) \ testhaptic$(EXE) \
testhittesting$(EXE) \
testrumble$(EXE) \ testrumble$(EXE) \
testhotplug$(EXE) \ testhotplug$(EXE) \
testthread$(EXE) \ testthread$(EXE) \
@ -108,6 +109,9 @@ testintersections$(EXE): $(srcdir)/testintersections.c
testrelative$(EXE): $(srcdir)/testrelative.c testrelative$(EXE): $(srcdir)/testrelative.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) $(CC) -o $@ $^ $(CFLAGS) $(LIBS)
testhittesting$(EXE): $(srcdir)/testhittesting.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
testdraw2$(EXE): $(srcdir)/testdraw2.c testdraw2$(EXE): $(srcdir)/testdraw2.c
$(CC) -o $@ $^ $(CFLAGS) $(LIBS) $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

134
test/testhittesting.c Normal file
View file

@ -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;
}