#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
#include "../../core/windows/SDL_windows.h"
#include "SDL_windowsvideo.h"
#ifndef SS_EDITCONTROL
#define SS_EDITCONTROL 0x2000
#endif
#ifndef IDOK
#define IDOK 1
#endif
#ifndef IDCANCEL
#define IDCANCEL 2
#endif
#define IDCLOSED 20
#define IDINVALPTRINIT 50
#define IDINVALPTRCOMMAND 51
#define IDINVALPTRSETFOCUS 52
#define IDINVALPTRDLGITEM 53
#define IDBUTTONINDEX0 100
#define DLGITEMTYPEBUTTON 0x0080
#define DLGITEMTYPESTATIC 0x0082
#define MAX_BUTTONS (0xffff - 100)
typedef HRESULT(CALLBACK *PFTASKDIALOGCALLBACK)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LONG_PTR lpRefData);
enum _TASKDIALOG_FLAGS
{
TDF_ENABLE_HYPERLINKS = 0x0001,
TDF_USE_HICON_MAIN = 0x0002,
TDF_USE_HICON_FOOTER = 0x0004,
TDF_ALLOW_DIALOG_CANCELLATION = 0x0008,
TDF_USE_COMMAND_LINKS = 0x0010,
TDF_USE_COMMAND_LINKS_NO_ICON = 0x0020,
TDF_EXPAND_FOOTER_AREA = 0x0040,
TDF_EXPANDED_BY_DEFAULT = 0x0080,
TDF_VERIFICATION_FLAG_CHECKED = 0x0100,
TDF_SHOW_PROGRESS_BAR = 0x0200,
TDF_SHOW_MARQUEE_PROGRESS_BAR = 0x0400,
TDF_CALLBACK_TIMER = 0x0800,
TDF_POSITION_RELATIVE_TO_WINDOW = 0x1000,
TDF_RTL_LAYOUT = 0x2000,
TDF_NO_DEFAULT_RADIO_BUTTON = 0x4000,
TDF_CAN_BE_MINIMIZED = 0x8000,
TDF_NO_SET_FOREGROUND = 0x00010000, TDF_SIZE_TO_CONTENT = 0x01000000 };
typedef int TASKDIALOG_FLAGS;
typedef enum _TASKDIALOG_MESSAGES
{
TDM_NAVIGATE_PAGE = WM_USER + 101,
TDM_CLICK_BUTTON = WM_USER + 102, TDM_SET_MARQUEE_PROGRESS_BAR = WM_USER + 103, TDM_SET_PROGRESS_BAR_STATE = WM_USER + 104, TDM_SET_PROGRESS_BAR_RANGE = WM_USER + 105, TDM_SET_PROGRESS_BAR_POS = WM_USER + 106, TDM_SET_PROGRESS_BAR_MARQUEE = WM_USER + 107, TDM_SET_ELEMENT_TEXT = WM_USER + 108, TDM_CLICK_RADIO_BUTTON = WM_USER + 110, TDM_ENABLE_BUTTON = WM_USER + 111, TDM_ENABLE_RADIO_BUTTON = WM_USER + 112, TDM_CLICK_VERIFICATION = WM_USER + 113, TDM_UPDATE_ELEMENT_TEXT = WM_USER + 114, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE = WM_USER + 115, TDM_UPDATE_ICON = WM_USER + 116 } TASKDIALOG_MESSAGES;
typedef enum _TASKDIALOG_NOTIFICATIONS
{
TDN_CREATED = 0,
TDN_NAVIGATED = 1,
TDN_BUTTON_CLICKED = 2, TDN_HYPERLINK_CLICKED = 3, TDN_TIMER = 4, TDN_DESTROYED = 5,
TDN_RADIO_BUTTON_CLICKED = 6, TDN_DIALOG_CONSTRUCTED = 7,
TDN_VERIFICATION_CLICKED = 8, TDN_HELP = 9,
TDN_EXPANDO_BUTTON_CLICKED = 10 } TASKDIALOG_NOTIFICATIONS;
typedef enum _TASKDIALOG_ELEMENTS
{
TDE_CONTENT,
TDE_EXPANDED_INFORMATION,
TDE_FOOTER,
TDE_MAIN_INSTRUCTION
} TASKDIALOG_ELEMENTS;
typedef enum _TASKDIALOG_ICON_ELEMENTS
{
TDIE_ICON_MAIN,
TDIE_ICON_FOOTER
} TASKDIALOG_ICON_ELEMENTS;
#define TD_WARNING_ICON MAKEINTRESOURCEW(-1)
#define TD_ERROR_ICON MAKEINTRESOURCEW(-2)
#define TD_INFORMATION_ICON MAKEINTRESOURCEW(-3)
#define TD_SHIELD_ICON MAKEINTRESOURCEW(-4)
enum _TASKDIALOG_COMMON_BUTTON_FLAGS
{
TDCBF_OK_BUTTON = 0x0001, TDCBF_YES_BUTTON = 0x0002, TDCBF_NO_BUTTON = 0x0004, TDCBF_CANCEL_BUTTON = 0x0008, TDCBF_RETRY_BUTTON = 0x0010, TDCBF_CLOSE_BUTTON = 0x0020 };
typedef int TASKDIALOG_COMMON_BUTTON_FLAGS;
#pragma pack(push, 1)
typedef struct _TASKDIALOG_BUTTON
{
int nButtonID;
PCWSTR pszButtonText;
} TASKDIALOG_BUTTON;
typedef struct _TASKDIALOGCONFIG
{
UINT cbSize;
HWND hwndParent; HINSTANCE hInstance; TASKDIALOG_FLAGS dwFlags; TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons; PCWSTR pszWindowTitle; union
{
HICON hMainIcon;
PCWSTR pszMainIcon;
} ;
PCWSTR pszMainInstruction;
PCWSTR pszContent;
UINT cButtons;
const TASKDIALOG_BUTTON *pButtons;
int nDefaultButton;
UINT cRadioButtons;
const TASKDIALOG_BUTTON *pRadioButtons;
int nDefaultRadioButton;
PCWSTR pszVerificationText;
PCWSTR pszExpandedInformation;
PCWSTR pszExpandedControlText;
PCWSTR pszCollapsedControlText;
union
{
HICON hFooterIcon;
PCWSTR pszFooterIcon;
} ;
PCWSTR pszFooter;
PFTASKDIALOGCALLBACK pfCallback;
LONG_PTR lpCallbackData;
UINT cxWidth; } TASKDIALOGCONFIG;
typedef struct
{
WORD dlgVer;
WORD signature;
DWORD helpID;
DWORD exStyle;
DWORD style;
WORD cDlgItems;
short x;
short y;
short cx;
short cy;
} DLGTEMPLATEEX;
typedef struct
{
DWORD helpID;
DWORD exStyle;
DWORD style;
short x;
short y;
short cx;
short cy;
DWORD id;
} DLGITEMTEMPLATEEX;
#pragma pack(pop)
typedef struct
{
DLGTEMPLATEEX *lpDialog;
void *data;
size_t size;
size_t used;
WORD numbuttons;
} WIN_DialogData;
static bool GetButtonIndex(const SDL_MessageBoxData *messageboxdata, SDL_MessageBoxButtonFlags flags, size_t *i)
{
for (*i = 0; *i < (size_t)messageboxdata->numbuttons; ++*i) {
if (messageboxdata->buttons[*i].flags & flags) {
return true;
}
}
return false;
}
static INT_PTR CALLBACK MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
const SDL_MessageBoxData *messageboxdata;
size_t buttonindex;
switch (iMessage) {
case WM_INITDIALOG:
if (lParam == 0) {
EndDialog(hDlg, IDINVALPTRINIT);
return TRUE;
}
messageboxdata = (const SDL_MessageBoxData *)lParam;
SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);
if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
HWND buttonctl = GetDlgItem(hDlg, (int)(IDBUTTONINDEX0 + buttonindex));
if (!buttonctl) {
EndDialog(hDlg, IDINVALPTRDLGITEM);
}
PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)buttonctl, TRUE);
} else {
SetFocus(hDlg);
}
return FALSE;
case WM_SETFOCUS:
messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
if (!messageboxdata) {
EndDialog(hDlg, IDINVALPTRSETFOCUS);
return TRUE;
}
if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
return FALSE;
}
return TRUE;
case WM_COMMAND:
messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
if (!messageboxdata) {
EndDialog(hDlg, IDINVALPTRCOMMAND);
return TRUE;
}
if (wParam == IDOK) {
if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
}
} else if (wParam == IDCANCEL) {
if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, &buttonindex)) {
EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
} else {
EndDialog(hDlg, IDCLOSED);
}
} else if (wParam >= IDBUTTONINDEX0 && (int)wParam - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
EndDialog(hDlg, wParam);
}
return TRUE;
default:
break;
}
return FALSE;
}
static bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
{
const size_t sizestep = 0x10000;
size_t size = dialog->size;
if (size == 0) {
size = 0x1000;
if (SIZE_MAX - sizestep < space) {
size = space;
} else if (space > size) {
size = (space + sizestep) & ~(sizestep - 1);
}
} else if (SIZE_MAX - dialog->used < space) {
SDL_OutOfMemory();
return false;
} else if (SIZE_MAX - (dialog->used + space) < sizestep) {
size = dialog->used + space;
} else if (size < dialog->used + space) {
size = dialog->used + space;
size += sizestep - size % sizestep;
}
if (size > dialog->size) {
void *data = SDL_realloc(dialog->data, size);
if (!data) {
return false;
}
dialog->data = data;
dialog->size = size;
dialog->lpDialog = (DLGTEMPLATEEX *)dialog->data;
}
return true;
}
static bool AlignDialogData(WIN_DialogData *dialog, size_t size)
{
size_t padding = (dialog->used % size);
if (!ExpandDialogSpace(dialog, padding)) {
return false;
}
dialog->used += padding;
return true;
}
static bool AddDialogData(WIN_DialogData *dialog, const void *data, size_t size)
{
if (!ExpandDialogSpace(dialog, size)) {
return false;
}
SDL_memcpy((Uint8 *)dialog->data + dialog->used, data, size);
dialog->used += size;
return true;
}
static bool AddDialogString(WIN_DialogData *dialog, const char *string)
{
WCHAR *wstring;
WCHAR *p;
size_t count;
bool status;
if (!string) {
string = "";
}
wstring = WIN_UTF8ToStringW(string);
if (!wstring) {
return false;
}
count = 0;
for (p = wstring; *p; ++p) {
++count;
}
++count;
status = AddDialogData(dialog, wstring, count * sizeof(WCHAR));
SDL_free(wstring);
return status;
}
static int s_BaseUnitsX;
static int s_BaseUnitsY;
static void Vec2ToDLU(short *x, short *y)
{
SDL_assert(s_BaseUnitsX != 0);
*x = (short)MulDiv(*x, 4, s_BaseUnitsX);
*y = (short)MulDiv(*y, 8, s_BaseUnitsY);
}
static bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption, WORD ordinal)
{
DLGITEMTEMPLATEEX item;
WORD marker = 0xFFFF;
WORD extraData = 0;
SDL_zero(item);
item.style = style;
item.exStyle = exStyle;
item.x = (short)x;
item.y = (short)y;
item.cx = (short)w;
item.cy = (short)h;
item.id = id;
Vec2ToDLU(&item.x, &item.y);
Vec2ToDLU(&item.cx, &item.cy);
if (!AlignDialogData(dialog, sizeof(DWORD))) {
return false;
}
if (!AddDialogData(dialog, &item, sizeof(item))) {
return false;
}
if (!AddDialogData(dialog, &marker, sizeof(marker))) {
return false;
}
if (!AddDialogData(dialog, &type, sizeof(type))) {
return false;
}
if (type == DLGITEMTYPEBUTTON || (type == DLGITEMTYPESTATIC && caption)) {
if (!AddDialogString(dialog, caption)) {
return false;
}
} else {
if (!AddDialogData(dialog, &marker, sizeof(marker))) {
return false;
}
if (!AddDialogData(dialog, &ordinal, sizeof(ordinal))) {
return false;
}
}
if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
return false;
}
if (type == DLGITEMTYPEBUTTON) {
dialog->numbuttons++;
}
++dialog->lpDialog->cDlgItems;
return true;
}
static bool AddDialogStaticText(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
{
DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL | WS_GROUP;
return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -1, text, 0);
}
static bool AddDialogStaticIcon(WIN_DialogData *dialog, int x, int y, int w, int h, Uint16 ordinal)
{
DWORD style = WS_VISIBLE | WS_CHILD | SS_ICON | WS_GROUP;
return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -2, NULL, ordinal);
}
static bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, bool isDefault)
{
DWORD style = WS_VISIBLE | WS_CHILD | WS_TABSTOP;
if (isDefault) {
style |= BS_DEFPUSHBUTTON;
} else {
style |= BS_PUSHBUTTON;
}
if (dialog->numbuttons == 0) {
style |= WS_GROUP;
}
return AddDialogControl(dialog, DLGITEMTYPEBUTTON, style, 0, x, y, w, h, id, text, 0);
}
static void FreeDialogData(WIN_DialogData *dialog)
{
SDL_free(dialog->data);
SDL_free(dialog);
}
static WIN_DialogData *CreateDialogData(int w, int h, const char *caption)
{
WIN_DialogData *dialog;
DLGTEMPLATEEX dialogTemplate;
WORD WordToPass;
SDL_zero(dialogTemplate);
dialogTemplate.dlgVer = 1;
dialogTemplate.signature = 0xffff;
dialogTemplate.style = (WS_CAPTION | DS_CENTER | DS_SHELLFONT);
dialogTemplate.x = 0;
dialogTemplate.y = 0;
dialogTemplate.cx = (short)w;
dialogTemplate.cy = (short)h;
Vec2ToDLU(&dialogTemplate.cx, &dialogTemplate.cy);
dialog = (WIN_DialogData *)SDL_calloc(1, sizeof(*dialog));
if (!dialog) {
return NULL;
}
if (!AddDialogData(dialog, &dialogTemplate, sizeof(dialogTemplate))) {
FreeDialogData(dialog);
return NULL;
}
WordToPass = 0;
if (!AddDialogData(dialog, &WordToPass, 2)) {
FreeDialogData(dialog);
return NULL;
}
if (!AddDialogData(dialog, &WordToPass, 2)) {
FreeDialogData(dialog);
return NULL;
}
if (!AddDialogString(dialog, caption)) {
FreeDialogData(dialog);
return NULL;
}
{
BYTE ToPass;
NONCLIENTMETRICSA NCM;
NCM.cbSize = sizeof(NCM);
SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
{
HDC ScreenDC = GetDC(NULL);
int LogicalPixelsY = GetDeviceCaps(ScreenDC, LOGPIXELSY);
if (!LogicalPixelsY) {
LogicalPixelsY = 72; }
WordToPass = (WORD)(-72 * NCM.lfMessageFont.lfHeight / LogicalPixelsY);
ReleaseDC(NULL, ScreenDC);
}
if (!AddDialogData(dialog, &WordToPass, 2)) {
FreeDialogData(dialog);
return NULL;
}
WordToPass = (WORD)NCM.lfMessageFont.lfWeight;
if (!AddDialogData(dialog, &WordToPass, 2)) {
FreeDialogData(dialog);
return NULL;
}
ToPass = NCM.lfMessageFont.lfItalic;
if (!AddDialogData(dialog, &ToPass, 1)) {
FreeDialogData(dialog);
return NULL;
}
ToPass = NCM.lfMessageFont.lfCharSet;
if (!AddDialogData(dialog, &ToPass, 1)) {
FreeDialogData(dialog);
return NULL;
}
if (!AddDialogString(dialog, NCM.lfMessageFont.lfFaceName)) {
FreeDialogData(dialog);
return NULL;
}
}
return dialog;
}
static const char *EscapeAmpersands(char **dst, size_t *dstlen, const char *src)
{
char *newdst;
size_t ampcount = 0;
size_t srclen = 0;
if (!src) {
return NULL;
}
while (src[srclen]) {
if (src[srclen] == '&') {
ampcount++;
}
srclen++;
}
srclen++;
if (ampcount == 0) {
return src;
}
if (SIZE_MAX - srclen < ampcount) {
return NULL;
}
if (!*dst || *dstlen < srclen + ampcount) {
size_t extraspace = SIZE_MAX - (srclen + ampcount);
if (extraspace > 512) {
extraspace = 512;
}
*dstlen = srclen + ampcount + extraspace;
SDL_free(*dst);
*dst = NULL;
newdst = (char *)SDL_malloc(*dstlen);
if (!newdst) {
return NULL;
}
*dst = newdst;
} else {
newdst = *dst;
}
while (srclen--) {
if (*src == '&') {
*newdst++ = '&';
}
*newdst++ = *src++;
}
return *dst;
}
static float WIN_GetContentScale(void)
{
int dpi = 0;
#if 0#endif
if (dpi == 0) {
HDC hdc = GetDC(NULL);
if (hdc) {
dpi = GetDeviceCaps(hdc, LOGPIXELSX);
ReleaseDC(NULL, hdc);
}
}
if (dpi == 0) {
dpi = USER_DEFAULT_SCREEN_DPI;
}
return dpi / (float)USER_DEFAULT_SCREEN_DPI;
}
static bool WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
WIN_DialogData *dialog;
int i, x, y;
HFONT DialogFont;
SIZE Size;
RECT TextSize;
wchar_t *wmessage;
TEXTMETRIC TM;
HDC FontDC;
INT_PTR rc;
char *ampescape = NULL;
size_t ampescapesize = 0;
Uint16 defbuttoncount = 0;
Uint16 icon = 0;
bool result;
HWND ParentWindow = NULL;
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 = (int)SDL_roundf(20 * scale);
if (messageboxdata->numbuttons > MAX_BUTTONS) {
return SDL_SetError("Number of buttons exceeds limit of %d", MAX_BUTTONS);
}
switch (messageboxdata->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) {
case SDL_MESSAGEBOX_ERROR:
icon = (Uint16)(size_t)IDI_ERROR;
break;
case SDL_MESSAGEBOX_WARNING:
icon = (Uint16)(size_t)IDI_WARNING;
break;
case SDL_MESSAGEBOX_INFORMATION:
icon = (Uint16)(size_t)IDI_INFORMATION;
break;
}
FontDC = CreateCompatibleDC(0);
{
LOGFONT lf;
NONCLIENTMETRICS NCM;
NCM.cbSize = sizeof(NCM);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
lf = NCM.lfMessageFont;
DialogFont = CreateFontIndirect(&lf);
}
SelectObject(FontDC, DialogFont);
{
GetTextMetrics(FontDC, &TM);
{
SIZE extent;
GetTextExtentPoint32A(FontDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &extent);
s_BaseUnitsX = (extent.cx / 26 + 1) / 2;
}
s_BaseUnitsY = TM.tmHeight;
}
wmessage = WIN_UTF8ToStringW(messageboxdata->message);
SDL_zero(TextSize);
DrawTextW(FontDC, wmessage, -1, &TextSize, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EDITCONTROL);
TextSize.left += TextMargin;
TextSize.right += TextMargin + 2;
TextSize.top += TextMargin;
TextSize.bottom += TextMargin + 2;
DeleteDC(FontDC);
SDL_free(wmessage);
Size.cx = TextSize.right - TextSize.left;
Size.cy = TextSize.bottom - TextSize.top;
Size.cx += TextMargin * 2;
Size.cy += TextMargin * 2;
if (icon) {
Size.cx += IconMargin + IconWidth;
TextSize.left += IconMargin + IconWidth;
TextSize.right += IconMargin + IconWidth;
}
if (Size.cx < (LONG)messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin) {
Size.cx = (LONG)messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
}
if (icon && Size.cy < (LONG)IconMargin * 2 + IconHeight) {
Size.cy = (LONG)IconMargin * 2 + IconHeight;
}
Size.cy += ButtonHeight + TextMargin;
dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title);
if (!dialog) {
return false;
}
if (icon && !AddDialogStaticIcon(dialog, IconMargin, IconMargin, IconWidth, IconHeight, icon)) {
FreeDialogData(dialog);
return false;
}
if (!AddDialogStaticText(dialog, TextSize.left, TextSize.top, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
FreeDialogData(dialog);
return false;
}
x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons;
y = Size.cy - ButtonHeight - ButtonMargin;
for (i = 0; i < messageboxdata->numbuttons; i++) {
bool isdefault = false;
const char *buttontext;
const SDL_MessageBoxButtonData *sdlButton;
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
sdlButton = &messageboxdata->buttons[i];
} else {
sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
}
if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
defbuttoncount++;
if (defbuttoncount == 1) {
isdefault = true;
}
}
buttontext = EscapeAmpersands(&escape, &escapesize, sdlButton->text);
if (!buttontext || !AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttontext, IDBUTTONINDEX0 + (int)(sdlButton - messageboxdata->buttons), isdefault)) {
FreeDialogData(dialog);
SDL_free(ampescape);
return false;
}
x += ButtonWidth + ButtonMargin;
}
SDL_free(ampescape);
if (messageboxdata->window) {
ParentWindow = messageboxdata->window->internal->hwnd;
}
rc = DialogBoxIndirectParam(NULL, (DLGTEMPLATE *)dialog->lpDialog, ParentWindow, MessageBoxDialogProc, (LPARAM)messageboxdata);
if (rc >= IDBUTTONINDEX0 && rc - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
*buttonID = messageboxdata->buttons[rc - IDBUTTONINDEX0].buttonID;
result = true;
} else if (rc == IDCLOSED) {
result = true;
*buttonID = -1;
} else {
if (rc == 0) {
SDL_SetError("Invalid parent window handle");
} else if (rc == -1) {
SDL_SetError("The message box encountered an error.");
} else if (rc == IDINVALPTRINIT || rc == IDINVALPTRSETFOCUS || rc == IDINVALPTRCOMMAND) {
SDL_SetError("Invalid message box pointer in dialog procedure");
} else if (rc == IDINVALPTRDLGITEM) {
SDL_SetError("Couldn't find dialog control of the default enter-key button");
} else {
SDL_SetError("An unknown error occurred");
}
result = false;
}
FreeDialogData(dialog);
return result;
}
typedef HRESULT (FAR WINAPI *TASKDIALOGINDIRECTPROC)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
bool WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
HWND ParentWindow = NULL;
wchar_t *wmessage;
wchar_t *wtitle;
TASKDIALOGCONFIG TaskConfig;
TASKDIALOG_BUTTON *pButtons;
TASKDIALOG_BUTTON *pButton;
HMODULE hComctl32;
TASKDIALOGINDIRECTPROC pTaskDialogIndirect;
HRESULT hr;
char *ampescape = NULL;
size_t ampescapesize = 0;
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 *pfnSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
pfnSetThreadDpiAwarenessContext pSetThreadDpiAwarenessContext = (pfnSetThreadDpiAwarenessContext)GetProcAddress(hUser32, "SetThreadDpiAwarenessContext");
DPI_AWARENESS_CONTEXT previous_context = DPI_AWARENESS_CONTEXT_UNAWARE;
if (pSetThreadDpiAwarenessContext) {
previous_context = pSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
hComctl32 = LoadLibrary(TEXT("comctl32.dll"));
if (!hComctl32) {
result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
goto done;
}
pTaskDialogIndirect = (TASKDIALOGINDIRECTPROC)GetProcAddress(hComctl32, "TaskDialogIndirect");
if (!pTaskDialogIndirect) {
FreeLibrary(hComctl32);
result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
goto done;
}
if (messageboxdata->window) {
ParentWindow = messageboxdata->window->internal->hwnd;
}
wmessage = WIN_UTF8ToStringW(messageboxdata->message);
wtitle = WIN_UTF8ToStringW(messageboxdata->title);
SDL_zero(TaskConfig);
TaskConfig.cbSize = sizeof(TASKDIALOGCONFIG);
TaskConfig.hwndParent = ParentWindow;
TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT;
TaskConfig.pszWindowTitle = wtitle;
if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
TaskConfig.pszMainIcon = TD_ERROR_ICON;
} else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
TaskConfig.pszMainIcon = TD_WARNING_ICON;
} else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) {
TaskConfig.pszMainIcon = TD_INFORMATION_ICON;
} else {
TaskConfig.pszMainIcon = NULL;
}
TaskConfig.pszContent = wmessage;
TaskConfig.cButtons = messageboxdata->numbuttons;
pButtons = (TASKDIALOG_BUTTON *)SDL_malloc(sizeof(TASKDIALOG_BUTTON) * messageboxdata->numbuttons);
TaskConfig.nDefaultButton = 0;
nCancelButton = 0;
for (i = 0; i < messageboxdata->numbuttons; i++) {
const char *buttontext;
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
pButton = &pButtons[i];
} else {
pButton = &pButtons[messageboxdata->numbuttons - 1 - i];
}
if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
nCancelButton = messageboxdata->buttons[i].buttonID;
pButton->nButtonID = IDCANCEL;
} else {
pButton->nButtonID = IDBUTTONINDEX0 + i;
}
buttontext = EscapeAmpersands(&escape, &escapesize, messageboxdata->buttons[i].text);
if (!buttontext) {
int j;
FreeLibrary(hComctl32);
SDL_free(ampescape);
SDL_free(wmessage);
SDL_free(wtitle);
for (j = 0; j < i; j++) {
SDL_free((wchar_t *)pButtons[j].pszButtonText);
}
SDL_free(pButtons);
return false;
}
pButton->pszButtonText = WIN_UTF8ToStringW(buttontext);
if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
TaskConfig.nDefaultButton = pButton->nButtonID;
}
}
TaskConfig.pButtons = pButtons;
hr = pTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL);
FreeLibrary(hComctl32);
SDL_free(ampescape);
SDL_free(wmessage);
SDL_free(wtitle);
for (i = 0; i < messageboxdata->numbuttons; i++) {
SDL_free((wchar_t *)pButtons[i].pszButtonText);
}
SDL_free(pButtons);
if (SUCCEEDED(hr)) {
if (nButton == IDCANCEL) {
*buttonID = nCancelButton;
} else if (nButton >= IDBUTTONINDEX0 && nButton < IDBUTTONINDEX0 + messageboxdata->numbuttons) {
*buttonID = messageboxdata->buttons[nButton - IDBUTTONINDEX0].buttonID;
} else {
*buttonID = -1;
}
result = true;
} else {
result = WIN_ShowOldMessageBox(messageboxdata, buttonID);
}
done:
if (pSetThreadDpiAwarenessContext) {
pSetThreadDpiAwarenessContext(previous_context);
}
return result;
}
#endif