HighDPI: remove SWP_NOSIZE in WIN_SetWindowPosition If the move results in a DPI change, we need to allow the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different). - WM_DPICHANGED: Don't assume WM_GETDPISCALEDSIZE is always called for PMv2 awareness - it's only called during interactive dragging. - WIN_AdjustWindowRectWithStyle: always calculate final window size including frame based on the destination rect, not based on the current window DPI. - Update wmmsg.h to include WM_GETDPISCALEDSIZE (for WMMSG_DEBUG) - WIN_AdjustWindowRectWithStyle: add optional logging - WM_GETMINMAXINFO: add optional HIGHDPI_DEBUG logging - WM_DPICHANGED: fix potentially clobbering data->expected_resize Together these changes fix the following scenario: - launch testwm2 with the SDL_WINDOWS_DPI_AWARENESS=permonitorv2 environment variable - Windows 10 21H2 (OS Build 19044.1706) - Left (primary) monitor: 3840x2160, 125% scaling - Right (secondary) monitor: 2560x1440, 100% scaling - Alt+Enter, Alt+Enter (to enter + leave desktop fullscreen), Alt+Right (to move window to right monitor). Ensure the window client area stays 640x480. Drag the window back to the 125% monitor, ensure client area stays 640x480.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 22780ad..4130f97 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1088,19 +1088,25 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
inside their function, so I have to do it here.
*/
BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
+ UINT dpi;
+
+ dpi = 96;
size.top = 0;
size.left = 0;
size.bottom = h;
size.right = w;
if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) {
- UINT dpi = data->videodata->GetDpiForWindow(hwnd);
+ dpi = data->videodata->GetDpiForWindow(hwnd);
data->videodata->AdjustWindowRectExForDpi(&size, style, menu, 0, dpi);
} else {
AdjustWindowRectEx(&size, style, menu, 0);
}
w = size.right - size.left;
h = size.bottom - size.top;
+#ifdef HIGHDPI_DEBUG
+ SDL_Log("WM_GETMINMAXINFO: max window size: %dx%d using dpi: %u", w, h, dpi);
+#endif
}
/* Fix our size to the current size */
@@ -1422,7 +1428,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_GETDPISCALEDSIZE:
/* Windows 10 Creators Update+ */
- /* Documented as only being sent to windows that are per-monitor V2 DPI aware. */
+ /* Documented as only being sent to windows that are per-monitor V2 DPI aware.
+
+ Experimentation shows it's only sent during interactive dragging, not in response to
+ SetWindowPos. */
if (data->videodata->GetDpiForWindow && data->videodata->AdjustWindowRectExForDpi) {
/* Windows expects applications to scale their window rects linearly
when dragging between monitors with different DPI's.
@@ -1490,6 +1499,7 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
const int newDPI = HIWORD(wParam);
RECT* const suggestedRect = (RECT*)lParam;
+ SDL_bool setExpectedResize = SDL_FALSE;
int w, h;
#ifdef HIGHDPI_DEBUG
@@ -1497,12 +1507,28 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
suggestedRect->left, suggestedRect->top, suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top);
#endif
- /* DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 means that
- WM_GETDPISCALEDSIZE will have been called, so we can use suggestedRect. */
+ if (data->expected_resize) {
+ /* This DPI change is coming from an explicit SetWindowPos call within SDL.
+ Assume all call sites are calculating the DPI-aware frame correctly, so
+ we don't need to do any further adjustment. */
+#ifdef HIGHDPI_DEBUG
+ SDL_Log("WM_DPICHANGED: Doing nothing, assuming window is already sized correctly");
+#endif
+ return 0;
+ }
+
+ /* Interactive user-initiated resizing/movement */
+
if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) {
+ /* WM_GETDPISCALEDSIZE should have been called prior, so we can trust the given
+ suggestedRect. */
w = suggestedRect->right - suggestedRect->left;
h = suggestedRect->bottom - suggestedRect->top;
- } else {
+
+#ifdef HIGHDPI_DEBUG
+ SDL_Log("WM_DPICHANGED: using suggestedRect");
+#endif
+ } else {
RECT rect = { 0, 0, data->window->w, data->window->h };
const DWORD style = GetWindowLong(hwnd, GWL_STYLE);
const BOOL menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
@@ -1521,7 +1547,10 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
suggestedRect->left, suggestedRect->top, w, h);
#endif
- data->expected_resize = SDL_TRUE;
+ if (!data->expected_resize) {
+ setExpectedResize = SDL_TRUE;
+ data->expected_resize = SDL_TRUE;
+ }
SetWindowPos(hwnd,
NULL,
suggestedRect->left,
@@ -1529,7 +1558,13 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
w,
h,
SWP_NOZORDER | SWP_NOACTIVATE);
- data->expected_resize = SDL_FALSE;
+ if (setExpectedResize) {
+ /* Only unset data->expected_resize if we set it above.
+ WM_DPICHANGED can happen inside a block of code that sets data->expected_resize,
+ e.g. WIN_SetWindowPositionInternal.
+ */
+ data->expected_resize = SDL_FALSE;
+ }
return 0;
}
break;
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index e2289f3..50420dc 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -46,6 +46,8 @@
#define SWP_NOCOPYBITS 0
#endif
+/* #define HIGHDPI_DEBUG */
+
/* Fake window to help with DirectInput events. */
HWND SDL_HelperWindow = NULL;
static const TCHAR *SDL_HelperWindowClassName = TEXT("SDLHelperWindowInputCatcher");
@@ -114,13 +116,14 @@ GetWindowStyle(SDL_Window * window)
}
static void
-WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current,
- SDL_bool force_ignore_window_dpi)
+WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x, int *y, int *width, int *height, SDL_bool use_current)
{
SDL_WindowData *data = (SDL_WindowData *)window->driverdata;
SDL_VideoData* videodata = SDL_GetVideoDevice() ? SDL_GetVideoDevice()->driverdata : NULL;
RECT rect;
+ UINT dpi;
+ dpi = 96;
rect.left = 0;
rect.top = 0;
rect.right = (use_current ? window->w : window->windowed.w);
@@ -133,33 +136,21 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x
if (WIN_IsPerMonitorV2DPIAware(SDL_GetVideoDevice())) {
/* With per-monitor v2, the window border/titlebar size depend on the DPI, so we need to call AdjustWindowRectExForDpi instead of
AdjustWindowRectEx. */
- UINT dpi;
+
+ UINT unused;
+ RECT screen_rect;
+ HMONITOR mon;
- if (data && !force_ignore_window_dpi) {
- /* The usual case - we have a HWND, so we can look up the DPI to use. */
- dpi = videodata->GetDpiForWindow(data->hwnd);
- } else {
- /* In this case we guess the window DPI based on its rectangle on the screen.
-
- This happens at creation time of an SDL window, before we have a HWND,
- and also in a bug workaround (when force_ignore_window_dpi is SDL_TRUE
- - see WIN_SetWindowFullscreen).
- */
- UINT unused;
- RECT screen_rect;
- HMONITOR mon;
-
- screen_rect.left = (use_current ? window->x : window->windowed.x);
- screen_rect.top = (use_current ? window->y : window->windowed.y);
- screen_rect.right = screen_rect.left + (use_current ? window->w : window->windowed.w);
- screen_rect.bottom = screen_rect.top + (use_current ? window->h : window->windowed.h);
-
- mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST);
-
- /* GetDpiForMonitor docs promise to return the same hdpi / vdpi */
- if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpi, &unused) != S_OK) {
- dpi = 96;
- }
+ screen_rect.left = (use_current ? window->x : window->windowed.x);
+ screen_rect.top = (use_current ? window->y : window->windowed.y);
+ screen_rect.right = screen_rect.left + (use_current ? window->w : window->windowed.w);
+ screen_rect.bottom = screen_rect.top + (use_current ? window->h : window->windowed.h);
+
+ mon = MonitorFromRect(&screen_rect, MONITOR_DEFAULTTONEAREST);
+
+ /* GetDpiForMonitor docs promise to return the same hdpi / vdpi */
+ if (videodata->GetDpiForMonitor(mon, MDT_EFFECTIVE_DPI, &dpi, &unused) != S_OK) {
+ dpi = 96;
}
videodata->AdjustWindowRectExForDpi(&rect, style, menu, 0, dpi);
@@ -172,6 +163,15 @@ WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, BOOL menu, int *x
*y = (use_current ? window->y : window->windowed.y) + rect.top;
*width = (rect.right - rect.left);
*height = (rect.bottom - rect.top);
+
+#ifdef HIGHDPI_DEBUG
+ SDL_Log("WIN_AdjustWindowRectWithStyle: in: %d, %d, %dx%d, returning: %d, %d, %dx%d, used dpi %d for frame calculation",
+ (use_current ? window->x : window->windowed.x),
+ (use_current ? window->y : window->windowed.y),
+ (use_current ? window->w : window->windowed.w),
+ (use_current ? window->h : window->windowed.h),
+ *x, *y, *width, *height, dpi);
+#endif
}
static void
@@ -184,7 +184,7 @@ WIN_AdjustWindowRect(SDL_Window *window, int *x, int *y, int *width, int *height
style = GetWindowLong(hwnd, GWL_STYLE);
menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
- WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current, SDL_FALSE);
+ WIN_AdjustWindowRectWithStyle(window, style, menu, x, y, width, height, use_current);
}
static void
@@ -279,7 +279,9 @@ SetupWindowData(_THIS, SDL_Window * window, HWND hwnd, HWND parent, SDL_bool cre
int x, y;
/* Figure out what the window area will be */
WIN_AdjustWindowRect(window, &x, &y, &w, &h, SDL_FALSE);
+ data->expected_resize = SDL_TRUE;
SetWindowPos(hwnd, HWND_NOTOPMOST, x, y, w, h, SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOACTIVATE);
+ data->expected_resize = SDL_FALSE;
} else {
window->w = w;
window->h = h;
@@ -395,7 +397,7 @@ WIN_CreateWindow(_THIS, SDL_Window * window)
style |= GetWindowStyle(window);
/* Figure out what the window area will be */
- WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE, SDL_FALSE);
+ WIN_AdjustWindowRectWithStyle(window, style, FALSE, &x, &y, &w, &h, SDL_FALSE);
hwnd =
CreateWindow(SDL_Appname, TEXT(""), style, x, y, w, h, parent, NULL,
@@ -580,7 +582,10 @@ WIN_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon)
void
WIN_SetWindowPosition(_THIS, SDL_Window * window)
{
- WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOSIZE | SWP_NOACTIVATE);
+ /* HighDPI support: removed SWP_NOSIZE. If the move results in a DPI change, we need to allow
+ * the window to resize (e.g. AdjustWindowRectExForDpi frame sizes are different).
+ */
+ WIN_SetWindowPositionInternal(_this, window, SWP_NOCOPYBITS | SWP_NOACTIVATE);
}
void
@@ -820,13 +825,7 @@ WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display,
}
menu = (style & WS_CHILDWINDOW) ? FALSE : (GetMenu(hwnd) != NULL);
- /* HighDPI bug workaround - when leaving exclusive fullscreen, the window DPI reported
- by GetDpiForWindow will be wrong. Pass SDL_TRUE for `force_ignore_window_dpi`
- makes us recompute the DPI based on the monitor we are restoring onto.
- Fixes windows shrinking slightly when going from exclusive fullscreen to windowed
- on a HighDPI monitor with scaling.
- */
- WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE, SDL_TRUE);
+ WIN_AdjustWindowRectWithStyle(window, style, menu, &x, &y, &w, &h, SDL_FALSE);
}
SetWindowLong(hwnd, GWL_STYLE, style);
data->expected_resize = SDL_TRUE;
diff --git a/src/video/windows/wmmsg.h b/src/video/windows/wmmsg.h
index bd416eb..86cae1d 100644
--- a/src/video/windows/wmmsg.h
+++ b/src/video/windows/wmmsg.h
@@ -762,7 +762,7 @@ const char *wmtab[] = {
"UNKNOWN (737)",
"UNKNOWN (738)",
"UNKNOWN (739)",
- "UNKNOWN (740)",
+ "WM_GETDPISCALEDSIZE",
"UNKNOWN (741)",
"UNKNOWN (742)",
"UNKNOWN (743)",