From 0a8ccb11ebc4d229adb8e5326a11bdc68e4022ac Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 29 Jun 2026 12:14:08 -0400 Subject: [PATCH] win32: Use the current cursor coordinates when processing WM_NCACTIVATE The event coordinates returned by GetMessagePos() for WM_NCACTIVATE are out of date if the cursor moved while an overlay was active, and may indicate that the cursor is still in the window when it is not. Always use the current cursor coordinates when processing this message to avoid incorrectly setting mouse focus if the cursor is no longer within the window. --- src/video/windows/SDL_windowsevents.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index eaf8b7566a..b8cecc7fd5 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -329,7 +329,7 @@ static void WIN_CheckAsyncMouseRelease(Uint64 timestamp, SDL_WindowData *data) data->mouse_button_flags = (WPARAM)-1; } -static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos) +static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, LPPOINT pos) { SDL_WindowData *data = window->internal; HWND hwnd = data->hwnd; @@ -366,8 +366,8 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus, DWORD pos) // In relative mode we are guaranteed to have mouse focus if we have keyboard focus if (!SDL_GetMouse()->relative_mode) { - cursorPos.x = (LONG)GET_X_LPARAM(pos); - cursorPos.y = (LONG)GET_Y_LPARAM(pos); + cursorPos.x = pos->x; + cursorPos.y = pos->y; ScreenToClient(hwnd, &cursorPos); SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)cursorPos.x, (float)cursorPos.y); } @@ -1221,8 +1221,13 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara /* Update the focus here, since it's possible to get WM_ACTIVATE and WM_SETFOCUS without actually being the foreground window, but this appears to get called in all cases where - the global foreground window changes to and from this window. */ - WIN_UpdateFocus(data->window, !!wParam, GetMessagePos()); + the global foreground window changes to and from this window. + + The current cursor position is needed here, as the message position contains old + coordinates if the pointer moved while an overlay was active. */ + POINT pos = { 0, 0 }; + GetCursorPos(&pos); + WIN_UpdateFocus(data->window, !!wParam, &pos); /* Handle borderless windows; this event is intended for drawing the titlebar, so we need to stop that from happening. */ @@ -1246,7 +1251,9 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_ACTIVATE: { // Update the focus in case we changed focus to a child window and then away from the application - WIN_UpdateFocus(data->window, !!LOWORD(wParam), GetMessagePos()); + const DWORD msgPos = GetMessagePos(); + POINT pos = { GET_X_LPARAM(msgPos), GET_Y_LPARAM(msgPos) }; + WIN_UpdateFocus(data->window, !!LOWORD(wParam), &pos); } break; case WM_MOUSEACTIVATE: @@ -1269,14 +1276,18 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara case WM_SETFOCUS: { // Update the focus in case it's changing between top-level windows in the same application - WIN_UpdateFocus(data->window, true, GetMessagePos()); + const DWORD msgPos = GetMessagePos(); + POINT pos = { GET_X_LPARAM(msgPos), GET_Y_LPARAM(msgPos) }; + WIN_UpdateFocus(data->window, true, &pos); } break; case WM_KILLFOCUS: case WM_ENTERIDLE: { // Update the focus in case it's changing between top-level windows in the same application - WIN_UpdateFocus(data->window, false, GetMessagePos()); + const DWORD msgPos = GetMessagePos(); + POINT pos = { GET_X_LPARAM(msgPos), GET_Y_LPARAM(msgPos) }; + WIN_UpdateFocus(data->window, false, &pos); } break; case WM_POINTERENTER: