From 7aef2350cf382e6392f672972b83ae19dca16054 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 23 Dec 2013 17:37:22 -0800 Subject: [PATCH] Added a relative mouse mode that uses mouse warping instead of raw input. To enable this, set the environment variable SDL_MOUSE_RELATIVE_MODE_WARP to "1" When mouse relative mode is disabled, put the cursor back where the application expects it to be, instead of where it was when relative mode was enabled. --- include/SDL_hints.h | 13 +++- src/events/SDL_mouse.c | 71 +++++++++++++++------- src/events/SDL_mouse_c.h | 3 +- src/video/SDL_video.c | 5 +- src/video/windows/SDL_windowsevents.c | 85 +++++++++++++++------------ src/video/x11/SDL_x11events.c | 2 +- src/video/x11/SDL_x11xinput2.c | 2 +- 7 files changed, 115 insertions(+), 66 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 1462f4e399999..9aad2e32ec324 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -173,7 +173,18 @@ extern "C" { #define SDL_HINT_GRAB_KEYBOARD "SDL_GRAB_KEYBOARD" /** - * \brief Minimize your SDL_Window if it loses key focus when in Fullscreen mode. Defaults to true. +* \brief A variable controlling whether relative mouse mode is implemented using mouse warping +* +* This variable can be set to the following values: +* "0" - Relative mouse mode uses raw input +* "1" - Relative mouse mode uses mouse warping +* +* By default SDL will use raw input for relative mouse mode +*/ +#define SDL_HINT_MOUSE_RELATIVE_MODE_WARP "SDL_MOUSE_RELATIVE_MODE_WARP" + +/** + * \brief Minimize your SDL_Window if it loses key focus when in fullscreen mode. Defaults to true. * */ #define SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS "SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS" diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 742802a755a01..8eb4414704dd8 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -23,6 +23,7 @@ /* General mouse handling code for SDL */ #include "SDL_assert.h" +#include "SDL_hints.h" #include "SDL_timer.h" #include "SDL_events.h" #include "SDL_events_c.h" @@ -205,12 +206,24 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ int yrel; int x_max = 0, y_max = 0; - /* relative motion is calculated regarding the system cursor last position */ + if (mouse->relative_mode_warp) { + int center_x = 0, center_y = 0; + SDL_GetWindowSize(window, ¢er_x, ¢er_y); + center_x /= 2; + center_y /= 2; + if (x == center_x && y == center_y) { + mouse->last_x = center_x; + mouse->last_y = center_y; + return 0; + } + SDL_WarpMouseInWindow(window, center_x, center_y); + } + if (relative) { xrel = x; yrel = y; - x = (mouse->last_x + x); - y = (mouse->last_y + y); + x = (mouse->last_x + xrel); + y = (mouse->last_y + yrel); } else { xrel = x - mouse->last_x; yrel = y - mouse->last_y; @@ -225,7 +238,7 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ } /* Update internal mouse coordinates */ - if (mouse->relative_mode == SDL_FALSE) { + if (!mouse->relative_mode) { mouse->x = x; mouse->y = y; } else { @@ -481,21 +494,36 @@ SDL_WarpMouseInWindow(SDL_Window * window, int x, int y) } } +static SDL_bool +ShouldUseRelativeModeWarp(SDL_Mouse *mouse) +{ + const char *hint; + + if (!mouse->SetRelativeMouseMode) { + return SDL_TRUE; + } + + hint = SDL_GetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP); + if (hint) { + if (*hint == '0') { + return SDL_FALSE; + } else { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + int SDL_SetRelativeMouseMode(SDL_bool enabled) { SDL_Mouse *mouse = SDL_GetMouse(); SDL_Window *focusWindow = SDL_GetKeyboardFocus(); - int original_x = mouse->x, original_y = mouse->y; if (enabled == mouse->relative_mode) { return 0; } - if (!mouse->SetRelativeMouseMode) { - return SDL_Unsupported(); - } - if (enabled && focusWindow) { /* Center it in the focused window to prevent clicks from going through * to background windows. @@ -504,23 +532,26 @@ SDL_SetRelativeMouseMode(SDL_bool enabled) SDL_WarpMouseInWindow(focusWindow, focusWindow->w/2, focusWindow->h/2); } - if (mouse->SetRelativeMouseMode(enabled) < 0) { + /* Set the relative mode */ + if (!enabled && mouse->relative_mode_warp) { + mouse->relative_mode_warp = SDL_FALSE; + } else if (enabled && ShouldUseRelativeModeWarp(mouse)) { + mouse->relative_mode_warp = SDL_TRUE; + } else if (mouse->SetRelativeMouseMode(enabled) < 0) { return -1; } - - /* Set the relative mode */ mouse->relative_mode = enabled; - if (enabled) { - /* Save the expected mouse position */ - mouse->original_x = original_x; - mouse->original_y = original_y; - } else if (mouse->focus) { - /* Restore the expected mouse position */ - SDL_WarpMouseInWindow(mouse->focus, mouse->original_x, mouse->original_y); + if (mouse->focus) { + SDL_UpdateWindowGrab(mouse->focus); + + /* Put the cursor back to where the application expects it */ + if (!enabled) { + SDL_WarpMouseInWindow(mouse->focus, mouse->x, mouse->y); + } } - /* Flush pending mouse motion */ + /* Flush pending mouse motion - ideally we would pump events, but that's not always safe */ SDL_FlushEvent(SDL_MOUSEMOTION); /* Update cursor visibility */ diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h index fd34c66f87d00..427b3b156a111 100644 --- a/src/events/SDL_mouse_c.h +++ b/src/events/SDL_mouse_c.h @@ -73,8 +73,7 @@ typedef struct int last_x, last_y; /* the last reported x and y coordinates */ Uint32 buttonstate; SDL_bool relative_mode; - /* the x and y coordinates when relative mode was activated */ - int original_x, original_y; + SDL_bool relative_mode_warp; /* Data for double-click tracking */ int num_clickstates; diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 5aacac80fde4b..ff80891209fef 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -2055,8 +2055,9 @@ SDL_UpdateWindowGrab(SDL_Window * window) { if (_this->SetWindowGrab) { SDL_bool grabbed; - if ((window->flags & SDL_WINDOW_INPUT_GRABBED) && - (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + if (SDL_GetMouse()->relative_mode_warp || + ((window->flags & SDL_WINDOW_INPUT_GRABBED) && + (window->flags & SDL_WINDOW_INPUT_FOCUS))) { grabbed = SDL_TRUE; } else { grabbed = SDL_FALSE; diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 9e82dc93586b7..a06f11fed5119 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -290,6 +290,7 @@ static void WIN_UpdateClipCursor(SDL_Window *window) { SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_Mouse *mouse = SDL_GetMouse(); /* Don't clip the cursor while we're in the modal resize or move loop */ if (data->in_modal_loop) { @@ -297,7 +298,7 @@ WIN_UpdateClipCursor(SDL_Window *window) return; } - if (SDL_GetMouse()->relative_mode) { + if (mouse->relative_mode && !mouse->relative_mode_warp) { LONG cx, cy; RECT rect; GetWindowRect(data->hwnd, &rect); @@ -312,8 +313,9 @@ WIN_UpdateClipCursor(SDL_Window *window) rect.bottom = cy+1; ClipCursor(&rect); - } else if ((window->flags & SDL_WINDOW_INPUT_GRABBED) && - (window->flags & SDL_WINDOW_INPUT_FOCUS)) { + } else if (mouse->relative_mode_warp || + ((window->flags & SDL_WINDOW_INPUT_GRABBED) && + (window->flags & SDL_WINDOW_INPUT_FOCUS))) { RECT rect; if (GetClientRect(data->hwnd, &rect) && !IsRectEmpty(&rect)) { ClientToScreen(data->hwnd, (LPPOINT) & rect); @@ -426,8 +428,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) break; case WM_MOUSEMOVE: - if( !SDL_GetMouse()->relative_mode ) - SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + { + SDL_Mouse *mouse = SDL_GetMouse(); + if (!mouse->relative_mode || mouse->relative_mode_warp) { + SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + } /* don't break here, fall through to check the wParam like the button presses */ case WM_LBUTTONUP: case WM_RBUTTONUP: @@ -437,49 +443,50 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: - if( !SDL_GetMouse()->relative_mode ) - WIN_CheckWParamMouseButtons( wParam, data ); + { + SDL_Mouse *mouse = SDL_GetMouse(); + if (!mouse->relative_mode || mouse->relative_mode_warp) { + WIN_CheckWParamMouseButtons(wParam, data); + } + } break; case WM_INPUT: - { - HRAWINPUT hRawInput = (HRAWINPUT)lParam; - RAWINPUT inp; - UINT size = sizeof(inp); + { + SDL_Mouse *mouse = SDL_GetMouse(); + HRAWINPUT hRawInput = (HRAWINPUT)lParam; + RAWINPUT inp; + UINT size = sizeof(inp); - if(!SDL_GetMouse()->relative_mode) - break; + if (!mouse->relative_mode || mouse->relative_mode_warp) { + break; + } - GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); + GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); - /* Mouse data */ - if(inp.header.dwType == RIM_TYPEMOUSE) - { - RAWMOUSE* mouse = &inp.data.mouse; + /* Mouse data */ + if (inp.header.dwType == RIM_TYPEMOUSE) { + 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) { + initialMousePoint.x = mouse->lLastX; + initialMousePoint.y = mouse->lLastY; + } + + SDL_SendMouseMotion(data->window, 0, 1, (int)(mouse->lLastX-initialMousePoint.x), (int)(mouse->lLastY-initialMousePoint.y) ); - 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; + WIN_CheckRawMouseButtons( mouse->usButtonFlags, data ); } - WIN_CheckRawMouseButtons( mouse->usButtonFlags, data ); } break; - } case WM_MOUSEWHEEL: { @@ -497,8 +504,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) s_AccumulatedMotion += WHEEL_DELTA; } } - break; } + break; case WM_MOUSEHWHEEL: { @@ -516,8 +523,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) s_AccumulatedMotion += WHEEL_DELTA; } } - break; } + break; #ifdef WM_MOUSELEAVE case WM_MOUSELEAVE: @@ -549,8 +556,8 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) GetKeyboardState(keyboardState); if (ToUnicode(wParam, (lParam >> 16) & 0xff, keyboardState, (LPWSTR)&utf32, 1, 0) > 0) { - WORD repitition; - for (repitition = lParam & 0xffff; repitition > 0; repitition--) { + WORD repetition; + for (repetition = lParam & 0xffff; repetition > 0; repetition--) { WIN_ConvertUTF32toUTF8(utf32, text); SDL_SendKeyboardText(text); } diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 423bd87d390f6..44ca1cd7dcc1b 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -698,7 +698,7 @@ X11_DispatchEvent(_THIS) case MotionNotify:{ SDL_Mouse *mouse = SDL_GetMouse(); - if(!mouse->relative_mode) { + if(!mouse->relative_mode || mouse->relative_mode_warp) { #ifdef DEBUG_MOTION printf("window %p: X11 motion: %d,%d\n", xevent.xmotion.x, xevent.xmotion.y); #endif diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index 196dd2ae9d84d..8993561eb27dd 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -134,7 +134,7 @@ X11_HandleXinput2Event(SDL_VideoData *videodata,XGenericEventCookie *cookie) SDL_Mouse *mouse = SDL_GetMouse(); double relative_cords[2]; - if (!mouse->relative_mode) { + if (!mouse->relative_mode || mouse->relative_mode_warp) { return 0; }