From bb7a27fadd68161dbc402fb9b23d604b4e238bad Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 30 May 2014 01:51:13 -0400 Subject: [PATCH] Fixed up SDL_CaptureMouse() on Windows to work like I expected. This would have been a one-line patch to the documentation (specifying that captures only work as long as the left mouse button is pressed), but I didn't like that, so I got a little crazy about this instead. --- src/video/windows/SDL_windowsevents.c | 55 ++++++++++++++------ src/video/windows/SDL_windowsmouse.c | 75 +++++++++++++++++++-------- 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index b04a7a20ec1d2..6b76215056f25 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -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 @@ -428,33 +429,55 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 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) { + 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 ); + } 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, GetKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_LEFT); + SDL_SendMouseButton(data->window, 0, GetKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_RIGHT); + SDL_SendMouseButton(data->window, 0, GetKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE); + SDL_SendMouseButton(data->window, 0, GetKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1); + SDL_SendMouseButton(data->window, 0, GetKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2); + } + } else { + SDL_assert(0 && "Shouldn't happen"); } - WIN_CheckRawMouseButtons( mouse->usButtonFlags, data ); } } break; @@ -499,7 +522,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) #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); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 1f38dcd55d873..0abd212404623 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -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() @@ -201,35 +239,25 @@ WIN_WarpMouse(SDL_Window * window, int x, int y) static int WIN_SetRelativeMouseMode(SDL_bool enabled) { - RAWINPUTDEVICE rawMouse = { 0x01, 0x02, 0, NULL }; /* Mouse: UsagePage = 1, Usage = 2 */ - - 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; + return ToggleRawInput(enabled); } static int WIN_CaptureMouse(SDL_Window *window) { if (!window) { - ReleaseCapture(); - } else { - const SDL_WindowData *data = (SDL_WindowData *) window->driverdata; - SetCapture(data->hwnd); + 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. */ + } } - return 0; + /* 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); } void @@ -259,6 +287,11 @@ WIN_QuitMouse(_THIS) mouse->def_cursor = NULL; mouse->cur_cursor = NULL; } + + if (rawInputEnableCount) { /* force RAWINPUT off here. */ + rawInputEnableCount = 1; + ToggleRawInput(SDL_FALSE); + } } #endif /* SDL_VIDEO_DRIVER_WINDOWS */