diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 22780ad84..4130f9706 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1088,19 +1088,25 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) inside their function, so I have to do it here. */ BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + UINT dpi; + + dpi = 96; size.top = 0; size.left = 0; size.bottom = h; size.right = w; if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { - UINT dpi = data->videodata->GetDpiForWindow(hwnd); + dpi = data->videodata->GetDpiForWindow(hwnd); data->videodata->AdjustWindowRectExForDpi(&size, style, menu, 0, dpi); } else { AdjustWindowRectEx(&size, style, menu, 0); } w = size.right - size.left; h = size.bottom - size.top; +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_GETMINMAXINFO: max window size: %dx%d using dpi: %u", w, h, dpi); +#endif } /* Fix our size to the current size */ @@ -1422,7 +1428,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_GETDPISCALEDSIZE: /* Windows 10 Creators Update+ */ - /* Documented as only being sent to windows that are per-monitor V2 DPI aware. */ + /* Documented as only being sent to windows that are per-monitor V2 DPI aware. + + Experimentation shows it's only sent during interactive dragging, not in response to + SetWindowPos. */ if (data->videodata->GetDpiForWindow && data->videodata->AdjustWindowRectExForDpi) { /* Windows expects applications to scale their window rects linearly when dragging between monitors with different DPI's. @@ -1490,6 +1499,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { const int newDPI = HIWORD(wParam); RECT* const suggestedRect = (RECT*)lParam; + SDL_bool setExpectedResize = SDL_FALSE; int w, h; #ifdef HIGHDPI_DEBUG @@ -1497,12 +1507,28 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) suggestedRect->left, suggestedRect->top, suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top); #endif - /* DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 means that - WM_GETDPISCALEDSIZE will have been called, so we can use suggestedRect. */ + if (data->expected_resize) { + /* This DPI change is coming from an explicit SetWindowPos call within SDL. + Assume all call sites are calculating the DPI-aware frame correctly, so + we don't need to do any further adjustment. */ +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_DPICHANGED: Doing nothing, assuming window is already sized correctly"); +#endif + return 0; + } + + /* Interactive user-initiated resizing/movement */ + if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { + /* WM_GETDPISCALEDSIZE should have been called prior, so we can trust the given + suggestedRect. */ w = suggestedRect->right - suggestedRect->left; h = suggestedRect->bottom - suggestedRect->top; - } else { + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_DPICHANGED: using suggestedRect"); +#endif + } else { RECT rect = { 0, 0, data->window->w, data->window->h }; const DWORD style = GetWindowLong(hwnd, GWL_STYLE); const BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); @@ -1521,7 +1547,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) suggestedRect->left, suggestedRect->top, w, h); #endif - data->expected_resize = SDL_TRUE; + if (!data->expected_resize) { + setExpectedResize = SDL_TRUE; + data->expected_resize = SDL_TRUE; + } SetWindowPos(hwnd, NULL, suggestedRect->left, @@ -1529,7 +1558,13 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) w, h, SWP_NOZORDER | SWP_NOACTIVATE); - data->expected_resize = SDL_FALSE; + if (setExpectedResize) { + /* Only unset data->expected_resize if we set it above. + WM_DPICHANGED can happen inside a block of code that sets data->expected_resize, + e.g. WIN_SetWindowPositionInternal. + */ + data->expected_resize = SDL_FALSE; + } return 0; } break; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index e2289f3ad..50420dcbf 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -46,6 +46,8 @@ #define SWP_NOCOPYBITS 0 #endif +/* #define HIGHDPI_DEBUG */ + /* Fake window to help with DirectInput events. */ HWND SDL_HelperWindow = NULL; static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher"); @@ -114,13 +116,14 @@ GetWindowStyle(SDL_Window * window) } static void -WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current, - SDL_bool force_ignore_window_dpi) +WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) { SDL_WindowData *data = (SDL_WindowData *)window->driverdata; SDL_VideoData* videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL; RECT rect; + UINT dpi; + dpi = 96; rect.left = 0; rect.top = 0; rect.right = (use_current ? window->w : window->windowed.w); @@ -133,33 +136,21 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { /* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of AdjustWindowRectEx. */ - UINT dpi; + + UINT unused; + RECT screen_rect; + HMONITOR mon; - if (data && !force_ignore_window_dpi) { - /* The usual case - we have a HWND, so we can look up the DPI to use. */ - dpi = videodata->GetDpiForWindow(data->hwnd); - } else { - /* In this case we guess the window DPI based on its rectangle on the screen. - - This happens at creation time of an SDL window, before we have a HWND, - and also in a bug workaround (when force_ignore_window_dpi is SDL_TRUE - - see WIN_SetWindowFullscreen). - */ - UINT unused; - RECT screen_rect; - HMONITOR mon; + screen_rect.left = (use_current ? window->x : window->windowed.x); + screen_rect.top = (use_current ? window->y : window->windowed.y); + screen_rect.right = screen_rect.left + (use_current ? window->w : window->windowed.w); + screen_rect.bottom = screen_rect.top + (use_current ? window->h : window->windowed.h); - screen_rect.left = (use_current ? window->x : window->windowed.x); - screen_rect.top = (use_current ? window->y : window->windowed.y); - screen_rect.right = screen_rect.left + (use_current ? window->w : window->windowed.w); - screen_rect.bottom = screen_rect.top + (use_current ? window->h : window->windowed.h); + mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST); - mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST); - - /* GetDpiForMonitor docs promise to return the same hdpi / vdpi */ - if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpi, &unused) != S_OK) { - dpi = 96; - } + /* GetDpiForMonitor docs promise to return the same hdpi / vdpi */ + if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpi, &unused) != S_OK) { + dpi = 96; } videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi); @@ -172,6 +163,15 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x *y = (use_current ? window->y : window->windowed.y) + rect.top; *width = (rect.right - rect.left); *height = (rect.bottom - rect.top); + +#ifdef HIGHDPI_DEBUG + SDL_Log("WIN_AdjustWindowRectWithStyle: in: %d, %d, %dx%d, returning: %d, %d, %dx%d, used dpi %d for frame calculation", + (use_current ? window->x : window->windowed.x), + (use_current ? window->y : window->windowed.y), + (use_current ? window->w : window->windowed.w), + (use_current ? window->h : window->windowed.h), + *x, *y, *width, *height, dpi); +#endif } static void @@ -184,7 +184,7 @@ WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height style = GetWindowLong(hwnd, GWL_STYLE); menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current, SDL_FALSE); + WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current); } static void @@ -279,7 +279,9 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre int x, y; /* Figure out what the window area will be */ WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE); + data->expected_resize = SDL_TRUE; SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, w, h, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE); + data->expected_resize = SDL_FALSE; } else { window->w = w; window->h = h; @@ -395,7 +397,7 @@ WIN_CreateWindow(_THIS, SDL_Window * window) style |= GetWindowStyle(window); /* Figure out what the window area will be */ - WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE, SDL_FALSE); + WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE); hwnd = CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL, @@ -580,7 +582,10 @@ WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon) void WIN_SetWindowPosition(_THIS, SDL_Window * window) { - WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE); + /* HighDPI support: removed SWP_NOSIZE. If the move results in a DPI change, we need to allow + * the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different). + */ + WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOACTIVATE); } void @@ -820,13 +825,7 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, } menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - /* HighDPI bug workaround - when leaving exclusive fullscreen, the window DPI reported - by GetDpiForWindow will be wrong. Pass SDL_TRUE for `force_ignore_window_dpi` - makes us recompute the DPI based on the monitor we are restoring onto. - Fixes windows shrinking slightly when going from exclusive fullscreen to windowed - on a HighDPI monitor with scaling. - */ - WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE, SDL_TRUE); + WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); } SetWindowLong(hwnd, GWL_STYLE, style); data->expected_resize = SDL_TRUE; diff --git a/src/video/windows/wmmsg.h b/src/video/windows/wmmsg.h index bd416eb80..86cae1df9 100644 --- a/src/video/windows/wmmsg.h +++ b/src/video/windows/wmmsg.h @@ -762,7 +762,7 @@ const char *wmtab[] = { "UNKNOWN (737)", "UNKNOWN (738)", "UNKNOWN (739)", - "UNKNOWN (740)", + "WM_GETDPISCALEDSIZE", "UNKNOWN (741)", "UNKNOWN (742)", "UNKNOWN (743)",