From f38fbef46f81cd4c73cbe94a31ad3d2ea676faec Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 19 May 2021 22:44:00 -0600 Subject: [PATCH 01/42] rebased windows-highdpi patch as of Nov 11, 2019 (b59d2dc from https://github.com/ericwa/SDL-mirror/tree/windows-highdpi) --- include/SDL_hints.h | 20 ++ include/SDL_video.h | 3 +- src/render/direct3d/SDL_render_d3d.c | 13 +- src/render/direct3d11/SDL_render_d3d11.c | 10 +- src/test/SDL_test_common.c | 2 +- src/video/SDL_video.c | 5 + src/video/windows/SDL_windowsevents.c | 211 ++++++++++++++++-- src/video/windows/SDL_windowsmodes.c | 273 +++++++++++++++++++---- src/video/windows/SDL_windowsmodes.h | 2 + src/video/windows/SDL_windowsopengl.c | 6 + src/video/windows/SDL_windowsopengl.h | 1 + src/video/windows/SDL_windowsvideo.c | 62 +++++ src/video/windows/SDL_windowsvideo.h | 46 +++- src/video/windows/SDL_windowswindow.c | 236 ++++++++++++++++++-- src/video/windows/SDL_windowswindow.h | 10 + 15 files changed, 809 insertions(+), 91 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 92fbb5778b205..65866f72053b7 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1121,6 +1121,26 @@ extern "C" { */ #define SDL_HINT_RENDER_DIRECT3D11_DEBUG "SDL_RENDER_DIRECT3D11_DEBUG" + /** + * \brief If set to 1, then allow high-DPI on all windows. + * + * If high-DPI is allowed, the framebuffer size (which can be queried by calling SDL_GL_GetDrawableSize, + * and returns a size in pixels) is decoupled from the SDL window size. SDL window sizes are in + * points, which are translated to pixels by multiplying by the scale factor of the monitor that + * the window is on. + * + * If high-DPI is not enabled with this hint, the framebuffer size will be equal to the window size + * returned by SDL_GetWindowSize, but the OS will scale up the framebuffer if the window is on a + * high-DPI monitor. + * + * An SDL_WINDOWEVENT_RESIZED event will be sent to the application when the framebuffer size changes, + * which could happen in response to the window moving to a monitor with a different scale factor + * (either from the user moving it, or SDL_SetWindowPosition), or the monitor's scale factor changing. + * + * High-DPI support is currently implemented on macOS and Windows. + */ +#define SDL_HINT_VIDEO_ALLOW_HIGHDPI "SDL_VIDEO_ALLOW_HIGHDPI" + /** * \brief A variable controlling whether the Direct3D device is initialized for thread-safe operations. * diff --git a/include/SDL_video.h b/include/SDL_video.h index e43cb27ecde51..91286d323a63d 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -116,7 +116,8 @@ typedef enum SDL_WINDOW_FOREIGN = 0x00000800, /**< window not created by SDL */ SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000, /**< window should be created in high-DPI mode if supported. On macOS NSHighResolutionCapable must be set true in the - application's Info.plist for this to have any effect. */ + application's Info.plist for this to have any effect. + (deprecated, use SDL_HINT_VIDEO_ALLOW_HIGHDPI) */ SDL_WINDOW_MOUSE_CAPTURE = 0x00004000, /**< window has mouse captured (unrelated to MOUSE_GRABBED) */ SDL_WINDOW_ALWAYS_ON_TOP = 0x00008000, /**< window should always be above others */ SDL_WINDOW_SKIP_TASKBAR = 0x00010000, /**< window should not be added to the taskbar */ diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index b267fb23edc7f..0a77946368eac 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -300,8 +300,7 @@ D3D_ActivateRenderer(SDL_Renderer * renderer) SDL_Window *window = renderer->window; int w, h; Uint32 window_flags = SDL_GetWindowFlags(window); - - SDL_GetWindowSize(window, &w, &h); + WIN_GetDrawableSize(window, &w, &h); data->pparams.BackBufferWidth = w; data->pparams.BackBufferHeight = h; if (window_flags & SDL_WINDOW_FULLSCREEN && (window_flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != SDL_WINDOW_FULLSCREEN_DESKTOP) { @@ -347,6 +346,13 @@ D3D_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) } } +static int +D3D_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) +{ + WIN_GetDrawableSize(renderer->window, w, h); + return 0; +} + static D3DBLEND GetBlendFunc(SDL_BlendFactor factor) { switch (factor) { @@ -1571,6 +1577,7 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) } renderer->WindowEvent = D3D_WindowEvent; + renderer->GetOutputSize = D3D_GetOutputSize; renderer->SupportsBlendMode = D3D_SupportsBlendMode; renderer->CreateTexture = D3D_CreateTexture; renderer->UpdateTexture = D3D_UpdateTexture; @@ -1600,7 +1607,7 @@ D3D_CreateRenderer(SDL_Window * window, Uint32 flags) SDL_GetWindowWMInfo(window, &windowinfo); window_flags = SDL_GetWindowFlags(window); - SDL_GetWindowSize(window, &w, &h); + WIN_GetDrawableSize(window, &w, &h); SDL_GetWindowDisplayMode(window, &fullscreen_mode); SDL_zero(pparams); diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 89e2ee544ea2f..441ce56517b3a 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -910,7 +910,7 @@ D3D11_CreateWindowSizeDependentResources(SDL_Renderer * renderer) /* The width and height of the swap chain must be based on the display's * non-rotated size. */ - SDL_GetWindowSize(renderer->window, &w, &h); + WIN_GetDrawableSize(renderer->window, &w, &h); data->rotation = D3D11_GetCurrentRotation(); /* SDL_Log("%s: windowSize={%d,%d}, orientation=%d\n", __FUNCTION__, w, h, (int)data->rotation); */ if (D3D11_IsDisplayRotated90Degrees(data->rotation)) { @@ -1041,6 +1041,13 @@ D3D11_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) } } +static int +D3D11_GetOutputSize(SDL_Renderer * renderer, int *w, int *h) +{ + WIN_GetDrawableSize(renderer->window, w, h); + return 0; +} + static SDL_bool D3D11_SupportsBlendMode(SDL_Renderer * renderer, SDL_BlendMode blendMode) { @@ -2353,6 +2360,7 @@ D3D11_CreateRenderer(SDL_Window * window, Uint32 flags) data->identity = MatrixIdentity(); renderer->WindowEvent = D3D11_WindowEvent; + renderer->GetOutputSize = D3D11_GetOutputSize; renderer->SupportsBlendMode = D3D11_SupportsBlendMode; renderer->CreateTexture = D3D11_CreateTexture; renderer->UpdateTexture = D3D11_UpdateTexture; diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 48c6c72f0b543..3779e5a55597d 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -244,7 +244,7 @@ SDLTest_CommonArg(SDLTest_CommonState * state, int index) return 1; } if (SDL_strcasecmp(argv[index], "--allow-highdpi") == 0) { - state->window_flags |= SDL_WINDOW_ALLOW_HIGHDPI; + SDL_SetHint(SDL_HINT_VIDEO_ALLOW_HIGHDPI, "1"); return 1; } if (SDL_strcasecmp(argv[index], "--windows") == 0) { diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 143f1172b8fed..30ae8ae1c7827 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -1517,6 +1517,11 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags) } } + /* This hint adds SDL_WINDOW_ALLOW_HIGHDPI to all windows. */ + if (SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_HIGHDPI, SDL_FALSE)) { + flags |= SDL_WINDOW_ALLOW_HIGHDPI; + } + if (flags & SDL_WINDOW_VULKAN) { if (!_this->Vulkan_CreateSurface) { SDL_SetError("Vulkan support is either not configured in SDL " diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index ba2553884acb7..e25c5d743abad 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)) @@ -648,6 +657,24 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData")); } if (!data) { + /* Request that windows scale the non-client area when moving between monitors. + This is only for Windows 10 Anniversary Update. + In the Windows 10 Creators Update, this is the default behaviour with a per-monitor V2 context. + */ + switch (msg) { + case WM_NCCREATE: + { + SDL_VideoDevice *device = SDL_GetVideoDevice(); + if (device && device->driverdata) { + SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, device->driverdata); + if (data->highdpi_enabled && data->EnableNonClientDpiScaling) { + data->EnableNonClientDpiScaling(hwnd); + } + } + } + break; + } + return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); } @@ -733,7 +760,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) /* Only generate mouse events for real mouse */ if (GetMouseMessageSource() != SDL_MOUSE_EVENT_SOURCE_TOUCH && lParam != data->last_pointer_update) { - SDL_SendMouseMotion(data->window, 0, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + + WIN_ClientPointToSDL(data->window, &x, &y); + + SDL_SendMouseMotion(data->window, 0, 0, x, y); } } } @@ -1031,6 +1063,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) int w, h; int min_w, min_h; int max_w, max_h; + int unused_x, unused_y; BOOL constrain_max_size; if (SDL_IsShapedWindow(data->window)) { @@ -1052,6 +1085,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) SDL_GetWindowMinimumSize(data->window, &min_w, &min_h); SDL_GetWindowMaximumSize(data->window, &max_w, &max_h); + /* Convert w, h, min_w, min_h, max_w, max_h to unscaled. + We can treat them as a point in the client area. */ + WIN_ClientPointFromSDL(data->window, &w, &h); + WIN_ClientPointFromSDL(data->window, &min_w, &min_h); + WIN_ClientPointFromSDL(data->window, &max_w, &max_h); + /* Store in min_w and min_h difference between current size and minimal size so we don't need to call AdjustWindowRectEx twice */ min_w -= w; @@ -1064,23 +1103,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) constrain_max_size = FALSE; } - if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) { - LONG style = GetWindowLong(hwnd, GWL_STYLE); - /* DJM - according to the docs for GetMenu(), the - return value is undefined if hwnd is a child window. - Apparently it's too difficult for MS to check - inside their function, so I have to do it here. - */ - BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - size.top = 0; - size.left = 0; - size.bottom = h; - size.right = w; - - AdjustWindowRectEx(&size, style, menu, 0); - w = size.right - size.left; - h = size.bottom - size.top; - } + /* Expand w/h to include the frame. */ + unused_x = 0; + unused_y = 0; + WIN_AdjustWindowRectWithRect(data->window, &unused_x, &unused_y, &w, &h); /* Fix our size to the current size */ info = (MINMAXINFO *) lParam; @@ -1141,12 +1167,27 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) x = rect.left; y = rect.top; + w = rect.right - rect.left; + h = rect.bottom - rect.top; + WIN_ScreenRectToSDL(&x, &y, &w, &h); + SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y); + /* NOTE: important to convert w/h from SDL (points) to Windows (pixels) using + WIN_ClientPointToSDL, which uses the window's actual + DPI value, rather than WIN_ScreenRectToSDL which guesses. */ w = rect.right - rect.left; h = rect.bottom - rect.top; + WIN_ClientPointToSDL(data->window, &w, &h); + 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 (points): (%d, %d) (%d x %d)", + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, + x, y, w, h); +#endif + /* Forces a WM_PAINT event */ InvalidateRect(hwnd, NULL, FALSE); @@ -1364,7 +1405,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) if (window->hit_test) { POINT winpoint = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; if (ScreenToClient(hwnd, &winpoint)) { - const SDL_Point point = { (int) winpoint.x, (int) winpoint.y }; + int x = (int) winpoint.x; + int y = (int) winpoint.y; + WIN_ClientPointToSDL(data->window, &x, &y); + const SDL_Point point = { x, y }; const SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); switch (rc) { #define POST_HIT_TEST(ret) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); return ret; } @@ -1385,6 +1429,135 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } break; + + case WM_GETDPISCALEDSIZE: /* Windows 10 Creators Update+ */ + /* GetDpiForWindow and AdjustWindowRectExForDpi will be present, but check anyway. */ + if (data->videodata->highdpi_enabled + && 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 potentialDPI = (int)wParam; + const int currentDPI = (int)data->videodata->GetDpiForWindow(hwnd); + SIZE *sizeInOut = (SIZE *)lParam; + + int x, y, w, h; + int frame_w, frame_h; + int query_client_w_win, query_client_h_win; + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_GETDPISCALEDSIZE: current DPI: %d potential DPI: %d. input size: (%dx%d)", currentDPI, potentialDPI, sizeInOut->cx, sizeInOut->cy); +#endif + + WIN_AdjustWindowRect(data->window, &x, &y, &w, &h, SDL_TRUE); + + /* get the frame size */ + frame_w = w - MulDiv(data->window->w, currentDPI, 96); + frame_h = h - MulDiv(data->window->h, currentDPI, 96); + + query_client_w_win = sizeInOut->cx - frame_w; + query_client_h_win = sizeInOut->cy - frame_h; + + /* convert to new dpi */ + query_client_w_win = MulDiv(query_client_w_win, potentialDPI, currentDPI); + query_client_h_win = MulDiv(query_client_h_win, potentialDPI, currentDPI); + + /* re-add the window frame in the new DPI */ + { + DWORD style = GetWindowLong(hwnd, GWL_STYLE); + BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + RECT rect; + + rect.left = 0; + rect.top = 0; + rect.right = query_client_w_win; + rect.bottom = query_client_h_win; + + if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, potentialDPI); + + /* 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+ */ + if (data->videodata->highdpi_enabled) { + const int newDPI = HIWORD(wParam); + RECT* const suggestedRect = (RECT*)lParam; + int x, y, w, h; + +#ifdef HIGHDPI_DEBUG + SDL_Log("WM_DPICHANGED: %d to %d\tsuggested rect: (%d, %d), (%dx%d)\n", + data->scaling_xdpi, newDPI, + suggestedRect->left, suggestedRect->top, suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top); +#endif + + /* update the cached DPI value for this window */ + data->scaling_xdpi = newDPI; + data->scaling_ydpi = newDPI; + + if (data->videodata->AreDpiAwarenessContextsEqual + && data->videodata->GetThreadDpiAwarenessContext + && data->videodata->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, data->videodata->GetThreadDpiAwarenessContext())) + { + /* + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 means that + WM_GETDPISCALEDSIZE will have been called, so we can use suggestedRect. + */ + w = suggestedRect->right - suggestedRect->left; + h = suggestedRect->bottom - suggestedRect->top; + } else { + /* + The OS does not support WM_GETDPISCALEDSIZE, so we can't use suggestedRect. + + (suggestedRect is calculated by default to preserves the apparent size of the window rect, + whereas we want to preserve the apparent size of the client rect.) + */ + WIN_AdjustWindowRect(data->window, &x, &y, &w, &h, SDL_TRUE); + } + +#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 + + /* clear the window size, to cause us to send a SDL_WINDOWEVENT_RESIZED event in WM_WINDOWPOSCHANGED */ + data->window->w = 0; + data->window->h = 0; + + 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_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 9080cc89552ad..b2a4d34b40b8d 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -50,6 +50,15 @@ WIN_UpdateDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_DisplayMode * int logical_width = GetDeviceCaps( hdc, HORZRES ); int logical_height = GetDeviceCaps( hdc, VERTRES ); + /* + If DPI-unaware: + - GetDeviceCaps( hdc, HORZRES ) will return the monitor width in points. + - DeviceMode.dmPelsWidth is actual pixels (unlike almost all other Windows API's, + it's not virtualized when DPI unaware). + + If DPI-aware: + - GetDeviceCaps( hdc, HORZRES ) will return pixels, same as DeviceMode.dmPelsWidth + */ mode->w = logical_width; mode->h = logical_height; @@ -301,29 +310,6 @@ WIN_InitModes(_THIS) return 0; } -int -WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) -{ - const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; - MONITORINFO minfo; - BOOL rc; - - SDL_zero(minfo); - minfo.cbSize = sizeof(MONITORINFO); - rc = GetMonitorInfo(data->MonitorHandle, &minfo); - - if (!rc) { - return SDL_SetError("Couldn't find monitor data"); - } - - rect->x = minfo.rcMonitor.left; - rect->y = minfo.rcMonitor.top; - rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left; - rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top; - - return 0; -} - int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * hdpi_out, float * vdpi_out) { @@ -344,29 +330,21 @@ WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * h } } else { // Window 8.0 and below: same DPI for all monitors. - HDC hdc; - int hdpi_int, vdpi_int, hpoints, vpoints, hpix, vpix; + int hpoints, vpoints, hpix, vpix; float hinches, vinches; - hdc = GetDC(NULL); - if (hdc == NULL) { - return SDL_SetError("GetDC failed"); - } - hdpi_int = GetDeviceCaps(hdc, LOGPIXELSX); - vdpi_int = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - + /* NOTE: all of this is just to compute the diagonal DPI. */ hpoints = GetSystemMetrics(SM_CXVIRTUALSCREEN); vpoints = GetSystemMetrics(SM_CYVIRTUALSCREEN); - hpix = MulDiv(hpoints, hdpi_int, 96); - vpix = MulDiv(vpoints, vdpi_int, 96); + hpix = MulDiv(hpoints, videodata->system_xdpi, 96); + vpix = MulDiv(vpoints, videodata->system_ydpi, 96); hinches = (float)hpoints / 96.0f; vinches = (float)vpoints / 96.0f; - hdpi = (float)hdpi_int; - vdpi = (float)vdpi_int; + hdpi = (float)videodata->system_xdpi; + vdpi = (float)videodata->system_ydpi; ddpi = SDL_ComputeDiagonalDPI(hpix, vpix, hinches, vinches); } @@ -383,12 +361,16 @@ WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * h return ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI"); } -int -WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +static int +WIN_GetDisplayBoundsInternal(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect, SDL_bool usable) { const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; + const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->driverdata; MONITORINFO minfo; + const RECT *rect_win; BOOL rc; + int x, y; + int w, h; SDL_zero(minfo); minfo.cbSize = sizeof(MONITORINFO); @@ -398,14 +380,157 @@ WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return SDL_SetError("Couldn't find monitor data"); } - rect->x = minfo.rcWork.left; - rect->y = minfo.rcWork.top; - rect->w = minfo.rcWork.right - minfo.rcWork.left; - rect->h = minfo.rcWork.bottom - minfo.rcWork.top; + rect_win = usable ? &minfo.rcWork : &minfo.rcMonitor; + + x = rect_win->left; + y = rect_win->top; + w = rect_win->right - rect_win->left; + h = rect_win->bottom - rect_win->top; + WIN_ScreenRectToSDL(&x, &y, &w, &h); + + rect->x = x; + rect->y = y; + rect->w = w; + rect->h = h; return 0; } +int +WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + return WIN_GetDisplayBoundsInternal(_this, display, rect, SDL_FALSE); +} + +int +WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + return WIN_GetDisplayBoundsInternal(_this, display, rect, SDL_TRUE); +} + +static int +WIN_GetMonitorDPIAndRects(const SDL_VideoData *videodata, HMONITOR monitor, UINT *xdpi, UINT *ydpi, RECT *monitorrect_sdl, RECT *monitorrect_win) +{ + HRESULT result; + MONITORINFO moninfo = { 0 }; + UINT unused; + int mon_width, mon_height; + + /* Check for Windows < 8.1*/ + if (!videodata->GetDpiForMonitor) { + *xdpi = videodata->system_xdpi; + *ydpi = videodata->system_ydpi; + } else { + result = videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, xdpi, &unused); + *ydpi = *xdpi; + if (result != S_OK) { + /* Shouldn't happen? */ + return SDL_SetError("GetDpiForMonitor failed"); + } + } + + moninfo.cbSize = sizeof(MONITORINFO); + if (!GetMonitorInfo(monitor, &moninfo)) { + /* Shouldn't happen? */ + return SDL_SetError("GetMonitorInfo failed"); + } + + *monitorrect_win = moninfo.rcMonitor; + *monitorrect_sdl = moninfo.rcMonitor; + + /* fix up the right/bottom of monitorrect_sdl */ + mon_width = moninfo.rcMonitor.right - moninfo.rcMonitor.left; + mon_height = moninfo.rcMonitor.bottom - moninfo.rcMonitor.top; + mon_width = MulDiv(mon_width, 96, *xdpi); + mon_height = MulDiv(mon_height, 96, *ydpi); + + monitorrect_sdl->right = monitorrect_sdl->left + mon_width; + monitorrect_sdl->bottom = monitorrect_sdl->top + mon_height; + + return 0; +} + +/* Convert an SDL to a Windows screen rect. */ +void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h) +{ + const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); + const SDL_VideoData *videodata; + RECT inputrect; + RECT monitorrect_sdl, monitorrect_win; + UINT xdpi, ydpi; + HMONITOR monitor; + + if (!videodevice || !videodevice->driverdata) + return; + + videodata = (SDL_VideoData *)videodevice->driverdata; + if (!videodata->highdpi_enabled) + return; + + /* + The trick here is passing SDL coordinates to MonitorFromRect, which expects Windows + coordinates (pixels). This is wrong, but there is no real alternative, and due to + the way we derive the SDL coordinate system, it works OK: + + - top-left corner of monitors in SDL coordinates are identical to the top-left corner in Windows coordinates. + - the widths/heights of monitors (and windows) in SDL coords are in scaled points, + which are equal or less than the corresponding sizes in pixels (because we only support scale factors >=100%) + - becuase of the above two points, a rect (in SDL coordinates) that is fully inside + a monitor's bounds (in SDL coordinates) will also be fully inside that monitor's bounds in Windows coordinates. + */ + inputrect.left = *x; + inputrect.top = *y; + inputrect.right = *x + *w; + inputrect.bottom = *y + *h; + monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); + + if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { + *w = MulDiv(*w, xdpi, 96); + *h = MulDiv(*h, ydpi, 96); + + *x = monitorrect_sdl.left + MulDiv(*x - monitorrect_sdl.left, xdpi, 96); + *y = monitorrect_sdl.top + MulDiv(*y - monitorrect_sdl.top, ydpi, 96); + + /* ensure the result is not past the right/bottom of the monitor rect */ + if (*x >= monitorrect_win.right) + *x = monitorrect_win.right - 1; + if (*y >= monitorrect_win.bottom) + *y = monitorrect_win.bottom - 1; + } +} + +/* Converts a Windows screen rect to an SDL one. */ +void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h) +{ + const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); + const SDL_VideoData *videodata; + RECT inputrect; + RECT monitorrect_sdl, monitorrect_win; + UINT xdpi, ydpi; + HMONITOR monitor; + + if (!videodevice || !videodevice->driverdata) + return; + + videodata = (SDL_VideoData *)videodevice->driverdata; + if (!videodata->highdpi_enabled) + return; + + inputrect.left = *x; + inputrect.top = *y; + inputrect.right = *x + *w; + inputrect.bottom = *y + *h; + monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); + + if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { + *w = MulDiv(*w, 96, xdpi); + *h = MulDiv(*h, 96, ydpi); + + *x = monitorrect_win.left + MulDiv(*x - monitorrect_win.left, 96, xdpi); + *y = monitorrect_win.top + MulDiv(*y - monitorrect_win.top, 96, ydpi); + } +} + void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { @@ -432,6 +557,36 @@ WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display) } } +#ifdef DEBUG_MODES +static void +WIN_LogMonitor(_THIS, HMONITOR mon) +{ + const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->driverdata; + MONITORINFOEX minfo; + UINT xdpi = 0, ydpi = 0; + char *name_utf8; + + if (vid_data->GetDpiForMonitor) + vid_data->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + + SDL_zero(minfo); + minfo.cbSize = sizeof(minfo); + GetMonitorInfo(mon, (LPMONITORINFO)&minfo); + + name_utf8 = WIN_StringToUTF8(minfo.szDevice); + + SDL_Log("WIN_LogMonitor: monitor \"%s\": dpi: %d. Windows virtual screen coordinates: (%d, %d), %dx%d", + name_utf8, + xdpi, + minfo.rcMonitor.left, + minfo.rcMonitor.top, + minfo.rcMonitor.right - minfo.rcMonitor.left, + minfo.rcMonitor.bottom - minfo.rcMonitor.top); + + SDL_free(name_utf8); +} +#endif + int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { @@ -439,9 +594,35 @@ WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) SDL_DisplayModeData *data = (SDL_DisplayModeData *) mode->driverdata; LONG status; +#ifdef DEBUG_MODES + SDL_Log("WIN_SetDisplayMode: monitor before mode change:"); + WIN_LogMonitor(_this, displaydata->MonitorHandle); +#endif + + /* + High-DPI notes: + + - ChangeDisplaySettingsEx always takes pixels. + - e.g. if the display is set to 2880x1800 with 200% scaling in the Control Panel, + - calling ChangeDisplaySettingsEx with a dmPelsWidth/Height other than 2880x1800 will + change the monitor DPI to 96. (100% scaling) + - calling ChangeDisplaySettingsEx with a dmPelsWidth/Height of 2880x1800 (or a NULL DEVMODE*) will + reset the monitor DPI to 192. (200% scaling) + NOTE: these are temporary changes in DPI, not modifications to the Control Panel setting. + + - Windows bug: windows do not get a WM_DPICHANGED message after a ChangeDisplaySettingsEx, even though the + monitor DPI changes + (as of Windows 10 Creator's Update, at least) + */ if (mode->driverdata == display->desktop_mode.driverdata) { +#ifdef DEBUG_MODES + SDL_Log("WIN_SetDisplayMode: resetting to original resolution"); +#endif status = ChangeDisplaySettingsExW(displaydata->DeviceName, NULL, NULL, CDS_FULLSCREEN, NULL); } else { +#ifdef DEBUG_MODES + SDL_Log("WIN_SetDisplayMode: changing to %dx%d pixels", data->DeviceMode.dmPelsWidth, data->DeviceMode.dmPelsHeight); +#endif status = ChangeDisplaySettingsExW(displaydata->DeviceName, &data->DeviceMode, NULL, CDS_FULLSCREEN, NULL); } if (status != DISP_CHANGE_SUCCESSFUL) { @@ -462,6 +643,12 @@ WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) } return SDL_SetError("ChangeDisplaySettingsEx() failed: %s", reason); } + +#ifdef DEBUG_MODES + SDL_Log("WIN_SetDisplayMode: monitor after mode change:"); + WIN_LogMonitor(_this, displaydata->MonitorHandle); +#endif + EnumDisplaySettingsW(displaydata->DeviceName, ENUM_CURRENT_SETTINGS, &data->DeviceMode); WIN_UpdateDisplayMode(_this, displaydata->DeviceName, ENUM_CURRENT_SETTINGS, mode); return 0; diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index 0f2cdf4891d46..63fc60b4b4449 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -38,6 +38,8 @@ typedef struct extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); +extern void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h); +extern void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h); extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); diff --git a/src/video/windows/SDL_windowsopengl.c b/src/video/windows/SDL_windowsopengl.c index 0c99ca9a61e56..98e1f06e59976 100644 --- a/src/video/windows/SDL_windowsopengl.c +++ b/src/video/windows/SDL_windowsopengl.c @@ -822,6 +822,12 @@ WIN_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) return 0; } +void +WIN_GL_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h) +{ + WIN_GetDrawableSize(window, w, h); +} + int WIN_GL_SetSwapInterval(_THIS, int interval) { diff --git a/src/video/windows/SDL_windowsopengl.h b/src/video/windows/SDL_windowsopengl.h index 2dec0023a425b..36779aa4a3a9d 100644 --- a/src/video/windows/SDL_windowsopengl.h +++ b/src/video/windows/SDL_windowsopengl.h @@ -71,6 +71,7 @@ extern int WIN_GL_SetupWindow(_THIS, SDL_Window * window); extern SDL_GLContext WIN_GL_CreateContext(_THIS, SDL_Window * window); extern int WIN_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); +extern void WIN_GL_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h); extern int WIN_GL_SetSwapInterval(_THIS, int interval); extern int WIN_GL_GetSwapInterval(_THIS); extern int WIN_GL_SwapWindow(_THIS, SDL_Window * window); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 39806930ba3b2..870b7a320af17 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -122,6 +122,15 @@ 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"); } else { SDL_ClearError(); } @@ -129,6 +138,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(); } @@ -190,6 +200,7 @@ WIN_CreateDevice(int devindex) device->GL_UnloadLibrary = WIN_GL_UnloadLibrary; device->GL_CreateContext = WIN_GL_CreateContext; device->GL_MakeCurrent = WIN_GL_MakeCurrent; + device->GL_GetDrawableSize = WIN_GL_GetDrawableSize; device->GL_SetSwapInterval = WIN_GL_SetSwapInterval; device->GL_GetSwapInterval = WIN_GL_GetSwapInterval; device->GL_SwapWindow = WIN_GL_SwapWindow; @@ -231,11 +242,62 @@ VideoBootStrap WINDOWS_bootstrap = { "windows", "SDL Windows video driver", WIN_CreateDevice }; +static void +WIN_SetDPIAware(_THIS) +{ + SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); + if (data->SetProcessDpiAwarenessContext) { + /* Windows 10 Anniversary Update+ */ + /* First, try the Windows 10 Creators Update version */ + BOOL result = data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + if (!result) { + result = data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + } + + /* + NOTE: The above calls will fail if the DPI awareness was already set outside of SDL. + It doesn't matter if they fail, we will just use whatever DPI awareness level was set externally. + */ + data->highdpi_enabled = SDL_TRUE; + } else if (data->SetProcessDpiAwareness) { + /* Windows 8.1-Windows 10 */ + HRESULT result = data->SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + + data->highdpi_enabled = SDL_TRUE; + } else if (data->SetProcessDPIAware) { + /* Vista-Windows 8.0 */ + BOOL success = data->SetProcessDPIAware(); + + data->highdpi_enabled = SDL_TRUE; + } + + /* NOTE: we won't set data->highdpi_enabled below Vista. + In theory we could still check LOGPIXELSX/Y on XP. */ +} + int WIN_VideoInit(_THIS) { SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + /* Set the process DPI awareness */ + if (SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_HIGHDPI, SDL_FALSE)) { + WIN_SetDPIAware(_this); + } + + /* Cache LOGPIXELSX/LOGPIXELSY. */ + { + HDC hdc = GetDC(NULL); + if (hdc) { + data->system_xdpi = GetDeviceCaps(hdc, LOGPIXELSX); + data->system_ydpi = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } else { + data->system_xdpi = 96; + data->system_ydpi = 96; + } + } + if (WIN_InitModes(_this) < 0) { return -1; } diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index daf68e6c2d641..cf165b4673f44 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,27 @@ 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); void* shcoreDLL; HRESULT (WINAPI *GetDpiForMonitor)( HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY ); - + HRESULT (WINAPI *SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness); + + SDL_bool highdpi_enabled; + int system_xdpi; + int system_ydpi; + SDL_bool ime_com_initialized; struct ITfThreadMgr *ime_threadmgr; SDL_bool ime_initialized; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d884b2b7c9a56..8ac815c1d43d2 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -24,6 +24,7 @@ #include "../../core/windows/SDL_windows.h" +#include "SDL_log.h" #include "../SDL_sysvideo.h" #include "../SDL_pixels_c.h" #include "../../events/SDL_keyboard_c.h" @@ -45,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"); @@ -112,29 +115,95 @@ GetWindowStyle(SDL_Window * window) return style; } +/* +in: client rect (in Windows coordinates) +out: window rect, including frame (in Windows coordinates) +*/ static void -WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) +WIN_AdjustWindowRectWithStyleAndRect(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height) { + const SDL_WindowData *data = (SDL_WindowData *)window->driverdata; RECT rect; rect.left = 0; rect.top = 0; - rect.right = (use_current ? window->w : window->windowed.w); - rect.bottom = (use_current ? window->h : window->windowed.h); + rect.right = *width; + rect.bottom = *height; /* 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 (data->videodata->highdpi_enabled && data->videodata->AdjustWindowRectExForDpi) { + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, data->scaling_xdpi); + } 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; + *x += rect.left; + *y += rect.top; *width = (rect.right - rect.left); *height = (rect.bottom - rect.top); } +/* +out: window rect, including frame (in Windows coordinates) +*/ static void +WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) +{ + int x_win, y_win; + int w_win, h_win; + + const int x_sdl = (use_current ? window->x : window->windowed.x); + const int y_sdl = (use_current ? window->y : window->windowed.y); + const int w_sdl = (use_current ? window->w : window->windowed.w); + const int h_sdl = (use_current ? window->h : window->windowed.h); + + x_win = x_sdl; + y_win = y_sdl; + w_win = w_sdl; + h_win = h_sdl; + WIN_ScreenRectFromSDL(&x_win, &y_win, &w_win, &h_win); + + /* NOTE: we don't use the width/height returned by WIN_ScreenRectFromSDL, + (which is making a guess of which monitor the rect is considered to be on) + but instead calculate width/height using WIN_ClientPointFromSDL + which is using the DPI values that Windows considers the window to have. + */ + w_win = w_sdl; + h_win = h_sdl; + WIN_ClientPointFromSDL(window, &w_win, &h_win); + + WIN_AdjustWindowRectWithStyleAndRect(window, style, menu, &x_win, &y_win, &w_win, &h_win); + + *x = x_win; + *y = y_win; + *width = w_win; + *height = h_win; +} + +/* +in: client rect (in Windows coordinates) +out: window rect, including frame (in Windows coordinates) +*/ +void +WIN_AdjustWindowRectWithRect(SDL_Window *window, int *x, int *y, int *width, int *height) +{ + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + HWND hwnd = data->hwnd; + DWORD style; + BOOL menu; + + style = GetWindowLong(hwnd, GWL_STYLE); + menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + WIN_AdjustWindowRectWithStyleAndRect(window, style, menu, x, y, width, height); +} + +/* +out: window rect, including frame (in Windows coordinates) +*/ +void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current) { SDL_WindowData *data = (SDL_WindowData *)window->driverdata; @@ -163,13 +232,56 @@ WIN_SetWindowPositionInternal(_THIS, SDL_Window * window, UINT flags) top = HWND_NOTOPMOST; } - WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_TRUE); +#ifdef HIGHDPI_DEBUG + SDL_Log("WIN_SetWindowPositionInternal: SDL window rect: (%d, %d) (%d x %d)", window->x, window->y, window->w, window->h); +#endif + + WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_TRUE); + +#ifdef HIGHDPI_DEBUG + SDL_Log("WIN_SetWindowPositionInternal: calling SetWindowPos (%d, %d) (%d x %d)", x, y, w, h); +#endif data->expected_resize = SDL_TRUE; SetWindowPos(hwnd, top, x, y, w, h, flags); data->expected_resize = SDL_FALSE; } +static void +WIN_GetDPIForHWND(const SDL_VideoData *videodata, HWND hwnd, int *xdpi, int *ydpi) +{ + *xdpi = 96; + *ydpi = 96; + + /* highdpi not requested? */ + if (!videodata->highdpi_enabled) + return; + + /* Window 10+ */ + if (videodata->GetDpiForWindow) { + *xdpi = videodata->GetDpiForWindow(hwnd); + *ydpi = *xdpi; + return; + } + + /* window 8.1+ */ + if (videodata->GetDpiForMonitor) { + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + if (monitor) { + UINT xdpi_uint, ydpi_uint; + if (S_OK == videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &xdpi_uint, &ydpi_uint)) { + *xdpi = xdpi_uint; + *ydpi = ydpi_uint; + } + } + return; + } + + /* Windows Vista-8.0 */ + *xdpi = videodata->system_xdpi; + *ydpi = videodata->system_ydpi; +} + static int SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool created) { @@ -192,6 +304,7 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre data->last_pointer_update = (LPARAM)-1; data->videodata = videodata; data->initializing = SDL_TRUE; + WIN_GetDPIForHWND(videodata, hwnd, &data->scaling_xdpi, &data->scaling_ydpi); window->driverdata = data; @@ -219,12 +332,17 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre } #endif + /* Move window to the correct monitor and size it */ + WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOACTIVATE); + /* Fill in the SDL window with the window data */ { RECT rect; if (GetClientRect(hwnd, &rect)) { int w = rect.right; int h = rect.bottom; + + WIN_ClientPointToSDL(window, &w, &h); if ((window->windowed.w && window->windowed.w != w) || (window->windowed.h && window->windowed.h != h)) { /* We tried to create a window larger than the desktop and Windows didn't allow it. Override! */ int x, y; @@ -238,12 +356,18 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre } } { + RECT rect; POINT point; point.x = 0; point.y = 0; - if (ClientToScreen(hwnd, &point)) { - window->x = point.x; - window->y = point.y; + if (ClientToScreen(hwnd, &point) && GetClientRect(hwnd, &rect)) { + int x = point.x; + int y = point.y; + int w = rect.right; + int h = rect.bottom; + WIN_ScreenRectToSDL(&x, &y, &w, &h); + window->x = x; + window->y = y; } } { @@ -304,8 +428,6 @@ WIN_CreateWindow(_THIS, SDL_Window * window) { HWND hwnd, parent = NULL; DWORD style = STYLE_BASIC; - int x, y; - int w, h; if (window->flags & SDL_WINDOW_SKIP_TASKBAR) { parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL); @@ -313,11 +435,15 @@ 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); - + /* For high-DPI support, it's easier / more robust** to create the window + with a width/height of 0, then in SetupWindowData we will check the + DPI and adjust the position and size to match window->x,y,w,h. + + **The reason is, we can't know window DPI for sure until after the + window is created. + */ hwnd = - CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL, + CreateWindow(SDL_Appname, TEXT(""), style, CW_USEDEFAULT, 0, 0, 0, parent, NULL, SDL_Instance, NULL); if (!hwnd) { return WIN_SetError("Couldn't create window"); @@ -681,14 +807,31 @@ WIN_RestoreWindow(_THIS, SDL_Window * window) void WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { + SDL_DisplayData *displaydata = (SDL_DisplayData *) display->driverdata; SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + SDL_VideoData *videodata = data->videodata; HWND hwnd = data->hwnd; - SDL_Rect bounds; + MONITORINFO minfo; DWORD style; HWND top; int x, y; int w, h; + /* BUG: windows don't receive a WM_DPICHANGED message after a ChangeDisplaySettingsEx, + so we must manually update the cached DPI (see WIN_SetDisplayMode). */ +#ifdef HIGHDPI_DEBUG + { + int xdpi, ydpi; + WIN_GetDPIForHWND(videodata, hwnd, &xdpi, &ydpi); + SDL_Log("WIN_SetWindowFullscreen: dpi: %d, stale cached dpi: %d", xdpi, data->scaling_xdpi); + } +#endif + WIN_GetDPIForHWND(videodata, hwnd, &data->scaling_xdpi, &data->scaling_ydpi); + + /* clear the window size, to cause us to send a SDL_WINDOWEVENT_RESIZED event in WM_WINDOWPOSCHANGED */ + data->window->w = 0; + data->window->h = 0; + if (SDL_ShouldAllowTopmost() && ((window->flags & (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS)) == (SDL_WINDOW_FULLSCREEN|SDL_WINDOW_INPUT_FOCUS) || window->flags & SDL_WINDOW_ALWAYS_ON_TOP)) { top = HWND_TOPMOST; } else { @@ -699,13 +842,20 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, style &= ~STYLE_MASK; style |= GetWindowStyle(window); - WIN_GetDisplayBounds(_this, display, &bounds); + /* Use GetMonitorInfo instead of WIN_GetDisplayBounds because we want the + monitor bounds in Windows coordinates (pixels) rather than SDL coordinates (points). */ + SDL_zero(minfo); + minfo.cbSize = sizeof(MONITORINFO); + if (!GetMonitorInfo(displaydata->MonitorHandle, &minfo)) { + SDL_SetError("GetMonitorInfo failed"); + return; + } if (fullscreen) { - x = bounds.x; - y = bounds.y; - w = bounds.w; - h = bounds.h; + x = minfo.rcMonitor.left; + y = minfo.rcMonitor.top; + w = minfo.rcMonitor.right - minfo.rcMonitor.left; + h = minfo.rcMonitor.bottom - minfo.rcMonitor.top; /* Unset the maximized flag. This fixes https://bugzilla.libsdl.org/show_bug.cgi?id=3215 @@ -1143,6 +1293,48 @@ WIN_SetWindowOpacity(_THIS, SDL_Window * window, float opacity) return 0; } +void +WIN_GetDrawableSize(const SDL_Window *window, int *w, int *h) +{ + const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata); + HWND hwnd = data->hwnd; + RECT rect; + + if (GetClientRect(hwnd, &rect)) { + *w = rect.right; + *h = rect.bottom; + } else { + *w = 0; + *h = 0; + } +} + +void +WIN_ClientPointToSDL(const SDL_Window *window, int *x, int *y) +{ + const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata); + const SDL_VideoData *videodata = data->videodata; + + if (!videodata->highdpi_enabled) + return; + + *x = MulDiv(*x, 96, data->scaling_xdpi); + *y = MulDiv(*y, 96, data->scaling_ydpi); +} + +void +WIN_ClientPointFromSDL(const SDL_Window *window, int *x, int *y) +{ + const SDL_WindowData *data = ((SDL_WindowData *)window->driverdata); + const SDL_VideoData *videodata = data->videodata; + + if (!videodata->highdpi_enabled) + return; + + *x = MulDiv(*x, data->scaling_xdpi, 96); + *y = MulDiv(*y, data->scaling_ydpi, 96); +} + void WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept) { diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index d1570fdb05d1c..c04782871d15e 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -58,8 +58,15 @@ typedef struct #if SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface; #endif + /* NOTE: differing xdpi and ydpi is legacy-only, but we still support it. + On Windows 8.1+ it shouldn't happen (GetDpiForMonitor docs promise xdpi and ydpi are equal) + and on Windows 10+ it won't (GetDpiForWindow has a single return value).*/ + int scaling_xdpi; + int scaling_ydpi; } SDL_WindowData; +extern void WIN_AdjustWindowRectWithRect(SDL_Window *window, int *x, int *y, int *width, int *height); +extern void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current); extern int WIN_CreateWindow(_THIS, SDL_Window * window); extern int WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data); extern void WIN_SetWindowTitle(_THIS, SDL_Window * window); @@ -90,6 +97,9 @@ extern SDL_bool WIN_GetWindowWMInfo(_THIS, SDL_Window * window, extern void WIN_OnWindowEnter(_THIS, SDL_Window * window); extern void WIN_UpdateClipCursor(SDL_Window *window); extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); +extern void WIN_GetDrawableSize(const SDL_Window *window, int *w, int *h); +extern void WIN_ClientPointToSDL(const SDL_Window *window, int *w, int *h); +extern void WIN_ClientPointFromSDL(const SDL_Window *window, int *w, int *h); extern void WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept); extern int WIN_FlashWindow(_THIS, SDL_Window * window, SDL_FlashOperation operation); From a7937578acb91e7107c30321c85737b427cb691b Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 29 May 2021 14:05:20 -0600 Subject: [PATCH 02/42] add IsValidDpiAwarenessContext function pointer --- src/video/windows/SDL_windowsvideo.c | 1 + src/video/windows/SDL_windowsvideo.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 870b7a320af17..84f7c7bf5434e 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -131,6 +131,7 @@ WIN_CreateDevice(int devindex) 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(); } diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index cf165b4673f44..150b3714b9781 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -176,6 +176,7 @@ typedef struct SDL_VideoData 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, From 7b2b762d0e8a841f55dc7c7f40d63e6e6e5a8991 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 7 Jun 2021 01:24:45 -0600 Subject: [PATCH 03/42] print display usable bounds --- src/test/SDL_test_common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 3779e5a55597d..8519fa492105a 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -2348,6 +2348,13 @@ SDLTest_CommonDrawWindowInfo(SDL_Renderer * renderer, SDL_Window * window, int * textY += lineHeight; } + if (0 == SDL_GetDisplayUsableBounds(windowDisplayIndex, &rect)) { + SDL_snprintf(text, sizeof(text), "SDL_GetDisplayUsableBounds: %d,%d, %dx%d", + rect.x, rect.y, rect.w, rect.h); + SDLTest_DrawString(renderer, 0, textY, text); + textY += lineHeight; + } + if (0 == SDL_GetCurrentDisplayMode(windowDisplayIndex, &mode)) { SDL_snprintf(text, sizeof(text), "SDL_GetCurrentDisplayMode: %dx%d@%d", mode.w, mode.h, mode.refresh_rate); From c64abd505ce724b45c7ce2dba0f39d076f70040c Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 7 Jun 2021 18:18:46 -0600 Subject: [PATCH 04/42] Revert "print display usable bounds" This reverts commit 551c3df4ecd44a616fdde4cebf8b52699bd04ab2. --- src/test/SDL_test_common.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 8519fa492105a..3779e5a55597d 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -2348,13 +2348,6 @@ SDLTest_CommonDrawWindowInfo(SDL_Renderer * renderer, SDL_Window * window, int * textY += lineHeight; } - if (0 == SDL_GetDisplayUsableBounds(windowDisplayIndex, &rect)) { - SDL_snprintf(text, sizeof(text), "SDL_GetDisplayUsableBounds: %d,%d, %dx%d", - rect.x, rect.y, rect.w, rect.h); - SDLTest_DrawString(renderer, 0, textY, text); - textY += lineHeight; - } - if (0 == SDL_GetCurrentDisplayMode(windowDisplayIndex, &mode)) { SDL_snprintf(text, sizeof(text), "SDL_GetCurrentDisplayMode: %dx%d@%d", mode.w, mode.h, mode.refresh_rate); From e4cd6e152e9264f25c26c9900fe86b78114fcb6a Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 14 Jun 2021 00:21:00 -0600 Subject: [PATCH 05/42] add missing include --- src/render/direct3d11/SDL_render_d3d11.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 441ce56517b3a..357ff712d26eb 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -27,6 +27,7 @@ #define COBJMACROS #include "../../core/windows/SDL_windows.h" +#include "../../video/windows/SDL_windowswindow.h" #include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_syswm.h" From e47e5410515e6a4d444af1ff256aed8b2ebdd11f Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 19:18:04 -0600 Subject: [PATCH 06/42] highdpi: remove system_xdpi/system_ydpi removes untested code and simplifies diff. First PR will be PMV2 only. --- src/video/windows/SDL_windowsmodes.c | 23 +++++++++++++++-------- src/video/windows/SDL_windowsvideo.c | 13 ------------- src/video/windows/SDL_windowsvideo.h | 2 -- src/video/windows/SDL_windowswindow.c | 3 +-- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index b2a4d34b40b8d..c47ac23e70032 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -330,21 +330,29 @@ WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * h } } else { // Window 8.0 and below: same DPI for all monitors. - int hpoints, vpoints, hpix, vpix; + HDC hdc; + int hdpi_int, vdpi_int, hpoints, vpoints, hpix, vpix; float hinches, vinches; - /* NOTE: all of this is just to compute the diagonal DPI. */ + hdc = GetDC(NULL); + if (hdc == NULL) { + return SDL_SetError("GetDC failed"); + } + hdpi_int = GetDeviceCaps(hdc, LOGPIXELSX); + vdpi_int = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + hpoints = GetSystemMetrics(SM_CXVIRTUALSCREEN); vpoints = GetSystemMetrics(SM_CYVIRTUALSCREEN); - hpix = MulDiv(hpoints, videodata->system_xdpi, 96); - vpix = MulDiv(vpoints, videodata->system_ydpi, 96); + hpix = MulDiv(hpoints, hdpi_int, 96); + vpix = MulDiv(vpoints, vdpi_int, 96); hinches = (float)hpoints / 96.0f; vinches = (float)vpoints / 96.0f; - hdpi = (float)videodata->system_xdpi; - vdpi = (float)videodata->system_ydpi; + hdpi = (float)hdpi_int; + vdpi = (float)vdpi_int; ddpi = SDL_ComputeDiagonalDPI(hpix, vpix, hinches, vinches); } @@ -418,8 +426,7 @@ WIN_GetMonitorDPIAndRects(const SDL_VideoData *videodata, HMONITOR monitor, UINT /* Check for Windows < 8.1*/ if (!videodata->GetDpiForMonitor) { - *xdpi = videodata->system_xdpi; - *ydpi = videodata->system_ydpi; + return SDL_SetError("GetDpiForMonitor failed"); } else { result = videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, xdpi, &unused); *ydpi = *xdpi; diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 84f7c7bf5434e..8a3fc0bacc44b 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -286,19 +286,6 @@ WIN_VideoInit(_THIS) WIN_SetDPIAware(_this); } - /* Cache LOGPIXELSX/LOGPIXELSY. */ - { - HDC hdc = GetDC(NULL); - if (hdc) { - data->system_xdpi = GetDeviceCaps(hdc, LOGPIXELSX); - data->system_ydpi = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } else { - data->system_xdpi = 96; - data->system_ydpi = 96; - } - } - if (WIN_InitModes(_this) < 0) { return -1; } diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index 150b3714b9781..ab3e007db9d86 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -186,8 +186,6 @@ typedef struct SDL_VideoData HRESULT (WINAPI *SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS dpiAwareness); SDL_bool highdpi_enabled; - int system_xdpi; - int system_ydpi; SDL_bool ime_com_initialized; struct ITfThreadMgr *ime_threadmgr; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 8ac815c1d43d2..603dd47c77317 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -278,8 +278,7 @@ WIN_GetDPIForHWND(const SDL_VideoData *videodata, HWND hwnd, int *xdpi, int *ydp } /* Windows Vista-8.0 */ - *xdpi = videodata->system_xdpi; - *ydpi = videodata->system_ydpi; + /* no SDL2 highdpi support */ } static int From 2f007a677d15653249e54717094ded77d0c5b87c Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 19:19:01 -0600 Subject: [PATCH 07/42] hidpi: remove EnableNonClientDpiScaling call first version of PR will be PMV2 only. --- src/video/windows/SDL_windowsevents.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index e25c5d743abad..9327588d93de1 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -657,24 +657,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) data = (SDL_WindowData *) GetProp(hwnd, TEXT("SDL_WindowData")); } if (!data) { - /* Request that windows scale the non-client area when moving between monitors. - This is only for Windows 10 Anniversary Update. - In the Windows 10 Creators Update, this is the default behaviour with a per-monitor V2 context. - */ - switch (msg) { - case WM_NCCREATE: - { - SDL_VideoDevice *device = SDL_GetVideoDevice(); - if (device && device->driverdata) { - SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, device->driverdata); - if (data->highdpi_enabled && data->EnableNonClientDpiScaling) { - data->EnableNonClientDpiScaling(hwnd); - } - } - } - break; - } - return CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam); } From 4eb585a2870e01b7d652173b401c5370d1be0431 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 19:58:59 -0600 Subject: [PATCH 08/42] highdpi: remove support for separate xdpi/ydpi --- src/video/windows/SDL_windowsevents.c | 5 ++- src/video/windows/SDL_windowswindow.c | 49 ++++++++------------------- src/video/windows/SDL_windowswindow.h | 6 +--- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 9327588d93de1..f2cb6d1405490 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1490,13 +1490,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) #ifdef HIGHDPI_DEBUG SDL_Log("WM_DPICHANGED: %d to %d\tsuggested rect: (%d, %d), (%dx%d)\n", - data->scaling_xdpi, newDPI, + data->scaling_dpi, newDPI, suggestedRect->left, suggestedRect->top, suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top); #endif /* update the cached DPI value for this window */ - data->scaling_xdpi = newDPI; - data->scaling_ydpi = newDPI; + data->scaling_dpi = newDPI; if (data->videodata->AreDpiAwarenessContextsEqual && data->videodata->GetThreadDpiAwarenessContext diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 603dd47c77317..8f9068d8e6e39 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -135,7 +135,7 @@ WIN_AdjustWindowRectWithStyleAndRect(SDL_Window *window, DWORD style, BOOL menu, */ if (!(window->flags & SDL_WINDOW_BORDERLESS)) if (data->videodata->highdpi_enabled && data->videodata->AdjustWindowRectExForDpi) { - data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, data->scaling_xdpi); + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, data->scaling_dpi); } else { AdjustWindowRectEx(&rect, style, menu, 0); } @@ -247,38 +247,21 @@ WIN_SetWindowPositionInternal(_THIS, SDL_Window * window, UINT flags) data->expected_resize = SDL_FALSE; } -static void -WIN_GetDPIForHWND(const SDL_VideoData *videodata, HWND hwnd, int *xdpi, int *ydpi) +static int +WIN_GetDPIForHWND(const SDL_VideoData *videodata, HWND hwnd) { - *xdpi = 96; - *ydpi = 96; - /* highdpi not requested? */ if (!videodata->highdpi_enabled) - return; + return 96; /* Window 10+ */ if (videodata->GetDpiForWindow) { - *xdpi = videodata->GetDpiForWindow(hwnd); - *ydpi = *xdpi; - return; - } - - /* window 8.1+ */ - if (videodata->GetDpiForMonitor) { - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - if (monitor) { - UINT xdpi_uint, ydpi_uint; - if (S_OK == videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &xdpi_uint, &ydpi_uint)) { - *xdpi = xdpi_uint; - *ydpi = ydpi_uint; - } - } - return; + return videodata->GetDpiForWindow(hwnd); } - /* Windows Vista-8.0 */ + /* Windows Vista-8.1 */ /* no SDL2 highdpi support */ + return 96; } static int @@ -303,7 +286,7 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre data->last_pointer_update = (LPARAM)-1; data->videodata = videodata; data->initializing = SDL_TRUE; - WIN_GetDPIForHWND(videodata, hwnd, &data->scaling_xdpi, &data->scaling_ydpi); + data->scaling_dpi = WIN_GetDPIForHWND(videodata, hwnd); window->driverdata = data; @@ -819,13 +802,9 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, /* BUG: windows don't receive a WM_DPICHANGED message after a ChangeDisplaySettingsEx, so we must manually update the cached DPI (see WIN_SetDisplayMode). */ #ifdef HIGHDPI_DEBUG - { - int xdpi, ydpi; - WIN_GetDPIForHWND(videodata, hwnd, &xdpi, &ydpi); - SDL_Log("WIN_SetWindowFullscreen: dpi: %d, stale cached dpi: %d", xdpi, data->scaling_xdpi); - } + SDL_Log("WIN_SetWindowFullscreen: dpi: %d, stale cached dpi: %d", WIN_GetDPIForHWND(videodata, hwnd), data->scaling_dpi); #endif - WIN_GetDPIForHWND(videodata, hwnd, &data->scaling_xdpi, &data->scaling_ydpi); + data->scaling_dpi = WIN_GetDPIForHWND(videodata, hwnd); /* clear the window size, to cause us to send a SDL_WINDOWEVENT_RESIZED event in WM_WINDOWPOSCHANGED */ data->window->w = 0; @@ -1317,8 +1296,8 @@ WIN_ClientPointToSDL(const SDL_Window *window, int *x, int *y) if (!videodata->highdpi_enabled) return; - *x = MulDiv(*x, 96, data->scaling_xdpi); - *y = MulDiv(*y, 96, data->scaling_ydpi); + *x = MulDiv(*x, 96, data->scaling_dpi); + *y = MulDiv(*y, 96, data->scaling_dpi); } void @@ -1330,8 +1309,8 @@ WIN_ClientPointFromSDL(const SDL_Window *window, int *x, int *y) if (!videodata->highdpi_enabled) return; - *x = MulDiv(*x, data->scaling_xdpi, 96); - *y = MulDiv(*y, data->scaling_ydpi, 96); + *x = MulDiv(*x, data->scaling_dpi, 96); + *y = MulDiv(*y, data->scaling_dpi, 96); } void diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index c04782871d15e..a341d9c02f53d 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -58,11 +58,7 @@ typedef struct #if SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface; #endif - /* NOTE: differing xdpi and ydpi is legacy-only, but we still support it. - On Windows 8.1+ it shouldn't happen (GetDpiForMonitor docs promise xdpi and ydpi are equal) - and on Windows 10+ it won't (GetDpiForWindow has a single return value).*/ - int scaling_xdpi; - int scaling_ydpi; + int scaling_dpi; } SDL_WindowData; extern void WIN_AdjustWindowRectWithRect(SDL_Window *window, int *x, int *y, int *width, int *height); From 15d0a24b25ba3e0ff0c94eee77f587f7024dbda8 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 20:11:08 -0600 Subject: [PATCH 09/42] highdpi: make WIN_GetGlobalMouseState dpi aware --- src/video/windows/SDL_windowsmodes.c | 12 ++++++++---- src/video/windows/SDL_windowsmouse.c | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index c47ac23e70032..b6f1e1bb5d725 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -525,13 +525,17 @@ void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h) inputrect.left = *x; inputrect.top = *y; - inputrect.right = *x + *w; - inputrect.bottom = *y + *h; + inputrect.right = *x + (w ? *w : 0); + inputrect.bottom = *y + (h ? *h : 0); monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { - *w = MulDiv(*w, 96, xdpi); - *h = MulDiv(*h, 96, ydpi); + if (w) { + *w = MulDiv(*w, 96, xdpi); + } + if (h) { + *h = MulDiv(*h, 96, ydpi); + } *x = monitorrect_win.left + MulDiv(*x - monitorrect_win.left, 96, xdpi); *y = monitorrect_win.top + MulDiv(*y - monitorrect_win.top, 96, ydpi); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 19977e214d73b..852b27476ccee 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -331,6 +331,7 @@ WIN_GetGlobalMouseState(int *x, int *y) GetCursorPos(&pt); *x = (int) pt.x; *y = (int) pt.y; + WIN_ScreenRectToSDL(x, y, NULL, NULL); retval |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0; retval |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0; From 592a6c7de13f51c302c89658af5034d3600f36c2 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 20:17:32 -0600 Subject: [PATCH 10/42] highdpi: support WIN_WarpMouseGlobal --- src/video/windows/SDL_windowsmouse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 852b27476ccee..6c09d569115c5 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -288,6 +288,7 @@ WIN_WarpMouseGlobal(int x, int y) { POINT pt; + WIN_ScreenRectFromSDL(&x, &y, NULL, NULL); pt.x = x; pt.y = y; SetCursorPos(pt.x, pt.y); From 2632ff6712e99eef95cca93c12491ff40b428913 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 21:05:58 -0600 Subject: [PATCH 11/42] highdpi: revert WIN_GetDisplayBounds/WIN_GetDisplayUsableBounds --- src/video/windows/SDL_windowsmodes.c | 55 +++++++++++++++------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index b6f1e1bb5d725..01b3367bab696 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -310,6 +310,29 @@ WIN_InitModes(_THIS) return 0; } +int +WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) +{ + const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; + MONITORINFO minfo; + BOOL rc; + + SDL_zero(minfo); + minfo.cbSize = sizeof(MONITORINFO); + rc = GetMonitorInfo(data->MonitorHandle, &minfo); + + if (!rc) { + return SDL_SetError("Couldn't find monitor data"); + } + + rect->x = minfo.rcMonitor.left; + rect->y = minfo.rcMonitor.top; + rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left; + rect->h = minfo.rcMonitor.bottom - minfo.rcMonitor.top; + + return 0; +} + int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * hdpi_out, float * vdpi_out) { @@ -369,8 +392,8 @@ WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi_out, float * h return ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI"); } -static int -WIN_GetDisplayBoundsInternal(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect, SDL_bool usable) +int +WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) { const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->driverdata; @@ -388,34 +411,14 @@ WIN_GetDisplayBoundsInternal(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect, return SDL_SetError("Couldn't find monitor data"); } - rect_win = usable ? &minfo.rcWork : &minfo.rcMonitor; - - x = rect_win->left; - y = rect_win->top; - w = rect_win->right - rect_win->left; - h = rect_win->bottom - rect_win->top; - WIN_ScreenRectToSDL(&x, &y, &w, &h); - - rect->x = x; - rect->y = y; - rect->w = w; - rect->h = h; + rect->x = minfo.rcWork.left; + rect->y = minfo.rcWork.top; + rect->w = minfo.rcWork.right - minfo.rcWork.left; + rect->h = minfo.rcWork.bottom - minfo.rcWork.top; return 0; } -int -WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) -{ - return WIN_GetDisplayBoundsInternal(_this, display, rect, SDL_FALSE); -} - -int -WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) -{ - return WIN_GetDisplayBoundsInternal(_this, display, rect, SDL_TRUE); -} - static int WIN_GetMonitorDPIAndRects(const SDL_VideoData *videodata, HMONITOR monitor, UINT *xdpi, UINT *ydpi, RECT *monitorrect_sdl, RECT *monitorrect_win) { From d759e21e3833db447ca8d01bea6d6673f68e9a2a Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 21:07:11 -0600 Subject: [PATCH 12/42] highdpi: fix up last commit --- src/video/windows/SDL_windowsmodes.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 01b3367bab696..4ee14e1c78003 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -396,12 +396,8 @@ int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) { const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; - const SDL_VideoData *vid_data = (const SDL_VideoData *)_this->driverdata; MONITORINFO minfo; - const RECT *rect_win; BOOL rc; - int x, y; - int w, h; SDL_zero(minfo); minfo.cbSize = sizeof(MONITORINFO); From 3aaedf0936b51d86c82402991c0b30bf570e11b6 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 21 Jun 2021 21:44:16 -0600 Subject: [PATCH 13/42] highdpi: fix WIN_GetDisplayBounds/WIN_GetDisplayUsableBounds --- src/video/windows/SDL_windowsmodes.c | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 4ee14e1c78003..93620b74095b2 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -310,10 +310,40 @@ WIN_InitModes(_THIS) return 0; } +static void +WIN_MonitorInfoToSDL(const SDL_VideoData *videodata, HMONITOR monitor, MONITORINFO *info) +{ + UINT xdpi, ydpi; + + if (!videodata->highdpi_enabled) { + return; + } + + /* Check for Windows < 8.1*/ + if (!videodata->GetDpiForMonitor) { + return; + } + if (videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK) { + /* Shouldn't happen? */ + return; + } + + /* Convert monitor size to points, leaving the monitor position in pixels */ + info->rcMonitor.right = info->rcMonitor.left + MulDiv(info->rcMonitor.right - info->rcMonitor.left, 96, xdpi); + info->rcMonitor.bottom = info->rcMonitor.top + MulDiv(info->rcMonitor.bottom - info->rcMonitor.top, 96, ydpi); + + /* Convert monitor work rect to points */ + info->rcWork.left = info->rcMonitor.left + MulDiv(info->rcWork.left - info->rcMonitor.left, 96, xdpi); + info->rcWork.right = info->rcMonitor.left + MulDiv(info->rcWork.right - info->rcMonitor.left, 96, xdpi); + info->rcWork.top = info->rcMonitor.top + MulDiv(info->rcWork.top - info->rcMonitor.top, 96, ydpi); + info->rcWork.bottom = info->rcMonitor.top + MulDiv(info->rcWork.bottom - info->rcMonitor.top, 96, ydpi); +} + int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) { const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; + const SDL_VideoData *videodata = (SDL_VideoData *)display->device->driverdata; MONITORINFO minfo; BOOL rc; @@ -325,6 +355,7 @@ WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return SDL_SetError("Couldn't find monitor data"); } + WIN_MonitorInfoToSDL(videodata, data->MonitorHandle, &minfo); rect->x = minfo.rcMonitor.left; rect->y = minfo.rcMonitor.top; rect->w = minfo.rcMonitor.right - minfo.rcMonitor.left; @@ -396,6 +427,7 @@ int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) { const SDL_DisplayData *data = (const SDL_DisplayData *)display->driverdata; + const SDL_VideoData *videodata = (SDL_VideoData *)display->device->driverdata; MONITORINFO minfo; BOOL rc; @@ -407,6 +439,7 @@ WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return SDL_SetError("Couldn't find monitor data"); } + WIN_MonitorInfoToSDL(videodata, data->MonitorHandle, &minfo); rect->x = minfo.rcWork.left; rect->y = minfo.rcWork.top; rect->w = minfo.rcWork.right - minfo.rcWork.left; From b8642ee9f82431433e0d16cf2f2bc494e4655bcb Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 22 Jun 2021 01:02:48 -0600 Subject: [PATCH 14/42] highdpi: clean up WM_GETMINMAXINFO --- src/video/windows/SDL_windowsevents.c | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index f2cb6d1405490..9a72b69052620 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1045,7 +1045,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) int w, h; int min_w, min_h; int max_w, max_h; - int unused_x, unused_y; BOOL constrain_max_size; if (SDL_IsShapedWindow(data->window)) { @@ -1085,10 +1084,29 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) constrain_max_size = FALSE; } - /* Expand w/h to include the frame. */ - unused_x = 0; - unused_y = 0; - WIN_AdjustWindowRectWithRect(data->window, &unused_x, &unused_y, &w, &h); + if (!(SDL_GetWindowFlags(data->window) & SDL_WINDOW_BORDERLESS)) { + LONG style = GetWindowLong(hwnd, GWL_STYLE); + /* DJM - according to the docs for GetMenu(), the + return value is undefined if hwnd is a child window. + Apparently it's too difficult for MS to check + inside their function, so I have to do it here. + */ + BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + size.top = 0; + size.left = 0; + size.bottom = h; + size.right = w; + + /* TODO: Possibly we should use AdjustWindowRectExForDpi even if + SDL DPI awareness wasn't requested */ + if (data->videodata->highdpi_enabled && data->videodata->AdjustWindowRectExForDpi) { + data->videodata->AdjustWindowRectExForDpi(&size, style, menu, 0, data->scaling_dpi); + } else { + AdjustWindowRectEx(&size, style, menu, 0); + } + w = size.right - size.left; + h = size.bottom - size.top; + } /* Fix our size to the current size */ info = (MINMAXINFO *) lParam; From 72ad1bc4fc58d9ec6be6a2cd56069fcc3e9be0cd Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 22 Jun 2021 02:04:47 -0600 Subject: [PATCH 15/42] highdpi: replace WIN_ScreenRectToSDL with WIN_ScreenPointToSDL --- src/video/windows/SDL_windowsevents.c | 6 ++---- src/video/windows/SDL_windowsmodes.c | 15 ++++----------- src/video/windows/SDL_windowsmouse.c | 2 +- src/video/windows/SDL_windowswindow.c | 4 +--- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 9a72b69052620..b115def1a4131 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1167,15 +1167,13 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) x = rect.left; y = rect.top; - w = rect.right - rect.left; - h = rect.bottom - rect.top; - WIN_ScreenRectToSDL(&x, &y, &w, &h); + WIN_ScreenPointToSDL(&x, &y); SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y); /* NOTE: important to convert w/h from SDL (points) to Windows (pixels) using WIN_ClientPointToSDL, which uses the window's actual - DPI value, rather than WIN_ScreenRectToSDL which guesses. */ + DPI value. */ w = rect.right - rect.left; h = rect.bottom - rect.top; WIN_ClientPointToSDL(data->window, &w, &h); diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 93620b74095b2..78dbfc06d18ba 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -538,8 +538,8 @@ void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h) } } -/* Converts a Windows screen rect to an SDL one. */ -void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h) +/* Converts a Windows screen point to an SDL one. */ +void WIN_ScreenPointToSDL(int *x, int *y) { const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); const SDL_VideoData *videodata; @@ -557,18 +557,11 @@ void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h) inputrect.left = *x; inputrect.top = *y; - inputrect.right = *x + (w ? *w : 0); - inputrect.bottom = *y + (h ? *h : 0); + inputrect.right = *x; + inputrect.bottom = *y; monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { - if (w) { - *w = MulDiv(*w, 96, xdpi); - } - if (h) { - *h = MulDiv(*h, 96, ydpi); - } - *x = monitorrect_win.left + MulDiv(*x - monitorrect_win.left, 96, xdpi); *y = monitorrect_win.top + MulDiv(*y - monitorrect_win.top, 96, ydpi); } diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 6c09d569115c5..660577ea1501c 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -332,7 +332,7 @@ WIN_GetGlobalMouseState(int *x, int *y) GetCursorPos(&pt); *x = (int) pt.x; *y = (int) pt.y; - WIN_ScreenRectToSDL(x, y, NULL, NULL); + WIN_ScreenPointToSDL(x, y); retval |= GetAsyncKeyState(!swapButtons ? VK_LBUTTON : VK_RBUTTON) & 0x8000 ? SDL_BUTTON_LMASK : 0; retval |= GetAsyncKeyState(!swapButtons ? VK_RBUTTON : VK_LBUTTON) & 0x8000 ? SDL_BUTTON_RMASK : 0; diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 8f9068d8e6e39..52427cff33e79 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -345,9 +345,7 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre if (ClientToScreen(hwnd, &point) && GetClientRect(hwnd, &rect)) { int x = point.x; int y = point.y; - int w = rect.right; - int h = rect.bottom; - WIN_ScreenRectToSDL(&x, &y, &w, &h); + WIN_ScreenPointToSDL(&x, &y); window->x = x; window->y = y; } From 00cc462f49899006f5f7d460d9af0aba87c3a4d4 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 22 Jun 2021 22:50:12 -0600 Subject: [PATCH 16/42] highdpi: remove scaling_dpi = newDPI; - if (data->videodata->AreDpiAwarenessContextsEqual - && data->videodata->GetThreadDpiAwarenessContext - && data->videodata->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, data->videodata->GetThreadDpiAwarenessContext())) - { - /* - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 means that - WM_GETDPISCALEDSIZE will have been called, so we can use suggestedRect. - */ - w = suggestedRect->right - suggestedRect->left; - h = suggestedRect->bottom - suggestedRect->top; - } else { - /* - The OS does not support WM_GETDPISCALEDSIZE, so we can't use suggestedRect. - - (suggestedRect is calculated by default to preserves the apparent size of the window rect, - whereas we want to preserve the apparent size of the client rect.) - */ - WIN_AdjustWindowRect(data->window, &x, &y, &w, &h, SDL_TRUE); - } + /* + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 means that + WM_GETDPISCALEDSIZE will have been called, so we can use suggestedRect. + */ + w = suggestedRect->right - suggestedRect->left; + h = suggestedRect->bottom - suggestedRect->top; #ifdef HIGHDPI_DEBUG SDL_Log("WM_DPICHANGED: current SDL window size: (%dx%d)\tcalling SetWindowPos: (%d, %d), (%dx%d)\n", From 66a67a57ead0f5fca71fed70ea4cba49a2a849c7 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 22 Jun 2021 23:15:14 -0600 Subject: [PATCH 17/42] highdpi: avoid WIN_AdjustWindowRect in WM_GETDPISCALEDSIZE --- src/video/windows/SDL_windowsevents.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 7975494a9e556..b05b8e366b00e 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1459,11 +1459,18 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) SDL_Log("WM_GETDPISCALEDSIZE: current DPI: %d potential DPI: %d. input size: (%dx%d)", currentDPI, potentialDPI, sizeInOut->cx, sizeInOut->cy); #endif - WIN_AdjustWindowRect(data->window, &x, &y, &w, &h, SDL_TRUE); + /* get the frame size in pixels at currentDPI */ + { + DWORD style = GetWindowLong(hwnd, GWL_STYLE); + BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + RECT rect = {0}; - /* get the frame size */ - frame_w = w - MulDiv(data->window->w, currentDPI, 96); - frame_h = h - MulDiv(data->window->h, currentDPI, 96); + if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) + data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, currentDPI); + + 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; From 0b91d8ec492560ef323b2536bc3c2476aeab5a0d Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Tue, 22 Jun 2021 23:17:49 -0600 Subject: [PATCH 18/42] highdpi: warning fixes --- src/video/windows/SDL_windowsevents.c | 3 +-- src/video/windows/SDL_windowsmodes.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index b05b8e366b00e..6a528f2d81c2f 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1451,7 +1451,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) const int currentDPI = (int)data->videodata->GetDpiForWindow(hwnd); SIZE *sizeInOut = (SIZE *)lParam; - int x, y, w, h; int frame_w, frame_h; int query_client_w_win, query_client_h_win; @@ -1509,7 +1508,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) if (data->videodata->highdpi_enabled) { const int newDPI = HIWORD(wParam); RECT* const suggestedRect = (RECT*)lParam; - int x, y, w, h; + int w, h; #ifdef HIGHDPI_DEBUG SDL_Log("WM_DPICHANGED: %d to %d\tsuggested rect: (%d, %d), (%dx%d)\n", diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index 63fc60b4b4449..790b138d83c27 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -39,7 +39,7 @@ extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h); -extern void WIN_ScreenRectToSDL(int *x, int *y, int *w, int *h); +extern void WIN_ScreenPointToSDL(int *x, int *y); extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); From 2f569ce0876004a76b85094a8e8871a596d9e438 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 23 Jun 2021 00:44:17 -0600 Subject: [PATCH 19/42] highdpi: revert some changes in windowswindow.c --- src/video/windows/SDL_windowsmodes.c | 10 ++- src/video/windows/SDL_windowsmouse.c | 2 +- src/video/windows/SDL_windowswindow.c | 108 ++++++++------------------ src/video/windows/SDL_windowswindow.h | 2 - 4 files changed, 42 insertions(+), 80 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 78dbfc06d18ba..eebf86f5a63d0 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -490,7 +490,7 @@ WIN_GetMonitorDPIAndRects(const SDL_VideoData *videodata, HMONITOR monitor, UINT } /* Convert an SDL to a Windows screen rect. */ -void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h) +void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h, int *dpi) { const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); const SDL_VideoData *videodata; @@ -535,6 +535,14 @@ void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h) *x = monitorrect_win.right - 1; if (*y >= monitorrect_win.bottom) *y = monitorrect_win.bottom - 1; + + if (dpi) { + *dpi = xdpi; + } + } else { + if (dpi) { + *dpi = 96; + } } } diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 660577ea1501c..6c7f1549ab79c 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -288,7 +288,7 @@ WIN_WarpMouseGlobal(int x, int y) { POINT pt; - WIN_ScreenRectFromSDL(&x, &y, NULL, NULL); + WIN_ScreenRectFromSDL(&x, &y, NULL, NULL, NULL); pt.x = x; pt.y = y; SetCursorPos(pt.x, pt.y); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 52427cff33e79..143beea78e908 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -116,32 +116,47 @@ GetWindowStyle(SDL_Window * window) } /* -in: client rect (in Windows coordinates) out: window rect, including frame (in Windows coordinates) + +Can be called before HWND is created. */ static void -WIN_AdjustWindowRectWithStyleAndRect(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height) +WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) { - const SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); + const SDL_VideoData *videodata; RECT rect; + int dpi; + + if (!videodevice || !videodevice->driverdata) + return; + + videodata = (SDL_VideoData *)videodevice->driverdata; - rect.left = 0; - rect.top = 0; - rect.right = *width; - rect.bottom = *height; + *x = (use_current ? window->x : window->windowed.x); + *y = (use_current ? window->y : window->windowed.y); + *width = (use_current ? window->w : window->windowed.w); + *height = (use_current ? window->h : window->windowed.h); + + WIN_ScreenRectFromSDL(x, y, width, height, &dpi); + + rect.left = *x; + rect.top = *y; + rect.right = *x + *width; + rect.bottom = *y + *height; /* 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)) - if (data->videodata->highdpi_enabled && data->videodata->AdjustWindowRectExForDpi) { - data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, data->scaling_dpi); + if (videodata->highdpi_enabled && videodata->AdjustWindowRectExForDpi) { + videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi); } else { AdjustWindowRectEx(&rect, style, menu, 0); } - *x += rect.left; - *y += rect.top; + *x = rect.left; + *y = rect.top; *width = (rect.right - rect.left); *height = (rect.bottom - rect.top); } @@ -150,60 +165,6 @@ WIN_AdjustWindowRectWithStyleAndRect(SDL_Window *window, DWORD style, BOOL menu, out: window rect, including frame (in Windows coordinates) */ static void -WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) -{ - int x_win, y_win; - int w_win, h_win; - - const int x_sdl = (use_current ? window->x : window->windowed.x); - const int y_sdl = (use_current ? window->y : window->windowed.y); - const int w_sdl = (use_current ? window->w : window->windowed.w); - const int h_sdl = (use_current ? window->h : window->windowed.h); - - x_win = x_sdl; - y_win = y_sdl; - w_win = w_sdl; - h_win = h_sdl; - WIN_ScreenRectFromSDL(&x_win, &y_win, &w_win, &h_win); - - /* NOTE: we don't use the width/height returned by WIN_ScreenRectFromSDL, - (which is making a guess of which monitor the rect is considered to be on) - but instead calculate width/height using WIN_ClientPointFromSDL - which is using the DPI values that Windows considers the window to have. - */ - w_win = w_sdl; - h_win = h_sdl; - WIN_ClientPointFromSDL(window, &w_win, &h_win); - - WIN_AdjustWindowRectWithStyleAndRect(window, style, menu, &x_win, &y_win, &w_win, &h_win); - - *x = x_win; - *y = y_win; - *width = w_win; - *height = h_win; -} - -/* -in: client rect (in Windows coordinates) -out: window rect, including frame (in Windows coordinates) -*/ -void -WIN_AdjustWindowRectWithRect(SDL_Window *window, int *x, int *y, int *width, int *height) -{ - SDL_WindowData *data = (SDL_WindowData *)window->driverdata; - HWND hwnd = data->hwnd; - DWORD style; - BOOL menu; - - style = GetWindowLong(hwnd, GWL_STYLE); - menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - WIN_AdjustWindowRectWithStyleAndRect(window, style, menu, x, y, width, height); -} - -/* -out: window rect, including frame (in Windows coordinates) -*/ -void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current) { SDL_WindowData *data = (SDL_WindowData *)window->driverdata; @@ -314,9 +275,6 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre } #endif - /* Move window to the correct monitor and size it */ - WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOACTIVATE); - /* Fill in the SDL window with the window data */ { RECT rect; @@ -408,6 +366,8 @@ WIN_CreateWindow(_THIS, SDL_Window * window) { HWND hwnd, parent = NULL; DWORD style = STYLE_BASIC; + int x, y; + int w, h; if (window->flags & SDL_WINDOW_SKIP_TASKBAR) { parent = CreateWindow(SDL_Appname, TEXT(""), STYLE_BASIC, 0, 0, 32, 32, NULL, NULL, SDL_Instance, NULL); @@ -415,15 +375,11 @@ WIN_CreateWindow(_THIS, SDL_Window * window) style |= GetWindowStyle(window); - /* For high-DPI support, it's easier / more robust** to create the window - with a width/height of 0, then in SetupWindowData we will check the - DPI and adjust the position and size to match window->x,y,w,h. - - **The reason is, we can't know window DPI for sure until after the - window is created. - */ + /* Figure out what the window area will be */ + WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE); + hwnd = - CreateWindow(SDL_Appname, TEXT(""), style, CW_USEDEFAULT, 0, 0, 0, parent, NULL, + CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL, SDL_Instance, NULL); if (!hwnd) { return WIN_SetError("Couldn't create window"); diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index a341d9c02f53d..c91221c64c700 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -61,8 +61,6 @@ typedef struct int scaling_dpi; } SDL_WindowData; -extern void WIN_AdjustWindowRectWithRect(SDL_Window *window, int *x, int *y, int *width, int *height); -extern void WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height, SDL_bool use_current); extern int WIN_CreateWindow(_THIS, SDL_Window * window); extern int WIN_CreateWindowFrom(_THIS, SDL_Window * window, const void *data); extern void WIN_SetWindowTitle(_THIS, SDL_Window * window); From 5c340e9fa0cdf970b3b7d0a8bbb10bcb2cabc346 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 23 Jun 2021 01:09:34 -0600 Subject: [PATCH 20/42] highdpi: split awareness hint into 2 --- include/SDL_hints.h | 7 +++++++ src/video/windows/SDL_windowsvideo.c | 26 +++++++++++++++++++------- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 65866f72053b7..0667f744cd759 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1141,6 +1141,13 @@ extern "C" { */ #define SDL_HINT_VIDEO_ALLOW_HIGHDPI "SDL_VIDEO_ALLOW_HIGHDPI" + /** + * \brief Declare DPI awareness, without enabling SDL's DPI-scaled points coordinate system + * + * See SDL_HINT_VIDEO_ALLOW_HIGHDPI + */ +#define SDL_HINT_WINDOWS_DECLARE_DPI_AWARE "SDL_HINT_WINDOWS_DECLARE_DPI_AWARE" + /** * \brief A variable controlling whether the Direct3D device is initialized for thread-safe operations. * diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 8a3fc0bacc44b..9f714681704b7 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -257,23 +257,31 @@ WIN_SetDPIAware(_THIS) /* NOTE: The above calls will fail if the DPI awareness was already set outside of SDL. - It doesn't matter if they fail, we will just use whatever DPI awareness level was set externally. */ - data->highdpi_enabled = SDL_TRUE; } else if (data->SetProcessDpiAwareness) { /* Windows 8.1-Windows 10 */ HRESULT result = data->SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - - data->highdpi_enabled = SDL_TRUE; } else if (data->SetProcessDPIAware) { /* Vista-Windows 8.0 */ BOOL success = data->SetProcessDPIAware(); + } +} +static void +WIN_SetAllowHighDPI(_THIS) +{ + SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); + + // Declare DPI aware (may have been done in external code or a manifest, as well) + WIN_SetDPIAware(_this); + + // Check for PMv2 context + if (data->AreDpiAwarenessContextsEqual + && data->GetThreadDpiAwarenessContext + && data->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, data->GetThreadDpiAwarenessContext())) + { data->highdpi_enabled = SDL_TRUE; } - - /* NOTE: we won't set data->highdpi_enabled below Vista. - In theory we could still check LOGPIXELSX/Y on XP. */ } int @@ -282,6 +290,10 @@ WIN_VideoInit(_THIS) SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; /* Set the process DPI awareness */ + if (SDL_GetHintBoolean(SDL_HINT_WINDOWS_DECLARE_DPI_AWARE, SDL_FALSE)) { + WIN_SetDPIAware(_this); + } + /* Check if SDL2 highdpi virtualization was requested */ if (SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_HIGHDPI, SDL_FALSE)) { WIN_SetDPIAware(_this); } From 011a7c6d05e9f80a00ca949de86982a41bce210e Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 23 Jun 2021 01:15:13 -0600 Subject: [PATCH 21/42] highdpi: fix last commit --- src/video/windows/SDL_windowsvideo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 9f714681704b7..78516c4723b20 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -295,7 +295,7 @@ WIN_VideoInit(_THIS) } /* Check if SDL2 highdpi virtualization was requested */ if (SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_HIGHDPI, SDL_FALSE)) { - WIN_SetDPIAware(_this); + WIN_SetAllowHighDPI(_this); } if (WIN_InitModes(_this) < 0) { From b67512d40d411922f7730252e543a38dbbe354b2 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 23 Jun 2021 23:22:45 -0600 Subject: [PATCH 22/42] highdpi: remove unused code --- src/video/windows/SDL_windowswindow.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 143beea78e908..58bc5522354fc 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -296,11 +296,10 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre } } { - RECT rect; POINT point; point.x = 0; point.y = 0; - if (ClientToScreen(hwnd, &point) && GetClientRect(hwnd, &rect)) { + if (ClientToScreen(hwnd, &point)) { int x = point.x; int y = point.y; WIN_ScreenPointToSDL(&x, &y); From dbcccad21fedb92a1f3860801b9230e7a83ebfa8 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 23 Jun 2021 23:26:13 -0600 Subject: [PATCH 23/42] highdpi: fix declaration --- src/video/windows/SDL_windowsmodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index 790b138d83c27..2eba22594267d 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -38,7 +38,7 @@ typedef struct extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); -extern void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h); +extern void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h, int *dpi); extern void WIN_ScreenPointToSDL(int *x, int *y); extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); From d8989a8fa9822579be17ec4fa5f60a79f37d705e Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Fri, 25 Jun 2021 00:19:00 -0600 Subject: [PATCH 24/42] highdpi: experiment with removing WIN_ScreenRectFromSDL --- src/video/windows/SDL_windowsmodes.c | 175 +++++++++++++------------- src/video/windows/SDL_windowsmodes.h | 2 +- src/video/windows/SDL_windowsmouse.c | 2 +- src/video/windows/SDL_windowswindow.c | 5 +- 4 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index eebf86f5a63d0..b7c805cae5607 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -448,56 +448,56 @@ WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return 0; } +/* Copied from SDL_GetWindowDisplayIndex */ static int -WIN_GetMonitorDPIAndRects(const SDL_VideoData *videodata, HMONITOR monitor, UINT *xdpi, UINT *ydpi, RECT *monitorrect_sdl, RECT *monitorrect_win) +SDL_GetPointDisplayIndex(int x, int y) { - HRESULT result; - MONITORINFO moninfo = { 0 }; - UINT unused; - int mon_width, mon_height; + int displayIndex; + int i, dist; + int closest = -1; + int closest_dist = 0x7FFFFFFF; + SDL_Point delta; + SDL_Rect rect; + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + SDL_Point center; + center.x = x; + center.y = y; - /* Check for Windows < 8.1*/ - if (!videodata->GetDpiForMonitor) { - return SDL_SetError("GetDpiForMonitor failed"); - } else { - result = videodata->GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, xdpi, &unused); - *ydpi = *xdpi; - if (result != S_OK) { - /* Shouldn't happen? */ - return SDL_SetError("GetDpiForMonitor failed"); + for (i = 0; i < _this->num_displays; ++i) { + SDL_GetDisplayBounds(i, &rect); + if (SDL_EnclosePoints(¢er, 1, &rect, NULL)) { + return i; } - } - moninfo.cbSize = sizeof(MONITORINFO); - if (!GetMonitorInfo(monitor, &moninfo)) { - /* Shouldn't happen? */ - return SDL_SetError("GetMonitorInfo failed"); + delta.x = center.x - (rect.x + rect.w / 2); + delta.y = center.y - (rect.y + rect.h / 2); + dist = (delta.x*delta.x + delta.y*delta.y); + if (dist < closest_dist) { + closest = i; + closest_dist = dist; + } } - - *monitorrect_win = moninfo.rcMonitor; - *monitorrect_sdl = moninfo.rcMonitor; - - /* fix up the right/bottom of monitorrect_sdl */ - mon_width = moninfo.rcMonitor.right - moninfo.rcMonitor.left; - mon_height = moninfo.rcMonitor.bottom - moninfo.rcMonitor.top; - mon_width = MulDiv(mon_width, 96, *xdpi); - mon_height = MulDiv(mon_height, 96, *ydpi); - - monitorrect_sdl->right = monitorrect_sdl->left + mon_width; - monitorrect_sdl->bottom = monitorrect_sdl->top + mon_height; - - return 0; + if (closest < 0) { + SDL_SetError("Couldn't find any displays"); + } + return closest; } -/* Convert an SDL to a Windows screen rect. */ -void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h, int *dpi) +/** +* Current bug: position a window slightly off the left edge of the 100% main monitor, +* touching the 125% monitor to the left. +* +* Then use Shift+Left/Right. The window drifts vertically. +* +* Solution: get rid of WIN_ScreenRectFromSDL. +*/ +void WIN_ScreenPointFromSDL(int *x, int *y, int *dpiOut) { const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); const SDL_VideoData *videodata; - RECT inputrect; - RECT monitorrect_sdl, monitorrect_win; - UINT xdpi, ydpi; - HMONITOR monitor; + int displayIndex; + SDL_Rect bounds; + float ddpi, hdpi, vdpi; if (!videodevice || !videodevice->driverdata) return; @@ -506,44 +506,24 @@ void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h, int *dpi) if (!videodata->highdpi_enabled) return; - /* - The trick here is passing SDL coordinates to MonitorFromRect, which expects Windows - coordinates (pixels). This is wrong, but there is no real alternative, and due to - the way we derive the SDL coordinate system, it works OK: - - - top-left corner of monitors in SDL coordinates are identical to the top-left corner in Windows coordinates. - - the widths/heights of monitors (and windows) in SDL coords are in scaled points, - which are equal or less than the corresponding sizes in pixels (because we only support scale factors >=100%) - - becuase of the above two points, a rect (in SDL coordinates) that is fully inside - a monitor's bounds (in SDL coordinates) will also be fully inside that monitor's bounds in Windows coordinates. - */ - inputrect.left = *x; - inputrect.top = *y; - inputrect.right = *x + *w; - inputrect.bottom = *y + *h; - monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); - - if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { - *w = MulDiv(*w, xdpi, 96); - *h = MulDiv(*h, ydpi, 96); - - *x = monitorrect_sdl.left + MulDiv(*x - monitorrect_sdl.left, xdpi, 96); - *y = monitorrect_sdl.top + MulDiv(*y - monitorrect_sdl.top, ydpi, 96); - - /* ensure the result is not past the right/bottom of the monitor rect */ - if (*x >= monitorrect_win.right) - *x = monitorrect_win.right - 1; - if (*y >= monitorrect_win.bottom) - *y = monitorrect_win.bottom - 1; - - if (dpi) { - *dpi = xdpi; - } - } else { - if (dpi) { - *dpi = 96; - } + displayIndex = SDL_GetPointDisplayIndex(*x, *y); + + if (displayIndex < 0) { + *dpiOut = 96; + return; + } + + if (SDL_GetDisplayBounds(displayIndex, &bounds) < 0 + || SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi) < 0) { + *dpiOut = 96; + return; } + + /* ddpi/hdpi/vdpi are all identical */ + *dpiOut = ddpi; + + *x = bounds.x + MulDiv(*x - bounds.x, *dpiOut, 96); + *y = bounds.y + MulDiv(*y - bounds.y, *dpiOut, 96); } /* Converts a Windows screen point to an SDL one. */ @@ -551,28 +531,45 @@ void WIN_ScreenPointToSDL(int *x, int *y) { const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); const SDL_VideoData *videodata; - RECT inputrect; - RECT monitorrect_sdl, monitorrect_win; - UINT xdpi, ydpi; + POINT point; HMONITOR monitor; + int i, displayIndex; + SDL_Rect bounds; + float ddpi, hdpi, vdpi; - if (!videodevice || !videodevice->driverdata) + if (!videodevice || !videodevice->driverdata) { return; + } videodata = (SDL_VideoData *)videodevice->driverdata; - if (!videodata->highdpi_enabled) + if (!videodata->highdpi_enabled) { return; + } - inputrect.left = *x; - inputrect.top = *y; - inputrect.right = *x; - inputrect.bottom = *y; - monitor = MonitorFromRect(&inputrect, MONITOR_DEFAULTTONEAREST); + point.x = *x; + point.y = *y; + monitor = MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST); + + /* Search for the corresponding SDL monitor */ + displayIndex = -1; + for (i = 0; i < videodevice->num_displays; ++i) { + SDL_DisplayData *driverdata = (SDL_DisplayData *)videodevice->displays[i].driverdata; + if (driverdata->MonitorHandle == monitor) { + displayIndex = i; + } + } + if (displayIndex == -1) { + return; + } - if (WIN_GetMonitorDPIAndRects(videodata, monitor, &xdpi, &ydpi, &monitorrect_sdl, &monitorrect_win) == 0) { - *x = monitorrect_win.left + MulDiv(*x - monitorrect_win.left, 96, xdpi); - *y = monitorrect_win.top + MulDiv(*y - monitorrect_win.top, 96, ydpi); + /* Get SDL display properties */ + if (SDL_GetDisplayBounds(displayIndex, &bounds) < 0 + || SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi) < 0) { + return; } + + *x = bounds.x + MulDiv(*x - bounds.x, 96, (int)ddpi); + *y = bounds.y + MulDiv(*y - bounds.y, 96, (int)ddpi); } void diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index 2eba22594267d..6bf62e58693ac 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -38,7 +38,7 @@ typedef struct extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); extern int WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); -extern void WIN_ScreenRectFromSDL(int *x, int *y, int *w, int *h, int *dpi); +extern void WIN_ScreenPointFromSDL(int *x, int *y, int *dpiOut); extern void WIN_ScreenPointToSDL(int *x, int *y); extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 6c7f1549ab79c..bd3038a6a99bb 100644 --- a/src/video/windows/SDL_windowsmouse.c +++ b/src/video/windows/SDL_windowsmouse.c @@ -288,7 +288,7 @@ WIN_WarpMouseGlobal(int x, int y) { POINT pt; - WIN_ScreenRectFromSDL(&x, &y, NULL, NULL, NULL); + WIN_ScreenPointFromSDL(&x, &y, NULL); pt.x = x; pt.y = y; SetCursorPos(pt.x, pt.y); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 58bc5522354fc..b887f122e2520 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -138,7 +138,10 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x *width = (use_current ? window->w : window->windowed.w); *height = (use_current ? window->h : window->windowed.h); - WIN_ScreenRectFromSDL(x, y, width, height, &dpi); + /* Convert from SDL coordinate to pixels */ + WIN_ScreenPointFromSDL(x, y, &dpi); + *width = MulDiv(*width, dpi, 96); + *height = MulDiv(*height, dpi, 96); rect.left = *x; rect.top = *y; From 3a0fa2cb8b2cf989eaac123783d87444cc6ff9eb Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Fri, 25 Jun 2021 00:57:04 -0600 Subject: [PATCH 25/42] highdpi: add logging --- src/video/windows/SDL_windowsmodes.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index b7c805cae5607..4c2e1424c0567 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -498,6 +498,7 @@ void WIN_ScreenPointFromSDL(int *x, int *y, int *dpiOut) int displayIndex; SDL_Rect bounds; float ddpi, hdpi, vdpi; + int x_sdl, y_sdl; if (!videodevice || !videodevice->driverdata) return; @@ -522,8 +523,15 @@ void WIN_ScreenPointFromSDL(int *x, int *y, int *dpiOut) /* ddpi/hdpi/vdpi are all identical */ *dpiOut = ddpi; - *x = bounds.x + MulDiv(*x - bounds.x, *dpiOut, 96); - *y = bounds.y + MulDiv(*y - bounds.y, *dpiOut, 96); + x_sdl = *x; + y_sdl = *y; + *x = bounds.x + MulDiv(x_sdl - bounds.x, (int)ddpi, 96); + *y = bounds.y + MulDiv(y_sdl - bounds.y, (int)ddpi, 96); + +#ifdef HIGHDPI_DEBUG + SDL_Log("WIN_ScreenPointFromSDL: (%d, %d) points -> (%d x %d) pixels, using %d DPI monitor", + x_sdl, y_sdl, *x, *y, (int)ddpi); +#endif } /* Converts a Windows screen point to an SDL one. */ @@ -536,6 +544,7 @@ void WIN_ScreenPointToSDL(int *x, int *y) int i, displayIndex; SDL_Rect bounds; float ddpi, hdpi, vdpi; + int x_pixels, y_pixels; if (!videodevice || !videodevice->driverdata) { return; @@ -568,8 +577,15 @@ void WIN_ScreenPointToSDL(int *x, int *y) return; } - *x = bounds.x + MulDiv(*x - bounds.x, 96, (int)ddpi); - *y = bounds.y + MulDiv(*y - bounds.y, 96, (int)ddpi); + x_pixels = *x; + y_pixels = *y; + *x = bounds.x + MulDiv(x_pixels - bounds.x, 96, (int)ddpi); + *y = bounds.y + MulDiv(y_pixels - bounds.y, 96, (int)ddpi); + +#ifdef HIGHDPI_DEBUG + SDL_Log("WIN_ScreenPointToSDL: (%d, %d) pixels -> (%d x %d) points, using %d DPI monitor", + x_pixels, y_pixels, *x, *y, (int)ddpi); +#endif } void From 8e34396e7a54488f23e5d0065aeded7320602994 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 26 Jun 2021 15:14:58 -0600 Subject: [PATCH 26/42] highdpi: fix SDL_GetPointDisplayIndex test --- src/video/windows/SDL_windowsmodes.c | 40 +++++++++++++++++++++------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 4c2e1424c0567..264d7c7776526 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -448,29 +448,51 @@ WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return 0; } -/* Copied from SDL_GetWindowDisplayIndex */ +static void +SDL_GetClosetPointOnRect(const SDL_Rect *rect, int *x, int *y) +{ + const int right = rect->x + rect->w; + const int bottom = rect->y + rect->h; + + if (*x < rect->x) { + *x = rect->x; + } else if (*x >= right) { + *x = right - 1; + } + + if (*y < rect->y) { + *y = rect->y; + } else if (*y >= bottom) { + *y = bottom - 1; + } +} + +/* Adapted from SDL_GetWindowDisplayIndex (different test though) */ static int SDL_GetPointDisplayIndex(int x, int y) { - int displayIndex; int i, dist; int closest = -1; int closest_dist = 0x7FFFFFFF; + int closest_x_on_disp; + int closest_y_on_disp; SDL_Point delta; SDL_Rect rect; SDL_VideoDevice *_this = SDL_GetVideoDevice(); - SDL_Point center; - center.x = x; - center.y = y; + SDL_Point point; + point.x = x; + point.y = y; for (i = 0; i < _this->num_displays; ++i) { + /* Check for an exact match */ SDL_GetDisplayBounds(i, &rect); - if (SDL_EnclosePoints(¢er, 1, &rect, NULL)) { + if (SDL_EnclosePoints(&point, 1, &rect, NULL)) { return i; } - delta.x = center.x - (rect.x + rect.w / 2); - delta.y = center.y - (rect.y + rect.h / 2); + SDL_GetClosetPointOnRect(&rect, &closest_x_on_disp, &closest_y_on_disp); + delta.x = point.x - closest_x_on_disp; + delta.y = point.y - closest_y_on_disp; dist = (delta.x*delta.x + delta.y*delta.y); if (dist < closest_dist) { closest = i; @@ -521,7 +543,7 @@ void WIN_ScreenPointFromSDL(int *x, int *y, int *dpiOut) } /* ddpi/hdpi/vdpi are all identical */ - *dpiOut = ddpi; + *dpiOut = (int)ddpi; x_sdl = *x; y_sdl = *y; From 1591972a353a870c2647c2f5ae6540bdae76661f Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 26 Jun 2021 15:26:29 -0600 Subject: [PATCH 27/42] highdpi: fix last commit --- src/video/windows/SDL_windowsmodes.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 264d7c7776526..13f0fc205b4d5 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -490,7 +490,11 @@ SDL_GetPointDisplayIndex(int x, int y) return i; } + /* Snap x, y to the display rect */ + closest_x_on_disp = point.x; + closest_y_on_disp = point.y; SDL_GetClosetPointOnRect(&rect, &closest_x_on_disp, &closest_y_on_disp); + delta.x = point.x - closest_x_on_disp; delta.y = point.y - closest_y_on_disp; dist = (delta.x*delta.x + delta.y*delta.y); From 918b02bdcc1873eca67768dcfcc29827605343c0 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 26 Jun 2021 15:26:56 -0600 Subject: [PATCH 28/42] highdpi: fix typo --- src/video/windows/SDL_windowsmodes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 13f0fc205b4d5..176ad99e581f0 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -449,7 +449,7 @@ WIN_GetDisplayUsableBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) } static void -SDL_GetClosetPointOnRect(const SDL_Rect *rect, int *x, int *y) +SDL_GetClosestPointOnRect(const SDL_Rect *rect, int *x, int *y) { const int right = rect->x + rect->w; const int bottom = rect->y + rect->h; @@ -493,7 +493,7 @@ SDL_GetPointDisplayIndex(int x, int y) /* Snap x, y to the display rect */ closest_x_on_disp = point.x; closest_y_on_disp = point.y; - SDL_GetClosetPointOnRect(&rect, &closest_x_on_disp, &closest_y_on_disp); + SDL_GetClosestPointOnRect(&rect, &closest_x_on_disp, &closest_y_on_disp); delta.x = point.x - closest_x_on_disp; delta.y = point.y - closest_y_on_disp; From 787c19b015cd043f59d2f2791a12806774710719 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 30 Jun 2021 00:21:09 -0600 Subject: [PATCH 29/42] highdpi: misc cleanup --- include/SDL_hints.h | 7 ------ src/video/windows/SDL_windowsevents.c | 26 ++++++++++----------- src/video/windows/SDL_windowsvideo.c | 33 ++++----------------------- src/video/windows/SDL_windowswindow.c | 25 ++++++++++++-------- 4 files changed, 32 insertions(+), 59 deletions(-) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 0667f744cd759..65866f72053b7 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -1141,13 +1141,6 @@ extern "C" { */ #define SDL_HINT_VIDEO_ALLOW_HIGHDPI "SDL_VIDEO_ALLOW_HIGHDPI" - /** - * \brief Declare DPI awareness, without enabling SDL's DPI-scaled points coordinate system - * - * See SDL_HINT_VIDEO_ALLOW_HIGHDPI - */ -#define SDL_HINT_WINDOWS_DECLARE_DPI_AWARE "SDL_HINT_WINDOWS_DECLARE_DPI_AWARE" - /** * \brief A variable controlling whether the Direct3D device is initialized for thread-safe operations. * diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 6a528f2d81c2f..ea34d1f4b757e 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1097,8 +1097,6 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) size.bottom = h; size.right = w; - /* TODO: Possibly we should use AdjustWindowRectExForDpi even if - SDL DPI awareness wasn't requested */ if (data->videodata->highdpi_enabled && data->videodata->AdjustWindowRectExForDpi) { data->videodata->AdjustWindowRectExForDpi(&size, style, menu, 0, data->scaling_dpi); } else { @@ -1447,38 +1445,38 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) scaling. */ - const int potentialDPI = (int)wParam; - const int currentDPI = (int)data->videodata->GetDpiForWindow(hwnd); + 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; #ifdef HIGHDPI_DEBUG - SDL_Log("WM_GETDPISCALEDSIZE: current DPI: %d potential DPI: %d. input size: (%dx%d)", currentDPI, potentialDPI, sizeInOut->cx, sizeInOut->cy); + SDL_Log("WM_GETDPISCALEDSIZE: current DPI: %d potential DPI: %d. input size: (%dx%d)", prevDPI, nextDPI, sizeInOut->cx, sizeInOut->cy); #endif - /* get the frame size in pixels at currentDPI */ + /* subtract the window frame size that would have been used at prevDPI */ { DWORD style = GetWindowLong(hwnd, GWL_STYLE); BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); RECT rect = {0}; if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) - data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, currentDPI); + 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; + query_client_w_win = sizeInOut->cx - frame_w; + query_client_h_win = sizeInOut->cy - frame_h; + } /* convert to new dpi */ - query_client_w_win = MulDiv(query_client_w_win, potentialDPI, currentDPI); - query_client_h_win = MulDiv(query_client_h_win, potentialDPI, currentDPI); + query_client_w_win = MulDiv(query_client_w_win, nextDPI, prevDPI); + query_client_h_win = MulDiv(query_client_h_win, nextDPI, prevDPI); - /* re-add the window frame in the new DPI */ + /* add the window frame size that would be used at nextDPI */ { DWORD style = GetWindowLong(hwnd, GWL_STYLE); BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); @@ -1490,7 +1488,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) rect.bottom = query_client_h_win; if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) - data->videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, potentialDPI); + 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; diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 78516c4723b20..a5b4d1d27abf1 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -243,37 +243,16 @@ VideoBootStrap WINDOWS_bootstrap = { "windows", "SDL Windows video driver", WIN_CreateDevice }; -static void -WIN_SetDPIAware(_THIS) -{ - SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); - if (data->SetProcessDpiAwarenessContext) { - /* Windows 10 Anniversary Update+ */ - /* First, try the Windows 10 Creators Update version */ - BOOL result = data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - if (!result) { - result = data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); - } - - /* - NOTE: The above calls will fail if the DPI awareness was already set outside of SDL. - */ - } else if (data->SetProcessDpiAwareness) { - /* Windows 8.1-Windows 10 */ - HRESULT result = data->SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - } else if (data->SetProcessDPIAware) { - /* Vista-Windows 8.0 */ - BOOL success = data->SetProcessDPIAware(); - } -} - static void WIN_SetAllowHighDPI(_THIS) { SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); // Declare DPI aware (may have been done in external code or a manifest, as well) - WIN_SetDPIAware(_this); + if (data->SetProcessDpiAwarenessContext) { + /* Windows 10 Creators Update+ */ + data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + } // Check for PMv2 context if (data->AreDpiAwarenessContextsEqual @@ -289,10 +268,6 @@ WIN_VideoInit(_THIS) { SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; - /* Set the process DPI awareness */ - if (SDL_GetHintBoolean(SDL_HINT_WINDOWS_DECLARE_DPI_AWARE, SDL_FALSE)) { - WIN_SetDPIAware(_this); - } /* Check if SDL2 highdpi virtualization was requested */ if (SDL_GetHintBoolean(SDL_HINT_VIDEO_ALLOW_HIGHDPI, SDL_FALSE)) { WIN_SetAllowHighDPI(_this); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index b887f122e2520..d31234c60aaa7 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -123,23 +123,30 @@ Can be called before HWND is created. static void WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current) { - const SDL_VideoDevice *videodevice = SDL_GetVideoDevice(); - const SDL_VideoData *videodata; + SDL_WindowData *data = (SDL_WindowData *)window->driverdata; + SDL_VideoData* videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL; RECT rect; int dpi; - if (!videodevice || !videodevice->driverdata) - return; - - videodata = (SDL_VideoData *)videodevice->driverdata; - + /* Window client rect, in SDL screen coordinates */ *x = (use_current ? window->x : window->windowed.x); *y = (use_current ? window->y : window->windowed.y); *width = (use_current ? window->w : window->windowed.w); *height = (use_current ? window->h : window->windowed.h); - /* Convert from SDL coordinate to pixels */ + /* Convert position from SDL coordinates to pixels */ WIN_ScreenPointFromSDL(x, y, &dpi); + + /* If the window exists, use our cached DPI for: + - converting the client area size from points to pixels + - the AdjustWindowRectExForDpi call below + + data is NULL only when first creating the window + */ + if (data) { + dpi = data->scaling_dpi; + } + *width = MulDiv(*width, dpi, 96); *height = MulDiv(*height, dpi, 96); @@ -153,7 +160,7 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x */ if (!(window->flags & SDL_WINDOW_BORDERLESS)) if (videodata->highdpi_enabled && videodata->AdjustWindowRectExForDpi) { - videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi); + videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, (UINT)dpi); } else { AdjustWindowRectEx(&rect, style, menu, 0); } From b4bf1f1fc5cf29c0a2206000a18d7d3d9adf2722 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Thu, 1 Jul 2021 14:33:17 -0600 Subject: [PATCH 30/42] highdpi: use SetThreadDpiAwarenessContext --- src/video/windows/SDL_windowsvideo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index a5b4d1d27abf1..7a2a4f76d5d80 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -249,9 +249,9 @@ WIN_SetAllowHighDPI(_THIS) SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); // Declare DPI aware (may have been done in external code or a manifest, as well) - if (data->SetProcessDpiAwarenessContext) { + if (data->SetThreadDpiAwarenessContext) { /* Windows 10 Creators Update+ */ - data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + data->SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } // Check for PMv2 context From a6b619d068f9971715fc9031caa0411ce992c700 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Thu, 1 Jul 2021 20:01:32 -0600 Subject: [PATCH 31/42] highdpi: fullscreen wip --- src/video/windows/SDL_windowswindow.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d31234c60aaa7..f6bde93ba8a2a 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -762,12 +762,7 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, int x, y; int w, h; - /* BUG: windows don't receive a WM_DPICHANGED message after a ChangeDisplaySettingsEx, - so we must manually update the cached DPI (see WIN_SetDisplayMode). */ -#ifdef HIGHDPI_DEBUG - SDL_Log("WIN_SetWindowFullscreen: dpi: %d, stale cached dpi: %d", WIN_GetDPIForHWND(videodata, hwnd), data->scaling_dpi); -#endif - data->scaling_dpi = WIN_GetDPIForHWND(videodata, hwnd); + SDL_Log("WIN_SetWindowFullscreen %d", (int)fullscreen); /* clear the window size, to cause us to send a SDL_WINDOWEVENT_RESIZED event in WM_WINDOWPOSCHANGED */ data->window->w = 0; @@ -805,6 +800,8 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, data->windowed_mode_was_maximized = SDL_TRUE; style &= ~WS_MAXIMIZE; } + + data->scaling_dpi = 96; } else { BOOL menu; @@ -819,6 +816,8 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, data->windowed_mode_was_maximized = SDL_FALSE; } + data->scaling_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); + menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); } @@ -826,6 +825,8 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, data->expected_resize = SDL_TRUE; SetWindowPos(hwnd, top, x, y, w, h, SWP_NOCOPYBITS | SWP_NOACTIVATE); data->expected_resize = SDL_FALSE; + + SDL_Log("WIN_SetWindowFullscreen %d done. Set window to %d,%d, %dx%d", (int)fullscreen, x, y, w, h); } int From 4ee1565c46b7862f5b97648cfcad1992e0e46958 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 7 Nov 2021 02:49:34 -0700 Subject: [PATCH 32/42] fix SDL_WINDOW_FULLSCREEN_DESKTOP giving window size in pixels --- src/video/windows/SDL_windowswindow.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index f6bde93ba8a2a..d07b0e0fff12e 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -801,7 +801,7 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, style &= ~WS_MAXIMIZE; } - data->scaling_dpi = 96; + //data->scaling_dpi = 96; } else { BOOL menu; @@ -816,7 +816,7 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, data->windowed_mode_was_maximized = SDL_FALSE; } - data->scaling_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); + //data->scaling_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); From 80e41de240b8aeb1cbcc4180a66338e96e0f98d9 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 6 Nov 2021 21:57:17 -0600 Subject: [PATCH 33/42] enable HIGHDPI_DEBUG --- src/video/windows/SDL_windowsevents.c | 2 +- src/video/windows/SDL_windowswindow.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index ea34d1f4b757e..5b70d5a29d5c7 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -52,7 +52,7 @@ #include "wmmsg.h" #endif -/* #define HIGHDPI_DEBUG */ +#define HIGHDPI_DEBUG /* Masks for processing the windows KEYDOWN and KEYUP messages */ #define REPEATED_KEYMASK (1<<30) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d07b0e0fff12e..7673df0802699 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -46,7 +46,7 @@ #define SWP_NOCOPYBITS 0 #endif -/* #define HIGHDPI_DEBUG */ +#define HIGHDPI_DEBUG /* Fake window to help with DirectInput events. */ HWND SDL_HelperWindow = NULL; From d5979c0b8de14ea73562fb80d13c464fe3d7f044 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 8 Nov 2021 23:29:45 -0700 Subject: [PATCH 34/42] highdpi: GetDrawableSize stuff --- src/video/windows/SDL_windowsopengl.c | 2 ++ src/video/windows/SDL_windowsopengles.c | 2 ++ src/video/windows/SDL_windowsopengles.h | 1 + src/video/windows/SDL_windowsvideo.c | 2 ++ 4 files changed, 7 insertions(+) diff --git a/src/video/windows/SDL_windowsopengl.c b/src/video/windows/SDL_windowsopengl.c index 98e1f06e59976..d73515abd9a9a 100644 --- a/src/video/windows/SDL_windowsopengl.c +++ b/src/video/windows/SDL_windowsopengl.c @@ -676,6 +676,8 @@ WIN_GL_CreateContext(_THIS, SDL_Window * window) _this->GL_UnloadLibrary = WIN_GLES_UnloadLibrary; _this->GL_CreateContext = WIN_GLES_CreateContext; _this->GL_MakeCurrent = WIN_GLES_MakeCurrent; + // FIXME: + //_this->GL_GetDrawableSize = WIN_GLES_GetDrawableSize; _this->GL_SetSwapInterval = WIN_GLES_SetSwapInterval; _this->GL_GetSwapInterval = WIN_GLES_GetSwapInterval; _this->GL_SwapWindow = WIN_GLES_SwapWindow; diff --git a/src/video/windows/SDL_windowsopengles.c b/src/video/windows/SDL_windowsopengles.c index eedc0e6abde8f..1dd761642914d 100644 --- a/src/video/windows/SDL_windowsopengles.c +++ b/src/video/windows/SDL_windowsopengles.c @@ -40,6 +40,7 @@ WIN_GLES_LoadLibrary(_THIS, const char *path) { _this->GL_UnloadLibrary = WIN_GL_UnloadLibrary; _this->GL_CreateContext = WIN_GL_CreateContext; _this->GL_MakeCurrent = WIN_GL_MakeCurrent; + _this->GL_GetDrawableSize = WIN_GL_GetDrawableSize; _this->GL_SetSwapInterval = WIN_GL_SetSwapInterval; _this->GL_GetSwapInterval = WIN_GL_GetSwapInterval; _this->GL_SwapWindow = WIN_GL_SwapWindow; @@ -72,6 +73,7 @@ WIN_GLES_CreateContext(_THIS, SDL_Window * window) _this->GL_UnloadLibrary = WIN_GL_UnloadLibrary; _this->GL_CreateContext = WIN_GL_CreateContext; _this->GL_MakeCurrent = WIN_GL_MakeCurrent; + _this->GL_GetDrawableSize = WIN_GL_GetDrawableSize; _this->GL_SetSwapInterval = WIN_GL_SetSwapInterval; _this->GL_GetSwapInterval = WIN_GL_GetSwapInterval; _this->GL_SwapWindow = WIN_GL_SwapWindow; diff --git a/src/video/windows/SDL_windowsopengles.h b/src/video/windows/SDL_windowsopengles.h index b21c56f4fedd0..e59c02ebd18ff 100644 --- a/src/video/windows/SDL_windowsopengles.h +++ b/src/video/windows/SDL_windowsopengles.h @@ -39,6 +39,7 @@ extern int WIN_GLES_LoadLibrary(_THIS, const char *path); extern SDL_GLContext WIN_GLES_CreateContext(_THIS, SDL_Window * window); extern int WIN_GLES_SwapWindow(_THIS, SDL_Window * window); extern int WIN_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context); +extern void WIN_GLES_GetDrawableSize(_THIS, SDL_Window* window, int* w, int* h); extern void WIN_GLES_DeleteContext(_THIS, SDL_GLContext context); extern int WIN_GLES_SetupWindow(_THIS, SDL_Window * window); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 7a2a4f76d5d80..7b5f6473783e2 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -213,6 +213,8 @@ WIN_CreateDevice(int devindex) device->GL_UnloadLibrary = WIN_GLES_UnloadLibrary; device->GL_CreateContext = WIN_GLES_CreateContext; device->GL_MakeCurrent = WIN_GLES_MakeCurrent; + // FIXME: + //device->GL_GetDrawableSize = WIN_GLES_GetDrawableSize; device->GL_SetSwapInterval = WIN_GLES_SetSwapInterval; device->GL_GetSwapInterval = WIN_GLES_GetSwapInterval; device->GL_SwapWindow = WIN_GLES_SwapWindow; From 4b6f42f27ea5c62d06840ba0618e22aea6e49aac Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 21 Nov 2021 15:25:22 -0700 Subject: [PATCH 35/42] highdpi: fix testgl2 issue caused by using SetThreadDpiAwarenessContext --- src/video/windows/SDL_windowsvideo.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 7b5f6473783e2..b3fea85f4da50 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -251,9 +251,10 @@ WIN_SetAllowHighDPI(_THIS) SDL_VideoData *data = SDL_static_cast(SDL_VideoData *, _this->driverdata); // Declare DPI aware (may have been done in external code or a manifest, as well) - if (data->SetThreadDpiAwarenessContext) { - /* Windows 10 Creators Update+ */ - data->SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + if (data->SetProcessDpiAwarenessContext) { + // 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) + data->SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } // Check for PMv2 context From 18007f0abf197fb9b850f10c9ca2d4b304334834 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 21 Nov 2021 16:08:49 -0700 Subject: [PATCH 36/42] highdpi: also support DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE --- src/video/windows/SDL_windowsvideo.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index b3fea85f4da50..a06c4a43a3ff8 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -264,6 +264,16 @@ WIN_SetAllowHighDPI(_THIS) { data->highdpi_enabled = SDL_TRUE; } + + // TODO: enabling DPI awareness through Windows Explorer + // (right click .exe -> Properties -> Compatibility -> High DPI Settings -> + // check "Override high DPI Scaling behaviour", select Application) gives + // a DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE context (at least on Windows 10 21H1) + if (data->AreDpiAwarenessContextsEqual + && data->GetThreadDpiAwarenessContext + && data->AreDpiAwarenessContextsEqual(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, data->GetThreadDpiAwarenessContext())) { + data->highdpi_enabled = SDL_TRUE; + } } int From f308ad539c52f6e2b0b8a04e641ca4141730ffaf Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 21 Nov 2021 19:54:51 -0700 Subject: [PATCH 37/42] highdpi: fix exiting exclusive fullscreen on highdpi monitor causing windows to shrink --- src/video/windows/SDL_windowsmodes.c | 2 +- src/video/windows/SDL_windowswindow.c | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 176ad99e581f0..b56dfd19ca1d2 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -30,7 +30,7 @@ #define CDS_FULLSCREEN 0 #endif -/* #define DEBUG_MODES */ +#define DEBUG_MODES static void WIN_UpdateDisplayMode(_THIS, LPCWSTR deviceName, DWORD index, SDL_DisplayMode * mode) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 7673df0802699..b3fc572d9d6d4 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -121,7 +121,7 @@ out: window rect, including frame (in Windows coordinates) Can be called before HWND is created. */ 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; @@ -143,9 +143,12 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x data is NULL only when first creating the window */ - if (data) { + if (data && !force_ignore_window_dpi) { dpi = data->scaling_dpi; } + if (data && force_ignore_window_dpi) { + SDL_Log("WIN_AdjustWindowRectWithStyle: using dpi %d, was going to use %d but told not to", dpi, data->scaling_dpi); + } *width = MulDiv(*width, dpi, 96); *height = MulDiv(*height, dpi, 96); @@ -184,7 +187,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 @@ -385,7 +388,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, @@ -749,6 +752,9 @@ WIN_RestoreWindow(_THIS, SDL_Window * window) data->expected_resize = SDL_FALSE; } +/* + * + */ void WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) { @@ -815,11 +821,14 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, style |= WS_MAXIMIZE; data->windowed_mode_was_maximized = SDL_FALSE; } - - //data->scaling_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); + + int curr_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); + SDL_Log("Leaving fullscreen, current window dpi is %d", curr_dpi); menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); - WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE); + + + WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE, SDL_TRUE); } SetWindowLong(hwnd, GWL_STYLE, style); data->expected_resize = SDL_TRUE; From fc53a9a087252915f82efe33ab89b0494b8db349 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 21 Nov 2021 20:39:15 -0700 Subject: [PATCH 38/42] highdpi: fix vulkan drawable size --- src/video/windows/SDL_windowsvideo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index a06c4a43a3ff8..9823bc3ea0281 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -225,6 +225,7 @@ WIN_CreateDevice(int devindex) device->Vulkan_UnloadLibrary = WIN_Vulkan_UnloadLibrary; device->Vulkan_GetInstanceExtensions = WIN_Vulkan_GetInstanceExtensions; device->Vulkan_CreateSurface = WIN_Vulkan_CreateSurface; + device->Vulkan_GetDrawableSize = WIN_GL_GetDrawableSize; #endif device->StartTextInput = WIN_StartTextInput; From 4c954119a336182136be16a3fea485962c950bb6 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sun, 21 Nov 2021 21:03:26 -0700 Subject: [PATCH 39/42] highdpi: port mouse_rect functionaliy --- src/video/windows/SDL_windowswindow.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index b3fc572d9d6d4..31663a1c8c650 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -1163,12 +1163,19 @@ WIN_UpdateClipCursor(SDL_Window *window) ClientToScreen(data->hwnd, (LPPOINT) & rect); ClientToScreen(data->hwnd, (LPPOINT) & rect + 1); if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) { + SDL_Rect mouse_rect_win_client; RECT mouse_rect, intersection; - mouse_rect.left = rect.left + window->mouse_rect.x; - mouse_rect.top = rect.top + window->mouse_rect.y; - mouse_rect.right = mouse_rect.left + window->mouse_rect.w - 1; - mouse_rect.bottom = mouse_rect.top + window->mouse_rect.h - 1; + /* mouse_rect_win_client is the mouse rect in Windows client space */ + mouse_rect_win_client = window->mouse_rect; + WIN_ClientPointFromSDL(window, &mouse_rect_win_client.x, &mouse_rect_win_client.y); + WIN_ClientPointFromSDL(window, &mouse_rect_win_client.w, &mouse_rect_win_client.h); + + /* mouse_rect is the rect in Windows screen space */ + mouse_rect.left = rect.left + mouse_rect_win_client.x; + mouse_rect.top = rect.top + mouse_rect_win_client.y; + mouse_rect.right = mouse_rect.left + mouse_rect_win_client.w - 1; + mouse_rect.bottom = mouse_rect.top + mouse_rect_win_client.h - 1; if (IntersectRect(&intersection, &rect, &mouse_rect)) { SDL_memcpy(&rect, &intersection, sizeof(rect)); } else if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) != 0) { From 381863eb4caeaf42886b61a7df206fa55a0b05cd Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 22 Nov 2021 00:19:29 -0700 Subject: [PATCH 40/42] highdpi: extra logging --- src/video/windows/SDL_windowsevents.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 5b70d5a29d5c7..0c3f994b5c1c4 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1179,9 +1179,9 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 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 (points): (%d, %d) (%d x %d)", + SDL_Log("WM_WINDOWPOSCHANGED: Windows client rect (pixels): (%d, %d) (%d x %d)\tSDL client rect (points): (%d, %d) (%d x %d) cached dpi %d, windows reported dpi %d", rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, - x, y, w, h); + x, y, w, h, data->scaling_dpi, data->videodata->GetDpiForWindow ? data->videodata->GetDpiForWindow(data->hwnd) : 0); #endif /* Forces a WM_PAINT event */ From c31ce1a143692f7d3cf65f7487c806e6be8f3880 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 22 Nov 2021 00:33:37 -0700 Subject: [PATCH 41/42] highdpi: wip: debugging exiting from fullscreen on macbook --- src/video/windows/SDL_windowswindow.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 31663a1c8c650..77c86e1fd1eb5 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -823,11 +823,18 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, } int curr_dpi = WIN_GetDPIForHWND(videodata, data->hwnd); - SDL_Log("Leaving fullscreen, current window dpi is %d", curr_dpi); + + UINT dpi_x = 0, dpi_y = 0; + videodata->GetDpiForMonitor(displaydata->MonitorHandle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); + int mon_dpi = dpi_x; menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL); + //data->scaling_dpi = mon_dpi; + + SDL_Log("Leaving fullscreen, current window dpi is %d mon dpi %d", curr_dpi, mon_dpi); + //WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE, SDL_FALSE); WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE, SDL_TRUE); } SetWindowLong(hwnd, GWL_STYLE, style); From b29801346cd36686f53c265a7d6de2a2e3b7aa81 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Mon, 22 Nov 2021 01:42:12 -0700 Subject: [PATCH 42/42] highdpi: work around for exiting fullscreen with dx9 renderer giving wrong window size --- src/render/direct3d/SDL_render_d3d.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 0a77946368eac..0d7d8e0362542 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -343,6 +343,7 @@ D3D_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) { data->updateSize = SDL_TRUE; + D3D_ActivateRenderer(renderer); } }