Don't need to use raw input to track the mouse during mouse capture (thanks Brick!)

main
Sam Lantinga 2021-10-14 11:46:07 -07:00
parent 0b6a821188
commit 5e89b3c89e
4 changed files with 62 additions and 56 deletions

View File

@ -742,6 +742,19 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
{ {
SDL_Mouse *mouse = SDL_GetMouse(); SDL_Mouse *mouse = SDL_GetMouse();
if (!data->mouse_tracked) {
TRACKMOUSEEVENT trackMouseEvent;
trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
trackMouseEvent.dwFlags = TME_LEAVE;
trackMouseEvent.hwndTrack = data->hwnd;
if (TrackMouseEvent(&trackMouseEvent)) {
data->mouse_tracked = SDL_TRUE;
}
}
if (!mouse->relative_mode || mouse->relative_mode_warp) { if (!mouse->relative_mode || mouse->relative_mode_warp) {
/* Only generate mouse events for real mouse */ /* Only generate mouse events for real mouse */
if (GetMouseMessageSource() != SDL_MOUSE_EVENT_SOURCE_TOUCH && if (GetMouseMessageSource() != SDL_MOUSE_EVENT_SOURCE_TOUCH &&
@ -781,13 +794,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM 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 isRelative = mouse->relative_mode || mouse->relative_mode_warp;
const SDL_bool isCapture = ((data->window->flags & SDL_WINDOW_MOUSE_CAPTURE) != 0);
/* Relative mouse motion is delivered to the window with keyboard focus */ /* Relative mouse motion is delivered to the window with keyboard focus */
if (!isRelative || data->window != SDL_GetKeyboardFocus()) { if (!isRelative || data->window != SDL_GetKeyboardFocus()) {
if (!isCapture) { break;
break;
}
} }
GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER)); GetRawInputData(hRawInput, RID_INPUT, &inp, &size, sizeof(RAWINPUTHEADER));
@ -910,28 +920,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} }
WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data, mouseID); WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data, mouseID);
} 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;
RECT hwndRect;
HWND currentHnd;
GetCursorPos(&pt);
currentHnd = WindowFromPoint(pt);
ScreenToClient(hwnd, &pt);
GetClientRect(hwnd, &hwndRect);
/* if in the window, WM_MOUSEMOVE, etc, will cover it. */
if(currentHnd != hwnd || pt.x < 0 || pt.y < 0 || pt.x > hwndRect.right || pt.y > hwndRect.right) {
SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
SDL_SendMouseMotion(data->window, mouseID, 0, (int)pt.x, (int)pt.y);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
}
} else { } else {
SDL_assert(0 && "Shouldn't happen"); SDL_assert(0 && "Shouldn't happen");
} }
@ -951,7 +939,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} }
break; break;
#ifdef WM_MOUSELEAVE
case WM_MOUSELEAVE: case WM_MOUSELEAVE:
if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) { if (SDL_GetMouseFocus() == data->window && !SDL_GetMouse()->relative_mode && !(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
if (!IsIconic(hwnd)) { if (!IsIconic(hwnd)) {
@ -973,18 +960,16 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} }
} }
/* When WM_MOUSELEAVE is fired we can be assured that the cursor has left the window. /* When WM_MOUSELEAVE is fired we can be assured that the cursor has left the window */
Regardless of relative mode, it is important that mouse focus is reset as there is a potential if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
race condition when in the process of leaving/entering relative mode, resulting in focus never
being lost. This then causes a cascading failure where SDL_WINDOWEVENT_ENTER / SDL_WINDOWEVENT_LEAVE
can stop firing permanently, due to the focus being in the wrong state and TrackMouseEvent never
resubscribing. */
if (!(data->window->flags & SDL_WINDOW_MOUSE_CAPTURE))
SDL_SetMouseFocus(NULL); SDL_SetMouseFocus(NULL);
}
/* Once we get WM_MOUSELEAVE we're guaranteed that the window is no longer tracked */
data->mouse_tracked = SDL_FALSE;
returnCode = 0; returnCode = 0;
break; break;
#endif /* WM_MOUSELEAVE */
case WM_KEYDOWN: case WM_KEYDOWN:
case WM_SYSKEYDOWN: case WM_SYSKEYDOWN:
@ -1467,6 +1452,31 @@ static void WIN_UpdateClipCursorForWindows()
} }
} }
static void WIN_UpdateMouseCapture()
{
SDL_Window* focusWindow = SDL_GetKeyboardFocus();
if (focusWindow && (focusWindow->flags & SDL_WINDOW_MOUSE_CAPTURE)) {
SDL_WindowData *data = (SDL_WindowData *) focusWindow->driverdata;
if (!data->mouse_tracked) {
POINT pt;
if (GetCursorPos(&pt) && ScreenToClient(data->hwnd, &pt)) {
SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
SDL_MouseID mouseID = SDL_GetMouse()->mouseID;
SDL_SendMouseMotion(data->window, mouseID, 0, (int)pt.x, (int)pt.y);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1);
SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2);
}
}
}
}
/* A message hook called before TranslateMessage() */ /* A message hook called before TranslateMessage() */
static SDL_WindowsMessageHook g_WindowsMessageHook = NULL; static SDL_WindowsMessageHook g_WindowsMessageHook = NULL;
static void *g_WindowsMessageHookData = NULL; static void *g_WindowsMessageHookData = NULL;
@ -1584,6 +1594,9 @@ WIN_PumpEvents(_THIS)
/* Update the clipping rect in case someone else has stolen it */ /* Update the clipping rect in case someone else has stolen it */
WIN_UpdateClipCursorForWindows(); WIN_UpdateClipCursorForWindows();
/* Update mouse capture */
WIN_UpdateMouseCapture();
} }

View File

@ -292,18 +292,22 @@ WIN_SetRelativeMouseMode(SDL_bool enabled)
static int static int
WIN_CaptureMouse(SDL_Window *window) WIN_CaptureMouse(SDL_Window *window)
{ {
if (!window) { if (window) {
SDL_Window *focusWin = SDL_GetKeyboardFocus(); SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
if (focusWin) { SetCapture(data->hwnd);
WIN_OnWindowEnter(SDL_GetVideoDevice(), focusWin); /* make sure WM_MOUSELEAVE messages are (re)enabled. */ } else {
SDL_Window *focus_window = SDL_GetMouseFocus();
if (focus_window) {
SDL_WindowData *data = (SDL_WindowData *)focus_window->driverdata;
if (!data->mouse_tracked) {
SDL_SetMouseFocus(NULL);
}
} }
ReleaseCapture();
} }
/* While we were thinking of SetCapture() when designing this API in SDL, return 0;
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);
} }
static Uint32 static Uint32

View File

@ -953,18 +953,6 @@ void WIN_OnWindowEnter(_THIS, SDL_Window * window)
if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) { if (window->flags & SDL_WINDOW_ALWAYS_ON_TOP) {
WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE); WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE);
} }
#ifdef WM_MOUSELEAVE
{
TRACKMOUSEEVENT trackMouseEvent;
trackMouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
trackMouseEvent.dwFlags = TME_LEAVE;
trackMouseEvent.hwndTrack = data->hwnd;
TrackMouseEvent(&trackMouseEvent);
}
#endif /* WM_MOUSELEAVE */
} }
void void

View File

@ -53,6 +53,7 @@ typedef struct
SDL_bool in_window_deactivation; SDL_bool in_window_deactivation;
RECT cursor_clipped_rect; RECT cursor_clipped_rect;
SDL_Point last_raw_mouse_position; SDL_Point last_raw_mouse_position;
SDL_bool mouse_tracked;
struct SDL_VideoData *videodata; struct SDL_VideoData *videodata;
#if SDL_VIDEO_OPENGL_EGL #if SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface; EGLSurface egl_surface;