Implemented SDL_CaptureMouse().

This commit is contained in:
Ryan C. Gordon 2014-05-24 01:30:37 -04:00
parent 846a3e0776
commit b7d2c0e9d6
11 changed files with 174 additions and 38 deletions

View File

@ -116,6 +116,37 @@ extern DECLSPEC void SDLCALL SDL_WarpMouseInWindow(SDL_Window * window,
*/
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.
*

View File

@ -108,7 +108,8 @@ typedef enum
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;
/**

View File

@ -579,3 +579,4 @@
#define SDL_WinRTGetFSPathUNICODE SDL_WinRTGetFSPathUNICODE_REAL
#define SDL_WinRTGetFSPathUTF8 SDL_WinRTGetFSPathUTF8_REAL
#define SDL_WinRTRunApp SDL_WinRTRunApp_REAL
#define SDL_CaptureMouse SDL_CaptureMouse_REAL

View File

@ -612,3 +612,4 @@ SDL_DYNAPI_PROC(const wchar_t*,SDL_WinRTGetFSPathUNICODE,(SDL_WinRT_Path a),(a),
SDL_DYNAPI_PROC(const char*,SDL_WinRTGetFSPathUTF8,(SDL_WinRT_Path a),(a),return)
SDL_DYNAPI_PROC(int,SDL_WinRTRunApp,(int a, char **b, void *c),(a,b,c),return)
#endif
SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return)

View File

@ -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 @@ SDL_SetKeyboardFocus(SDL_Window * window)
/* 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);

View File

@ -140,30 +140,17 @@ static SDL_bool
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
the pointer.
Windows doesn't give you mouse events outside your window unless you call
SetCapture().
Both of these are slightly scary changes, so for now we'll punt and if the
mouse leaves the window you'll lose mouse focus and reset button state.
*/
#ifdef SUPPORT_DRAG_OUTSIDE_WINDOW
if (!inWindow && !buttonstate) {
#else
if (!inWindow) {
#endif
if (window == mouse->focus) {
#ifdef DEBUG_MOUSE
printf("Mouse left window, synthesizing move & focus lost event\n");
@ -204,7 +191,6 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
int posted;
int xrel;
int yrel;
int x_max = 0, y_max = 0;
if (mouse->relative_mode_warp) {
int center_x = 0, center_y = 0;
@ -246,24 +232,29 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
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;
/* 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;
}
// !!! FIXME: shouldn't this be (window) instead of (mouse->focus)?
SDL_GetWindowSize(mouse->focus, &x_max, &y_max);
--x_max;
--y_max;
if (mouse->y > y_max) {
mouse->y = y_max;
}
if (mouse->y < 0) {
mouse->y = 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;
}
}
mouse->xdelta += xrel;
@ -426,6 +417,7 @@ SDL_MouseQuit(void)
SDL_Cursor *cursor, *next;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_CaptureMouse(SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(1);
@ -572,6 +564,42 @@ SDL_GetRelativeMouseMode()
return mouse->relative_mode;
}
int
SDL_CaptureMouse(SDL_bool enabled)
{
SDL_Mouse *mouse = SDL_GetMouse();
SDL_Window *focusWindow;
SDL_bool isCaptured;
if (!mouse->CaptureMouse) {
return SDL_Unsupported();
}
focusWindow = SDL_GetKeyboardFocus();
isCaptured = focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE);
if (isCaptured == enabled) {
return 0; /* already done! */
}
if (enabled) {
if (!focusWindow) {
return SDL_SetError("No window has focus");
} else if (mouse->CaptureMouse(focusWindow) == -1) {
return -1; /* CaptureMouse() should call SetError */
}
focusWindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
} else {
if (mouse->CaptureMouse(NULL) == -1) {
return -1; /* CaptureMouse() should call SetError */
}
focusWindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
}
return 0;
}
SDL_Cursor *
SDL_CreateCursor(const Uint8 * data, const Uint8 * mask,
int w, int h, int hot_x, int hot_y)

View File

@ -63,6 +63,9 @@ typedef struct
/* Set relative mode */
int (*SetRelativeMouseMode) (SDL_bool enabled);
/* Set mouse capture */
int (*CaptureMouse) (SDL_Window * window);
/* Data common to all mice */
SDL_MouseID mouseID;
SDL_Window *focus;

View File

@ -999,10 +999,12 @@ default: return "???";
static void
SDLTest_PrintEvent(SDL_Event * event)
{
#if 0
if ((event->type == SDL_MOUSEMOTION) || (event->type == SDL_FINGERMOTION)) {
/* Mouse and finger motion are really spammy */
return;
}
#endif
switch (event->type) {
case SDL_WINDOWEVENT:
@ -1379,6 +1381,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);
printf("%sapturing mouse %s!\n", shouldCapture ? "C" : "Unc", (rc == 0) ? "succeeded" : "failed");
}
}
break;
case SDLK_v:
if (withControl) {

View File

@ -3269,12 +3269,17 @@ SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
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);
@ -3326,6 +3331,13 @@ SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
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);

View File

@ -219,6 +219,19 @@ WIN_SetRelativeMouseMode(SDL_bool enabled)
return 0;
}
static int
WIN_CaptureMouse(SDL_Window *window)
{
if (!window) {
ReleaseCapture();
} else {
const SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
SetCapture(data->hwnd);
}
return 0;
}
void
WIN_InitMouse(_THIS)
{
@ -230,6 +243,7 @@ WIN_InitMouse(_THIS)
mouse->FreeCursor = WIN_FreeCursor;
mouse->WarpMouse = WIN_WarpMouse;
mouse->SetRelativeMouseMode = WIN_SetRelativeMouseMode;
mouse->CaptureMouse = WIN_CaptureMouse;
SDL_SetDefaultCursor(WIN_CreateDefaultCursor());

View File

@ -330,6 +330,29 @@ X11_SetRelativeMouseMode(SDL_bool enabled)
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;
}
void
X11_InitMouse(_THIS)
{
@ -341,6 +364,7 @@ X11_InitMouse(_THIS)
mouse->FreeCursor = X11_FreeCursor;
mouse->WarpMouse = X11_WarpMouse;
mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
mouse->CaptureMouse = X11_CaptureMouse;
SDL_SetDefaultCursor(X11_CreateDefaultCursor());
}