diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 92fbb5778b205..f3f0eecd69717 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1690,6 +1690,36 @@ extern "C" { */ #define SDL_HINT_WINDOWS_USE_D3D9EX "SDL_WINDOWS_USE_D3D9EX" +/** + * \brief Controls whether SDL will declare the process to be DPI aware. + * + * This hint must be set before initializing the video subsystem. + * + * The main purpose of declaring DPI awareness is to disable OS bitmap scaling of SDL windows on monitors with + * a DPI scale factor. + * + * This hint is equivalent to requesting DPI awareness via external means (e.g. calling SetProcessDpiAwarenessContext) + * and does not cause SDL to use a virtualized coordinate system, so it will generally give you 1 SDL coordinate = 1 pixel + * even on high-DPI displays. + * + * For more information, see: + * https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows + * + * This variable can be set to the following values: + * "" - Do not change the DPI awareness (default). + * "unaware" - Declare the process as DPI unaware. (Windows 8.1 and later). + * "system" - Request system DPI awareness. (Vista and later). + * "permonitor" - Request per-monitor DPI awareness. (Windows 8.1 and later). + * "permonitorv2" - Request per-monitor V2 DPI awareness. (Windows 10, version 1607 and later). + * The most visible difference from "permonitor" is that window title bar will be scaled + * to the visually correct size when dragging between monitors with different scale factors. + * This is the preferred DPI awareness level. + * + * If the requested DPI awareness is not available on the currently running OS, SDL will try to request the best + * available match. + */ +#define SDL_HINT_WINDOWS_DPI_AWARENESS "SDL_WINDOWS_DPI_AWARENESS" + /** * \brief A variable controlling whether the window frame and title bar are interactive when the cursor is hidden * diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index ba2553884acb7..ed47003167fb6 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -33,6 +33,7 @@ #include "../../events/SDL_touch_c.h" #include "../../events/scancodes_windows.h" #include "SDL_hints.h" +#include "SDL_log.h" /* Dropfile support */ #include @@ -51,6 +52,8 @@ #include "wmmsg.h" #endif +/* #define HIGHDPI_DEBUG */ + /* Masks for processing the windows KEYDOWN and KEYUP messages */ #define REPEATED_KEYMASK (1<<30) #define EXTENDED_KEYMASK (1<<24) @@ -85,6 +88,12 @@ #ifndef WM_UNICHAR #define WM_UNICHAR 0x0109 #endif +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif +#ifndef WM_GETDPISCALEDSIZE +#define WM_GETDPISCALEDSIZE 0x02E4 +#endif #ifndef IS_HIGH_SURROGATE #define IS_HIGH_SURROGATE(x) (((x) >= 0xd800) && ((x) <= 0xdbff)) @@ -1077,7 +1086,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) size.bottom = h; size.right = w; - AdjustWindowRectEx(&size, style, menu, 0); + if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { + UINT 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; } @@ -1147,6 +1161,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) h = rect.bottom - rect.top; SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w, h); +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_WINDOWPOSCHANGED: Windows client rect (pixels): (%d, %d) (%d x %d)\tSDL client rect: (%d, %d) (%d x %d)\tGetDpiForWindow: %d", + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + x, y, w, h, data->videodata->GetDpiForWindow ? (int)data->videodata->GetDpiForWindow(data->hwnd) : 0); +#endif + /* Forces a WM_PAINT event */ InvalidateRect(hwnd, NULL, FALSE); @@ -1385,6 +1405,120 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } break; + + case WM_GETDPISCALEDSIZE: + /* Windows 10 Creators Update+ */ + /* Documented as only being sent to windows that are per-monitor V2 DPI aware. */ + if (data->videodata->GetDpiForWindow && data->videodata->AdjustWindowRectExForDpi) { + /* Windows expects applications to scale their window rects linearly + when dragging between monitors with different DPI's. + e.g. a 100x100 window dragged to a 200% scaled monitor + becomes 200x200. + + For SDL, we instead want the client size to scale linearly. + This is not the same as the window rect scaling linearly, + because Windows doesn't scale the non-client area (titlebar etc.) + linearly. So, we need to handle this message to request custom + scaling. */ + + const int nextDPI = (int)wParam; + const int prevDPI = (int)data->videodata->GetDpiForWindow(hwnd); + SIZE *sizeInOut = (SIZE *)lParam; + + int frame_w, frame_h; + int query_client_w_win, query_client_h_win; + + const DWORD style = GetWindowLong(hwnd, GWL_STYLE); + const BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_GETDPISCALEDSIZE: current DPI: %d potential DPI: %d input size: (%dx%d)", + prevDPI, nextDPI, sizeInOut->cx, sizeInOut->cy); +#endif + + /* Subtract the window frame size that would have been used at prevDPI */ + { + RECT rect = {0}; + + if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, prevDPI); + } + + frame_w = -rect.left + rect.right; + frame_h = -rect.top + rect.bottom; + + query_client_w_win = sizeInOut->cx - frame_w; + query_client_h_win = sizeInOut->cy - frame_h; + } + + /* Add the window frame size that would be used at nextDPI */ + { + RECT rect = { 0, 0, query_client_w_win, query_client_h_win }; + + if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, nextDPI); + } + + /* This is supposed to control the suggested rect param of WM_DPICHANGED */ + sizeInOut->cx = rect.right - rect.left; + sizeInOut->cy = rect.bottom - rect.top; + } + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_GETDPISCALEDSIZE: output size: (%dx%d)", sizeInOut->cx, sizeInOut->cy); +#endif + return TRUE; + } + break; + + case WM_DPICHANGED: + /* Windows 8.1+ */ + { + const int newDPI = HIWORD(wParam); + RECT* const suggestedRect = (RECT*)lParam; + int w, h; + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_DPICHANGED: to %d\tsuggested rect: (%d, %d), (%dx%d)\n", newDPI, + 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 (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) { + w = suggestedRect->right - suggestedRect->left; + h = suggestedRect->bottom - suggestedRect->top; + } 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); + + if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { + AdjustWindowRectEx(&rect, style, menu, 0); + } + + w = rect.right - rect.left; + h = rect.bottom - rect.top; + } + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_DPICHANGED: current SDL window size: (%dx%d)\tcalling SetWindowPos: (%d, %d), (%dx%d)\n", + data->window->w, data->window->h, + suggestedRect->left, suggestedRect->top, w, h); +#endif + + data->expected_resize = SDL_TRUE; + SetWindowPos(hwnd, + NULL, + suggestedRect->left, + suggestedRect->top, + w, + h, + SWP_NOZORDER | SWP_NOACTIVATE); + data->expected_resize = SDL_FALSE; + return 0; + } + break; } /* If there's a window proc, assume it's going to handle messages */ diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 39806930ba3b2..097e57ec0736e 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -122,6 +122,16 @@ WIN_CreateDevice(int devindex) data->CloseTouchInputHandle = (BOOL (WINAPI *)(HTOUCHINPUT)) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle"); data->GetTouchInputInfo = (BOOL (WINAPI *)(HTOUCHINPUT, UINT, PTOUCHINPUT, int)) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo"); data->RegisterTouchWindow = (BOOL (WINAPI *)(HWND, ULONG)) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow"); + data->SetProcessDPIAware = (BOOL (WINAPI *)(void)) SDL_LoadFunction(data->userDLL, "SetProcessDPIAware"); + data->SetProcessDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT)) SDL_LoadFunction(data->userDLL, "SetProcessDpiAwarenessContext"); + data->SetThreadDpiAwarenessContext = (DPI_AWARENESS_CONTEXT (WINAPI *)(DPI_AWARENESS_CONTEXT)) SDL_LoadFunction(data->userDLL, "SetThreadDpiAwarenessContext"); + data->GetThreadDpiAwarenessContext = (DPI_AWARENESS_CONTEXT (WINAPI *)(void)) SDL_LoadFunction(data->userDLL, "GetThreadDpiAwarenessContext"); + data->GetAwarenessFromDpiAwarenessContext = (DPI_AWARENESS (WINAPI *)(DPI_AWARENESS_CONTEXT)) SDL_LoadFunction(data->userDLL, "GetAwarenessFromDpiAwarenessContext"); + data->EnableNonClientDpiScaling = (BOOL (WINAPI *)(HWND)) SDL_LoadFunction(data->userDLL, "EnableNonClientDpiScaling"); + data->AdjustWindowRectExForDpi = (BOOL (WINAPI *)(LPRECT, DWORD, BOOL, DWORD, UINT)) SDL_LoadFunction(data->userDLL, "AdjustWindowRectExForDpi"); + data->GetDpiForWindow = (UINT (WINAPI *)(HWND)) SDL_LoadFunction(data->userDLL, "GetDpiForWindow"); + data->AreDpiAwarenessContextsEqual = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT)) SDL_LoadFunction(data->userDLL, "AreDpiAwarenessContextsEqual"); + data->IsValidDpiAwarenessContext = (BOOL (WINAPI *)(DPI_AWARENESS_CONTEXT)) SDL_LoadFunction(data->userDLL, "IsValidDpiAwarenessContext"); } else { SDL_ClearError(); } @@ -129,6 +139,7 @@ WIN_CreateDevice(int devindex) data->shcoreDLL = SDL_LoadObject("SHCORE.DLL"); if (data->shcoreDLL) { data->GetDpiForMonitor = (HRESULT (WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *)) SDL_LoadFunction(data->shcoreDLL, "GetDpiForMonitor"); + data->SetProcessDpiAwareness = (HRESULT (WINAPI *)(PROCESS_DPI_AWARENESS)) SDL_LoadFunction(data->shcoreDLL, "SetProcessDpiAwareness"); } else { SDL_ClearError(); } @@ -231,11 +242,103 @@ VideoBootStrap WINDOWS_bootstrap = { "windows", "SDL Windows video driver", WIN_CreateDevice }; +static BOOL +WIN_DeclareDPIAwareUnaware(_THIS) +{ + SDL_VideoData* data = (SDL_VideoData*)_this->driverdata; + + if (data->SetProcessDpiAwarenessContext) { + return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); + } else if (data->SetProcessDpiAwareness) { + /* Windows 8.1 */ + return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_DPI_UNAWARE)); + } + return FALSE; +} + +static BOOL +WIN_DeclareDPIAwareSystem(_THIS) +{ + SDL_VideoData* data = (SDL_VideoData*)_this->driverdata; + + if (data->SetProcessDpiAwarenessContext) { + /* Windows 10, version 1607 */ + return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); + } else if (data->SetProcessDpiAwareness) { + /* Windows 8.1 */ + return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)); + } else if (data->SetProcessDPIAware) { + /* Windows Vista */ + return data->SetProcessDPIAware(); + } + return FALSE; +} + +static BOOL +WIN_DeclareDPIAwarePerMonitor(_THIS) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + + if (data->SetProcessDpiAwarenessContext) { + /* Windows 10, version 1607 */ + return data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + } else if (data->SetProcessDpiAwareness) { + /* Windows 8.1 */ + return SUCCEEDED(data->SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)); + } else { + /* Older OS: fall back to system DPI aware */ + return WIN_DeclareDPIAwareSystem(_this); + } + return FALSE; +} + +static BOOL +WIN_DeclareDPIAwarePerMonitorV2(_THIS) +{ + SDL_VideoData* data = (SDL_VideoData*)_this->driverdata; + + /* Declare DPI aware(may have been done in external code or a manifest, as well) */ + if (data->SetProcessDpiAwarenessContext) { + /* Windows 10, version 1607 */ + + /* NOTE: SetThreadDpiAwarenessContext doesn't work here with OpenGL - the OpenGL contents + end up still getting OS scaled. (tested on Windows 10 21H1 19043.1348, NVIDIA 496.49) */ + if (data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { + return TRUE; + } else { + return WIN_DeclareDPIAwarePerMonitor(_this); + } + } else { + /* Older OS: fall back to per-monitor (or system) */ + return WIN_DeclareDPIAwarePerMonitor(_this); + } +} + +static void +WIN_InitDPIAwareness(_THIS) +{ + const char* hint = SDL_GetHint(SDL_HINT_WINDOWS_DPI_AWARENESS); + + if (hint != NULL) { + if (SDL_strcmp(hint, "permonitorv2") == 0) { + WIN_DeclareDPIAwarePerMonitorV2(_this); + } else if (SDL_strcmp(hint, "permonitor") == 0) { + WIN_DeclareDPIAwarePerMonitor(_this); + } else if (SDL_strcmp(hint, "system") == 0) { + WIN_DeclareDPIAwareSystem(_this); + } else if (SDL_strcmp(hint, "unaware") == 0) { + WIN_DeclareDPIAwareUnaware(_this); + } + } +} + int WIN_VideoInit(_THIS) { SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + WIN_InitDPIAwareness(_this); + if (WIN_InitModes(_this) < 0) { return -1; } @@ -455,6 +558,19 @@ SDL_DXGIGetOutputInfo(int displayIndex, int *adapterIndex, int *outputIndex) #endif } +SDL_bool +WIN_IsPerMonitorV2DPIAware(_THIS) +{ + SDL_VideoData* data = (SDL_VideoData*) _this->driverdata; + + if (data->AreDpiAwarenessContextsEqual && data->GetThreadDpiAwarenessContext) { + /* Windows 10, version 1607 */ + return (SDL_bool)data->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, + data->GetThreadDpiAwarenessContext()); + } + return SDL_FALSE; +} + #endif /* SDL_VIDEO_DRIVER_WINDOWS */ /* vim: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index daf68e6c2d641..7f6469b8156dc 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -86,10 +86,40 @@ typedef enum MONITOR_DPI_TYPE { MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; +typedef enum PROCESS_DPI_AWARENESS { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; + #else #include #endif /* WINVER < 0x0603 */ +#ifndef _DPI_AWARENESS_CONTEXTS_ + +typedef enum DPI_AWARENESS { + DPI_AWARENESS_INVALID = -1, + DPI_AWARENESS_UNAWARE = 0, + DPI_AWARENESS_SYSTEM_AWARE = 1, + DPI_AWARENESS_PER_MONITOR_AWARE = 2 +} DPI_AWARENESS; + +DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); + +#define DPI_AWARENESS_CONTEXT_UNAWARE ((DPI_AWARENESS_CONTEXT)-1) +#define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE ((DPI_AWARENESS_CONTEXT)-2) +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3) + +#endif /* _DPI_AWARENESS_CONTEXTS_ */ + +/* Windows 10 Creators Update */ +#if NTDDI_VERSION < 0x0A000003 + +#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4) + +#endif /* NTDDI_VERSION < 0x0A000003 */ + typedef BOOL (*PFNSHFullScreen)(HWND, DWORD); typedef void (*PFCoordTransform)(SDL_Window*, POINT*); @@ -137,13 +167,24 @@ typedef struct SDL_VideoData BOOL (WINAPI *CloseTouchInputHandle)( HTOUCHINPUT ); BOOL (WINAPI *GetTouchInputInfo)( HTOUCHINPUT, UINT, PTOUCHINPUT, int ); BOOL (WINAPI *RegisterTouchWindow)( HWND, ULONG ); + BOOL (WINAPI *SetProcessDPIAware)( void ); + BOOL (WINAPI *SetProcessDpiAwarenessContext)( DPI_AWARENESS_CONTEXT ); + DPI_AWARENESS_CONTEXT (WINAPI *SetThreadDpiAwarenessContext)( DPI_AWARENESS_CONTEXT ); + DPI_AWARENESS_CONTEXT (WINAPI *GetThreadDpiAwarenessContext)( void ); + DPI_AWARENESS (WINAPI *GetAwarenessFromDpiAwarenessContext)( DPI_AWARENESS_CONTEXT ); + BOOL (WINAPI *EnableNonClientDpiScaling)( HWND ); + BOOL (WINAPI *AdjustWindowRectExForDpi)( LPRECT, DWORD, BOOL, DWORD, UINT ); + UINT (WINAPI *GetDpiForWindow)( HWND ); + BOOL (WINAPI *AreDpiAwarenessContextsEqual)(DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT); + BOOL (WINAPI *IsValidDpiAwarenessContext)(DPI_AWARENESS_CONTEXT); void* shcoreDLL; HRESULT (WINAPI *GetDpiForMonitor)( HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY ); - + HRESULT (WINAPI *SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness); + SDL_bool ime_com_initialized; struct ITfThreadMgr *ime_threadmgr; SDL_bool ime_initialized; @@ -200,6 +241,8 @@ extern SDL_bool g_WindowFrameUsableWhileCursorHidden; typedef struct IDirect3D9 IDirect3D9; extern SDL_bool D3D_LoadDLL( void **pD3DDLL, IDirect3D9 **pDirect3D9Interface ); +extern SDL_bool WIN_IsPerMonitorV2DPIAware(_THIS); + #endif /* SDL_windowsvideo_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d884b2b7c9a56..a7df3a55e264c 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -113,8 +113,11 @@ 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) +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) { + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + SDL_VideoData* videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL; RECT rect; rect.left = 0; @@ -125,8 +128,44 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x /* borderless windows will have WM_NCCALCSIZE return 0 for the non-client area. When this happens, it looks like windows will send a resize message expanding the window client area to the previous window + chrome size, so shouldn't need to adjust the window size for the set styles. */ - if (!(window->flags & SDL_WINDOW_BORDERLESS)) - AdjustWindowRectEx(&rect, style, menu, 0); + if (!(window->flags & SDL_WINDOW_BORDERLESS)) { + 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; + + 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); + + 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; + } + } + + videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi); + } else { + AdjustWindowRectEx(&rect, style, menu, 0); + } + } *x = (use_current ? window->x : window->windowed.x) + rect.left; *y = (use_current ? window->y : window->windowed.y) + rect.top; @@ -144,7 +183,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); + WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current, SDL_FALSE); } static void @@ -314,7 +353,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); + WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE, SDL_FALSE); hwnd = CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL, @@ -729,7 +768,13 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, } menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); + /* 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); } SetWindowLong(hwnd, GWL_STYLE, style); data->expected_resize = SDL_TRUE;