Fixed bug 2260 - SDL_SetCursorGrab() is buggy on Windows

BurnSpamAddress

Steps to reproduce:
1. Grab the cursor with SDL_SetCursorGrab()
2. Alt-tab away from the window
3. Click on the titlebar of the window

This will cause the window to disappear underneath the taskbar!

This appears to be a general issue with ClipCursor() on windows, i.e. I am getting the same behavior if I call ClipCursor() directly.

It is caused by a feedback loop between the ClipCursor function and the modal resize/move event loop that handles mouse-based sizing on Windows.
This commit is contained in:
Sam Lantinga 2013-11-27 10:29:38 -08:00
parent fa4e4a643a
commit d2511d9ef9
2 changed files with 62 additions and 29 deletions

View File

@ -286,6 +286,45 @@ WIN_ConvertUTF32toUTF8(UINT32 codepoint, char * text)
return SDL_TRUE;
}
static void
WIN_UpdateClipCursor(SDL_Window *window)
{
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
/* Don't clip the cursor while we're in the modal resize or move loop */
if (data->in_modal_loop) {
ClipCursor(NULL);
return;
}
if (SDL_GetMouse()->relative_mode) {
LONG cx, cy;
RECT rect;
GetWindowRect(data->hwnd, &rect);
cx = (rect.left + rect.right) / 2;
cy = (rect.top + rect.bottom) / 2;
/* Make an absurdly small clip rect */
rect.left = cx-1;
rect.right = cx+1;
rect.top = cy-1;
rect.bottom = cy+1;
ClipCursor(&rect);
} else if ((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);
ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
ClipCursor(&rect);
}
} else {
ClipCursor(NULL);
}
}
LRESULT CALLBACK
WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
@ -369,22 +408,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
WIN_CheckWParamMouseButton( ( keyState & 0x8000 ), (mouseFlags & SDL_BUTTON_X2MASK), data, SDL_BUTTON_X2 );
data->mouse_button_flags = 0;
if(SDL_GetMouse()->relative_mode) {
LONG cx, cy;
RECT rect;
GetWindowRect(hwnd, &rect);
cx = (rect.left + rect.right) / 2;
cy = (rect.top + rect.bottom) / 2;
/* Make an absurdly small clip rect */
rect.left = cx-1;
rect.right = cx+1;
rect.top = cy-1;
rect.bottom = cy+1;
ClipCursor(&rect);
}
WIN_UpdateClipCursor(data->window);
/*
* FIXME: Update keyboard state
@ -585,6 +609,22 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
break;
#endif /* WM_INPUTLANGCHANGE */
case WM_ENTERSIZEMOVE:
case WM_ENTERMENULOOP:
{
data->in_modal_loop = SDL_TRUE;
WIN_UpdateClipCursor(data->window);
}
break;
case WM_EXITSIZEMOVE:
case WM_EXITMENULOOP:
{
data->in_modal_loop = SDL_FALSE;
WIN_UpdateClipCursor(data->window);
}
break;
#ifdef WM_GETMINMAXINFO
case WM_GETMINMAXINFO:
{
@ -673,20 +713,14 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
RECT rect;
int x, y;
int w, h;
Uint32 window_flags;
if (!GetClientRect(hwnd, &rect) ||
(rect.right == rect.left && rect.bottom == rect.top)) {
if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
break;
}
ClientToScreen(hwnd, (LPPOINT) & rect);
ClientToScreen(hwnd, (LPPOINT) & rect + 1);
window_flags = SDL_GetWindowFlags(data->window);
if ((window_flags & SDL_WINDOW_INPUT_GRABBED) &&
(window_flags & SDL_WINDOW_INPUT_FOCUS)) {
ClipCursor(&rect);
}
WIN_UpdateClipCursor(data->window);
x = rect.left;
y = rect.top;

View File

@ -207,7 +207,7 @@ WIN_SetRelativeMouseMode(SDL_bool enabled)
/* (Un)register raw input for mice */
if(RegisterRawInputDevices(&rawMouse, 1, sizeof(RAWINPUTDEVICE)) == FALSE) {
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. */
@ -216,7 +216,7 @@ WIN_SetRelativeMouseMode(SDL_bool enabled)
}
}
if(enabled) {
if (enabled) {
LONG cx, cy;
RECT rect;
GetWindowRect(hWnd, &rect);
@ -231,10 +231,9 @@ WIN_SetRelativeMouseMode(SDL_bool enabled)
rect.bottom = cy+1;
ClipCursor(&rect);
}
else
} else {
ClipCursor(NULL);
}
return 0;
}