win32: Use the current cursor coordinates when processing WM_NCACTIVATE
Some checks are pending
Build (All) / level2 (push) Blocked by required conditions
Build (All) / Create test plan (push) Waiting to run
Build (All) / level1 (push) Blocked by required conditions

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.

(cherry picked from commit 0a8ccb11eb)
This commit is contained in:
Frank Praznik 2026-06-29 12:14:08 -04:00
parent 1326f406a6
commit 2ccd76818e
No known key found for this signature in database

View file

@ -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: