From 49663bfb589324d7983bd64cc037080bb212b0b9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 3 Jan 2025 08:44:15 -0800
Subject: [PATCH] Make Windows dialogs DPI aware
Fixes https://github.com/libsdl-org/SDL/issues/4775
Fixes https://github.com/libsdl-org/SDL/issues/9691
---
src/video/windows/SDL_windowsmessagebox.c | 65 +++++++++++++++++++----
1 file changed, 54 insertions(+), 11 deletions(-)
diff --git a/src/video/windows/SDL_windowsmessagebox.c b/src/video/windows/SDL_windowsmessagebox.c
index 9520e5579739d..0162d162089c4 100644
--- a/src/video/windows/SDL_windowsmessagebox.c
+++ b/src/video/windows/SDL_windowsmessagebox.c
@@ -670,6 +670,31 @@ static const char *EscapeAmpersands(char **dst, size_t *dstlen, const char *src)
return *dst;
}
+static float WIN_GetContentScale(void)
+{
+ int dpi = 0;
+
+#if 0 // We don't know what monitor the dialog will be shown on
+ UINT hdpi_uint, vdpi_uint;
+ if (GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi_uint, &vdpi_uint) == S_OK) {
+ dpi = (int)hdpi_uint;
+ }
+#endif
+ if (dpi == 0) {
+ // Window 8.0 and below: same DPI for all monitors
+ HDC hdc = GetDC(NULL);
+ if (hdc) {
+ dpi = GetDeviceCaps(hdc, LOGPIXELSX);
+ ReleaseDC(NULL, hdc);
+ }
+ }
+ if (dpi == 0) {
+ // Safe default
+ dpi = USER_DEFAULT_SCREEN_DPI;
+ }
+ return dpi / (float)USER_DEFAULT_SCREEN_DPI;
+}
+
// This function is called if a Task Dialog is unsupported.
static bool WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
@@ -690,13 +715,14 @@ static bool WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int
HWND ParentWindow = NULL;
- const int ButtonWidth = 88;
- const int ButtonHeight = 26;
- const int TextMargin = 16;
- const int ButtonMargin = 12;
+ const float scale = WIN_GetContentScale();
+ const int ButtonWidth = (int)SDL_roundf(88 * scale);
+ const int ButtonHeight = (int)SDL_roundf(26 * scale);
+ const int TextMargin = (int)SDL_roundf(16 * scale);
+ const int ButtonMargin = (int)SDL_roundf(12 * scale);
const int IconWidth = GetSystemMetrics(SM_CXICON);
const int IconHeight = GetSystemMetrics(SM_CYICON);
- const int IconMargin = 20;
+ const int IconMargin = (int)SDL_roundf(20 * scale);
if (messageboxdata->numbuttons > MAX_BUTTONS) {
return SDL_SetError("Number of buttons exceeds limit of %d", MAX_BUTTONS);
@@ -925,15 +951,25 @@ bool WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
int nButton;
int nCancelButton;
int i;
+ bool result = false;
if (SIZE_MAX / sizeof(TASKDIALOG_BUTTON) < messageboxdata->numbuttons) {
return SDL_OutOfMemory();
}
+ HMODULE hUser32 = GetModuleHandle(TEXT("user32.dll"));
+ typedef DPI_AWARENESS_CONTEXT (WINAPI * SetThreadDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT);
+ SetThreadDpiAwarenessContext_t SetThreadDpiAwarenessContextFunc = (SetThreadDpiAwarenessContext_t)GetProcAddress(hUser32, "SetThreadDpiAwarenessContext");
+ DPI_AWARENESS_CONTEXT previous_context;
+ if (SetThreadDpiAwarenessContextFunc) {
+ previous_context = SetThreadDpiAwarenessContextFunc(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ }
+
// If we cannot load comctl32.dll use the old messagebox!
hComctl32 = LoadLibrary(TEXT("comctl32.dll"));
if (!hComctl32) {
- return WIN_ShowOldMessageBox(messageboxdata, buttonID);
+ result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
+ goto done;
}
/* If TaskDialogIndirect doesn't exist use the old messagebox!
@@ -941,12 +977,13 @@ bool WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
The manifest file in the application may require targeting version 6 of comctl32.dll, even
when we use LoadLibrary here!
If you don't want to bother with manifests, put this #pragma in your app's source code somewhere:
- pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+ #pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
*/
pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC)GetProcAddress(hComctl32, "TaskDialogIndirect");
if (!pfnTaskDialogIndirect) {
FreeLibrary(hComctl32);
- return WIN_ShowOldMessageBox(messageboxdata, buttonID);
+ result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
+ goto done;
}
/* If we have a parent window, get the Instance and HWND for them
@@ -1033,11 +1070,17 @@ bool WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
} else {
*buttonID = -1;
}
- return true;
+ result = true;
+ } else {
+ // We failed showing the Task Dialog, use the old message box!
+ result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
}
- // We failed showing the Task Dialog, use the old message box!
- return WIN_ShowOldMessageBox(messageboxdata, buttonID);
+done:
+ if (SetThreadDpiAwarenessContextFunc) {
+ SetThreadDpiAwarenessContextFunc(previous_context);
+ }
+ return result;
}
#endif // SDL_VIDEO_DRIVER_WINDOWS