Atlas - SDL_windowsmessagebox.c
Home / ext / SDL2 / src / video / windows Lines: 1 | Size: 28693 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_VIDEO_DRIVER_WINDOWS 24 25#ifdef HAVE_LIMITS_H 26#include <limits.h> 27#else 28#ifndef SIZE_MAX 29#define SIZE_MAX ((size_t)-1) 30#endif 31#endif 32 33#include "../../core/windows/SDL_windows.h" 34 35#include "SDL_assert.h" 36#include "SDL_windowsvideo.h" 37#include "SDL_windowstaskdialog.h" 38 39#ifndef SS_EDITCONTROL 40#define SS_EDITCONTROL 0x2000 41#endif 42 43#ifndef IDOK 44#define IDOK 1 45#endif 46 47#ifndef IDCANCEL 48#define IDCANCEL 2 49#endif 50 51/* Custom dialog return codes */ 52#define IDCLOSED 20 53#define IDINVALPTRINIT 50 54#define IDINVALPTRCOMMAND 51 55#define IDINVALPTRSETFOCUS 52 56#define IDINVALPTRDLGITEM 53 57/* First button ID */ 58#define IDBUTTONINDEX0 100 59 60#define DLGITEMTYPEBUTTON 0x0080 61#define DLGITEMTYPESTATIC 0x0082 62 63/* Windows only sends the lower 16 bits of the control ID when a button 64 * gets clicked. There are also some predefined and custom IDs that lower 65 * the available number further. 2^16 - 101 buttons should be enough for 66 * everyone, no need to make the code more complex. 67 */ 68#define MAX_BUTTONS (0xffff - 100) 69 70 71/* Display a Windows message box */ 72 73#pragma pack(push, 1) 74 75typedef struct 76{ 77 WORD dlgVer; 78 WORD signature; 79 DWORD helpID; 80 DWORD exStyle; 81 DWORD style; 82 WORD cDlgItems; 83 short x; 84 short y; 85 short cx; 86 short cy; 87} DLGTEMPLATEEX; 88 89typedef struct 90{ 91 DWORD helpID; 92 DWORD exStyle; 93 DWORD style; 94 short x; 95 short y; 96 short cx; 97 short cy; 98 DWORD id; 99} DLGITEMTEMPLATEEX; 100 101#pragma pack(pop) 102 103typedef struct 104{ 105 DLGTEMPLATEEX* lpDialog; 106 Uint8 *data; 107 size_t size; 108 size_t used; 109 WORD numbuttons; 110} WIN_DialogData; 111 112static SDL_bool GetButtonIndex(const SDL_MessageBoxData *messageboxdata, Uint32 flags, size_t *i) 113{ 114 for (*i = 0; *i < (size_t)messageboxdata->numbuttons; ++*i) { 115 if (messageboxdata->buttons[*i].flags & flags) { 116 return SDL_TRUE; 117 } 118 } 119 return SDL_FALSE; 120} 121 122static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam) 123{ 124 const SDL_MessageBoxData *messageboxdata; 125 size_t buttonindex; 126 127 switch ( iMessage ) { 128 case WM_INITDIALOG: 129 if (lParam == 0) { 130 EndDialog(hDlg, IDINVALPTRINIT); 131 return TRUE; 132 } 133 messageboxdata = (const SDL_MessageBoxData *)lParam; 134 SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); 135 136 if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) { 137 /* Focus on the first default return-key button */ 138 HWND buttonctl = GetDlgItem(hDlg, (int)(IDBUTTONINDEX0 + buttonindex)); 139 if (buttonctl == NULL) { 140 EndDialog(hDlg, IDINVALPTRDLGITEM); 141 } 142 PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)buttonctl, TRUE); 143 } else { 144 /* Give the focus to the dialog window instead */ 145 SetFocus(hDlg); 146 } 147 return FALSE; 148 case WM_SETFOCUS: 149 messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA); 150 if (messageboxdata == NULL) { 151 EndDialog(hDlg, IDINVALPTRSETFOCUS); 152 return TRUE; 153 } 154 155 /* Let the default button be focused if there is one. Otherwise, prevent any initial focus. */ 156 if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) { 157 return FALSE; 158 } 159 return TRUE; 160 case WM_COMMAND: 161 messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA); 162 if (messageboxdata == NULL) { 163 EndDialog(hDlg, IDINVALPTRCOMMAND); 164 return TRUE; 165 } 166 167 /* Return the ID of the button that was pushed */ 168 if (wParam == IDOK) { 169 if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) { 170 EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex); 171 } 172 } else if (wParam == IDCANCEL) { 173 if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, &buttonindex)) { 174 EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex); 175 } else { 176 /* Closing of window was requested by user or system. It would be rude not to comply. */ 177 EndDialog(hDlg, IDCLOSED); 178 } 179 } else if (wParam >= IDBUTTONINDEX0 && (int)wParam - IDBUTTONINDEX0 < messageboxdata->numbuttons) { 180 EndDialog(hDlg, wParam); 181 } 182 return TRUE; 183 184 default: 185 break; 186 } 187 return FALSE; 188} 189 190static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space) 191{ 192 /* Growing memory in 64 KiB steps. */ 193 const size_t sizestep = 0x10000; 194 size_t size = dialog->size; 195 196 if (size == 0) { 197 /* Start with 4 KiB or a multiple of 64 KiB to fit the data. */ 198 size = 0x1000; 199 if (SIZE_MAX - sizestep < space) { 200 size = space; 201 } else if (space > size) { 202 size = (space + sizestep) & ~(sizestep - 1); 203 } 204 } else if (SIZE_MAX - dialog->used < space) { 205 SDL_OutOfMemory(); 206 return SDL_FALSE; 207 } else if (SIZE_MAX - (dialog->used + space) < sizestep) { 208 /* Close to the maximum. */ 209 size = dialog->used + space; 210 } else if (size < dialog->used + space) { 211 /* Round up to the next 64 KiB block. */ 212 size = dialog->used + space; 213 size += sizestep - size % sizestep; 214 } 215 216 if (size > dialog->size) { 217 void *data = SDL_realloc(dialog->data, size); 218 if (!data) { 219 SDL_OutOfMemory(); 220 return SDL_FALSE; 221 } 222 dialog->data = data; 223 dialog->size = size; 224 dialog->lpDialog = (DLGTEMPLATEEX*)dialog->data; 225 } 226 return SDL_TRUE; 227} 228 229static SDL_bool AlignDialogData(WIN_DialogData *dialog, size_t size) 230{ 231 size_t padding = (dialog->used % size); 232 233 if (!ExpandDialogSpace(dialog, padding)) { 234 return SDL_FALSE; 235 } 236 237 dialog->used += padding; 238 239 return SDL_TRUE; 240} 241 242static SDL_bool AddDialogData(WIN_DialogData *dialog, const void *data, size_t size) 243{ 244 if (!ExpandDialogSpace(dialog, size)) { 245 return SDL_FALSE; 246 } 247 248 SDL_memcpy(dialog->data+dialog->used, data, size); 249 dialog->used += size; 250 251 return SDL_TRUE; 252} 253 254static SDL_bool AddDialogString(WIN_DialogData *dialog, const char *string) 255{ 256 WCHAR *wstring; 257 WCHAR *p; 258 size_t count; 259 SDL_bool status; 260 261 if (!string) { 262 string = ""; 263 } 264 265 wstring = WIN_UTF8ToString(string); 266 if (!wstring) { 267 return SDL_FALSE; 268 } 269 270 /* Find out how many characters we have, including null terminator */ 271 count = 0; 272 for (p = wstring; *p; ++p) { 273 ++count; 274 } 275 ++count; 276 277 status = AddDialogData(dialog, wstring, count*sizeof(WCHAR)); 278 SDL_free(wstring); 279 return status; 280} 281 282static int s_BaseUnitsX; 283static int s_BaseUnitsY; 284static void Vec2ToDLU(short *x, short *y) 285{ 286 SDL_assert(s_BaseUnitsX != 0); /* we init in WIN_ShowMessageBox(), which is the only public function... */ 287 288 *x = MulDiv(*x, 4, s_BaseUnitsX); 289 *y = MulDiv(*y, 8, s_BaseUnitsY); 290} 291 292 293static SDL_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) 294{ 295 DLGITEMTEMPLATEEX item; 296 WORD marker = 0xFFFF; 297 WORD extraData = 0; 298 299 SDL_zero(item); 300 item.style = style; 301 item.exStyle = exStyle; 302 item.x = x; 303 item.y = y; 304 item.cx = w; 305 item.cy = h; 306 item.id = id; 307 308 Vec2ToDLU(&item.x, &item.y); 309 Vec2ToDLU(&item.cx, &item.cy); 310 311 if (!AlignDialogData(dialog, sizeof(DWORD))) { 312 return SDL_FALSE; 313 } 314 if (!AddDialogData(dialog, &item, sizeof(item))) { 315 return SDL_FALSE; 316 } 317 if (!AddDialogData(dialog, &marker, sizeof(marker))) { 318 return SDL_FALSE; 319 } 320 if (!AddDialogData(dialog, &type, sizeof(type))) { 321 return SDL_FALSE; 322 } 323 if (type == DLGITEMTYPEBUTTON || (type == DLGITEMTYPESTATIC && caption != NULL)) { 324 if (!AddDialogString(dialog, caption)) { 325 return SDL_FALSE; 326 } 327 } else { 328 if (!AddDialogData(dialog, &marker, sizeof(marker))) { 329 return SDL_FALSE; 330 } 331 if (!AddDialogData(dialog, &ordinal, sizeof(ordinal))) { 332 return SDL_FALSE; 333 } 334 } 335 if (!AddDialogData(dialog, &extraData, sizeof(extraData))) { 336 return SDL_FALSE; 337 } 338 if (type == DLGITEMTYPEBUTTON) { 339 dialog->numbuttons++; 340 } 341 ++dialog->lpDialog->cDlgItems; 342 343 return SDL_TRUE; 344} 345 346static SDL_bool AddDialogStaticText(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text) 347{ 348 DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL | WS_GROUP; 349 return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -1, text, 0); 350} 351 352static SDL_bool AddDialogStaticIcon(WIN_DialogData *dialog, int x, int y, int w, int h, Uint16 ordinal) 353{ 354 DWORD style = WS_VISIBLE | WS_CHILD | SS_ICON | WS_GROUP; 355 return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -2, NULL, ordinal); 356} 357 358static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault) 359{ 360 DWORD style = WS_VISIBLE | WS_CHILD | WS_TABSTOP; 361 if (isDefault) { 362 style |= BS_DEFPUSHBUTTON; 363 } else { 364 style |= BS_PUSHBUTTON; 365 } 366 /* The first button marks the start of the group. */ 367 if (dialog->numbuttons == 0) { 368 style |= WS_GROUP; 369 } 370 return AddDialogControl(dialog, DLGITEMTYPEBUTTON, style, 0, x, y, w, h, IDBUTTONINDEX0 + dialog->numbuttons, text, 0); 371} 372 373static void FreeDialogData(WIN_DialogData *dialog) 374{ 375 SDL_free(dialog->data); 376 SDL_free(dialog); 377} 378 379static WIN_DialogData *CreateDialogData(int w, int h, const char *caption) 380{ 381 WIN_DialogData *dialog; 382 DLGTEMPLATEEX dialogTemplate; 383 WORD WordToPass; 384 385 SDL_zero(dialogTemplate); 386 dialogTemplate.dlgVer = 1; 387 dialogTemplate.signature = 0xffff; 388 dialogTemplate.style = (WS_CAPTION | DS_CENTER | DS_SHELLFONT); 389 dialogTemplate.x = 0; 390 dialogTemplate.y = 0; 391 dialogTemplate.cx = w; 392 dialogTemplate.cy = h; 393 Vec2ToDLU(&dialogTemplate.cx, &dialogTemplate.cy); 394 395 dialog = (WIN_DialogData *)SDL_calloc(1, sizeof(*dialog)); 396 if (!dialog) { 397 return NULL; 398 } 399 400 if (!AddDialogData(dialog, &dialogTemplate, sizeof(dialogTemplate))) { 401 FreeDialogData(dialog); 402 return NULL; 403 } 404 405 /* No menu */ 406 WordToPass = 0; 407 if (!AddDialogData(dialog, &WordToPass, 2)) { 408 FreeDialogData(dialog); 409 return NULL; 410 } 411 412 /* No custom class */ 413 if (!AddDialogData(dialog, &WordToPass, 2)) { 414 FreeDialogData(dialog); 415 return NULL; 416 } 417 418 /* title */ 419 if (!AddDialogString(dialog, caption)) { 420 FreeDialogData(dialog); 421 return NULL; 422 } 423 424 /* Font stuff */ 425 { 426 /* 427 * We want to use the system messagebox font. 428 */ 429 BYTE ToPass; 430 431 NONCLIENTMETRICSA NCM; 432 NCM.cbSize = sizeof(NCM); 433 SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0); 434 435 /* Font size - convert to logical font size for dialog parameter. */ 436 { 437 HDC ScreenDC = GetDC(NULL); 438 int LogicalPixelsY = GetDeviceCaps(ScreenDC, LOGPIXELSY); 439 if (!LogicalPixelsY) /* This can happen if the application runs out of GDI handles */ 440 LogicalPixelsY = 72; 441 WordToPass = (WORD)(-72 * NCM.lfMessageFont.lfHeight / LogicalPixelsY); 442 ReleaseDC(NULL, ScreenDC); 443 } 444 445 if (!AddDialogData(dialog, &WordToPass, 2)) { 446 FreeDialogData(dialog); 447 return NULL; 448 } 449 450 /* Font weight */ 451 WordToPass = (WORD)NCM.lfMessageFont.lfWeight; 452 if (!AddDialogData(dialog, &WordToPass, 2)) { 453 FreeDialogData(dialog); 454 return NULL; 455 } 456 457 /* italic? */ 458 ToPass = NCM.lfMessageFont.lfItalic; 459 if (!AddDialogData(dialog, &ToPass, 1)) { 460 FreeDialogData(dialog); 461 return NULL; 462 } 463 464 /* charset? */ 465 ToPass = NCM.lfMessageFont.lfCharSet; 466 if (!AddDialogData(dialog, &ToPass, 1)) { 467 FreeDialogData(dialog); 468 return NULL; 469 } 470 471 /* font typeface. */ 472 if (!AddDialogString(dialog, NCM.lfMessageFont.lfFaceName)) { 473 FreeDialogData(dialog); 474 return NULL; 475 } 476 } 477 478 return dialog; 479} 480 481/* Escaping ampersands is necessary to disable mnemonics in dialog controls. 482 * The caller provides a char** for dst and a size_t* for dstlen where the 483 * address of the work buffer and its size will be stored. Their values must be 484 * NULL and 0 on the first call. src is the string to be escaped. On error, the 485 * function returns NULL and, on success, returns a pointer to the escaped 486 * sequence as a read-only string that is valid until the next call or until the 487 * work buffer is freed. Once all strings have been processed, it's the caller's 488 * responsibilty to free the work buffer with SDL_free, even on errors. 489 */ 490static const char *EscapeAmpersands(char **dst, size_t *dstlen, const char *src) 491{ 492 char *newdst; 493 size_t ampcount = 0; 494 size_t srclen = 0; 495 496 if (src == NULL) { 497 return NULL; 498 } 499 500 while (src[srclen]) { 501 if (src[srclen] == '&') { 502 ampcount++; 503 } 504 srclen++; 505 } 506 srclen++; 507 508 if (ampcount == 0) { 509 /* Nothing to do. */ 510 return src; 511 } 512 if (SIZE_MAX - srclen < ampcount) { 513 return NULL; 514 } 515 if (*dst == NULL || *dstlen < srclen + ampcount) { 516 /* Allocating extra space in case the next strings are a bit longer. */ 517 size_t extraspace = SIZE_MAX - (srclen + ampcount); 518 if (extraspace > 512) { 519 extraspace = 512; 520 } 521 *dstlen = srclen + ampcount + extraspace; 522 SDL_free(*dst); 523 *dst = NULL; 524 newdst = SDL_malloc(*dstlen); 525 if (newdst == NULL) { 526 return NULL; 527 } 528 *dst = newdst; 529 } else { 530 newdst = *dst; 531 } 532 533 /* The escape character is the ampersand itself. */ 534 while (srclen--) { 535 if (*src == '&') { 536 *newdst++ = '&'; 537 } 538 *newdst++ = *src++; 539 } 540 541 return *dst; 542} 543 544/* This function is called if a Task Dialog is unsupported. */ 545static int 546WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) 547{ 548 WIN_DialogData *dialog; 549 int i, x, y, retval; 550 const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons; 551 HFONT DialogFont; 552 SIZE Size; 553 RECT TextSize; 554 wchar_t* wmessage; 555 TEXTMETRIC TM; 556 HDC FontDC; 557 INT_PTR result; 558 char *ampescape = NULL; 559 size_t ampescapesize = 0; 560 Uint16 defbuttoncount = 0; 561 Uint16 icon = 0; 562 563 HWND ParentWindow = NULL; 564 565 const int ButtonWidth = 88; 566 const int ButtonHeight = 26; 567 const int TextMargin = 16; 568 const int ButtonMargin = 12; 569 const int IconWidth = GetSystemMetrics(SM_CXICON); 570 const int IconHeight = GetSystemMetrics(SM_CYICON); 571 const int IconMargin = 20; 572 573 if (messageboxdata->numbuttons > MAX_BUTTONS) { 574 return SDL_SetError("Number of butons exceeds limit of %d", MAX_BUTTONS); 575 } 576 577 switch (messageboxdata->flags) { 578 case SDL_MESSAGEBOX_ERROR: 579 icon = (Uint16)(size_t)IDI_ERROR; 580 break; 581 case SDL_MESSAGEBOX_WARNING: 582 icon = (Uint16)(size_t)IDI_WARNING; 583 break; 584 case SDL_MESSAGEBOX_INFORMATION: 585 icon = (Uint16)(size_t)IDI_INFORMATION; 586 break; 587 } 588 589 /* Jan 25th, 2013 - [email protected] 590 * 591 * 592 * I've tried to make this more reasonable, but I've run in to a lot 593 * of nonsense. 594 * 595 * The original issue is the code was written in pixels and not 596 * dialog units (DLUs). All DialogBox functions use DLUs, which 597 * vary based on the selected font (yay). 598 * 599 * According to MSDN, the most reliable way to convert is via 600 * MapDialogUnits, which requires an HWND, which we don't have 601 * at time of template creation. 602 * 603 * We do however have: 604 * The system font (DLU width 8 for me) 605 * The font we select for the dialog (DLU width 6 for me) 606 * 607 * Based on experimentation, *neither* of these return the value 608 * actually used. Stepping in to MapDialogUnits(), the conversion 609 * is fairly clear, and uses 7 for me. 610 * 611 * As a result, some of this is hacky to ensure the sizing is 612 * somewhat correct. 613 * 614 * Honestly, a long term solution is to use CreateWindow, not CreateDialog. 615 * 616 617 * 618 * In order to get text dimensions we need to have a DC with the desired font. 619 * I'm assuming a dialog box in SDL is rare enough we can to the create. 620 */ 621 FontDC = CreateCompatibleDC(0); 622 623 { 624 /* Create a duplicate of the font used in system message boxes. */ 625 LOGFONT lf; 626 NONCLIENTMETRICS NCM; 627 NCM.cbSize = sizeof(NCM); 628 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0); 629 lf = NCM.lfMessageFont; 630 DialogFont = CreateFontIndirect(&lf); 631 } 632 633 /* Select the font in to our DC */ 634 SelectObject(FontDC, DialogFont); 635 636 { 637 /* Get the metrics to try and figure our DLU conversion. */ 638 GetTextMetrics(FontDC, &TM); 639 640 /* Calculation from the following documentation: 641 * https://support.microsoft.com/en-gb/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font 642 * This fixes bug 2137, dialog box calculation with a fixed-width system font 643 */ 644 { 645 SIZE extent; 646 GetTextExtentPoint32A(FontDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &extent); 647 s_BaseUnitsX = (extent.cx / 26 + 1) / 2; 648 } 649 /*s_BaseUnitsX = TM.tmAveCharWidth + 1;*/ 650 s_BaseUnitsY = TM.tmHeight; 651 } 652 653 /* Measure the *pixel* size of the string. */ 654 wmessage = WIN_UTF8ToString(messageboxdata->message); 655 SDL_zero(TextSize); 656 DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EDITCONTROL); 657 658 /* Add margins and some padding for hangs, etc. */ 659 TextSize.left += TextMargin; 660 TextSize.right += TextMargin + 2; 661 TextSize.top += TextMargin; 662 TextSize.bottom += TextMargin + 2; 663 664 /* Done with the DC, and the string */ 665 DeleteDC(FontDC); 666 SDL_free(wmessage); 667 668 /* Increase the size of the dialog by some border spacing around the text. */ 669 Size.cx = TextSize.right - TextSize.left; 670 Size.cy = TextSize.bottom - TextSize.top; 671 Size.cx += TextMargin * 2; 672 Size.cy += TextMargin * 2; 673 674 /* Make dialog wider and shift text over for the icon. */ 675 if (icon) { 676 Size.cx += IconMargin + IconWidth; 677 TextSize.left += IconMargin + IconWidth; 678 TextSize.right += IconMargin + IconWidth; 679 } 680 681 /* Ensure the size is wide enough for all of the buttons. */ 682 if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin) 683 Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin; 684 685 /* Reset the height to the icon size if it is actually bigger than the text. */ 686 if (icon && Size.cy < IconMargin * 2 + IconHeight) { 687 Size.cy = IconMargin * 2 + IconHeight; 688 } 689 690 /* Add vertical space for the buttons and border. */ 691 Size.cy += ButtonHeight + TextMargin; 692 693 dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title); 694 if (!dialog) { 695 return -1; 696 } 697 698 if (icon && ! AddDialogStaticIcon(dialog, IconMargin, IconMargin, IconWidth, IconHeight, icon)) { 699 FreeDialogData(dialog); 700 return -1; 701 } 702 703 if (!AddDialogStaticText(dialog, TextSize.left, TextSize.top, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) { 704 FreeDialogData(dialog); 705 return -1; 706 } 707 708 /* Align the buttons to the right/bottom. */ 709 x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons; 710 y = Size.cy - ButtonHeight - ButtonMargin; 711 for (i = messageboxdata->numbuttons - 1; i >= 0; --i) { 712 SDL_bool isdefault = SDL_FALSE; 713 const char *buttontext; 714 715 if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { 716 defbuttoncount++; 717 if (defbuttoncount == 1) { 718 isdefault = SDL_TRUE; 719 } 720 } 721 722 buttontext = EscapeAmpersands(&escape, &escapesize, buttons[i].text); 723 if (buttontext == NULL || !AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttontext, buttons[i].buttonid, isdefault)) { 724 FreeDialogData(dialog); 725 SDL_free(ampescape); 726 return -1; 727 } 728 x += ButtonWidth + ButtonMargin; 729 } 730 SDL_free(ampescape); 731 732 /* If we have a parent window, get the Instance and HWND for them 733 * so that our little dialog gets exclusive focus at all times. */ 734 if (messageboxdata->window) { 735 ParentWindow = ((SDL_WindowData*)messageboxdata->window->driverdata)->hwnd; 736 } 737 738 result = DialogBoxIndirectParam(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc, (LPARAM)messageboxdata); 739 if (result >= IDBUTTONINDEX0 && result - IDBUTTONINDEX0 < messageboxdata->numbuttons) { 740 *buttonid = messageboxdata->buttons[(messageboxdata->numbuttons - 1) - (result - IDBUTTONINDEX0)].buttonid; 741 retval = 0; 742 } else if (result == IDCLOSED) { 743 /* Dialog window closed by user or system. */ 744 /* This could use a special return code. */ 745 retval = 0; 746 *buttonid = -1; 747 } else { 748 if (result == 0) { 749 SDL_SetError("Invalid parent window handle"); 750 } else if (result == -1) { 751 SDL_SetError("The message box encountered an error."); 752 } else if (result == IDINVALPTRINIT || result == IDINVALPTRSETFOCUS || result == IDINVALPTRCOMMAND) { 753 SDL_SetError("Invalid message box pointer in dialog procedure"); 754 } else if (result == IDINVALPTRDLGITEM) { 755 SDL_SetError("Couldn't find dialog control of the default enter-key button"); 756 } else { 757 SDL_SetError("An unknown error occured"); 758 } 759 retval = -1; 760 } 761 762 FreeDialogData(dialog); 763 return retval; 764} 765 766/* TaskDialogIndirect procedure 767 * This is because SDL targets Windows XP (0x501), so this is not defined in the platform SDK. 768 */ 769typedef HRESULT(FAR WINAPI *TASKDIALOGINDIRECTPROC)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked); 770 771int 772WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) 773{ 774 HWND ParentWindow = NULL; 775 wchar_t *wmessage; 776 wchar_t *wtitle; 777 TASKDIALOGCONFIG TaskConfig; 778 TASKDIALOG_BUTTON *pButtons; 779 TASKDIALOG_BUTTON *pButton; 780 HMODULE hComctl32; 781 TASKDIALOGINDIRECTPROC pfnTaskDialogIndirect; 782 HRESULT hr; 783 char *ampescape = NULL; 784 size_t ampescapesize = 0; 785 int nButton; 786 int nCancelButton; 787 int i; 788 789 if (SIZE_MAX / sizeof(TASKDIALOG_BUTTON) < messageboxdata->numbuttons) { 790 return SDL_OutOfMemory(); 791 } 792 793 /* If we cannot load comctl32.dll use the old messagebox! */ 794 hComctl32 = LoadLibrary(TEXT("Comctl32.dll")); 795 if (hComctl32 == NULL) { 796 return WIN_ShowOldMessageBox(messageboxdata, buttonid); 797 } 798 799 /* If TaskDialogIndirect doesn't exist use the old messagebox! 800 This will fail prior to Windows Vista. 801 The manifest file in the application may require targeting version 6 of comctl32.dll, even 802 when we use LoadLibrary here! 803 If you don't want to bother with manifests, put this #pragma in your app's source code somewhere: 804 pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 805 */ 806 pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC) GetProcAddress(hComctl32, "TaskDialogIndirect"); 807 if (pfnTaskDialogIndirect == NULL) { 808 FreeLibrary(hComctl32); 809 return WIN_ShowOldMessageBox(messageboxdata, buttonid); 810 } 811 812 /* If we have a parent window, get the Instance and HWND for them 813 so that our little dialog gets exclusive focus at all times. */ 814 if (messageboxdata->window) { 815 ParentWindow = ((SDL_WindowData *) messageboxdata->window->driverdata)->hwnd; 816 } 817 818 wmessage = WIN_UTF8ToString(messageboxdata->message); 819 wtitle = WIN_UTF8ToString(messageboxdata->title); 820 821 SDL_zero(TaskConfig); 822 TaskConfig.cbSize = sizeof (TASKDIALOGCONFIG); 823 TaskConfig.hwndParent = ParentWindow; 824 TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT; 825 TaskConfig.pszWindowTitle = wtitle; 826 if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) { 827 TaskConfig.pszMainIcon = TD_ERROR_ICON; 828 } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) { 829 TaskConfig.pszMainIcon = TD_WARNING_ICON; 830 } else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) { 831 TaskConfig.pszMainIcon = TD_INFORMATION_ICON; 832 } else { 833 TaskConfig.pszMainIcon = NULL; 834 } 835 836 TaskConfig.pszContent = wmessage; 837 TaskConfig.cButtons = messageboxdata->numbuttons; 838 pButtons = SDL_malloc(sizeof (TASKDIALOG_BUTTON) * messageboxdata->numbuttons); 839 TaskConfig.nDefaultButton = 0; 840 nCancelButton = 0; 841 for (i = 0; i < messageboxdata->numbuttons; i++) 842 { 843 const char *buttontext; 844 pButton = &pButtons[messageboxdata->numbuttons-1-i]; 845 if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { 846 nCancelButton = messageboxdata->buttons[i].buttonid; 847 pButton->nButtonID = 2; 848 } else { 849 pButton->nButtonID = messageboxdata->buttons[i].buttonid + 1; 850 if (pButton->nButtonID >= 2) { 851 pButton->nButtonID++; 852 } 853 } 854 buttontext = EscapeAmpersands(&escape, &escapesize, messageboxdata->buttons[i].text); 855 if (buttontext == NULL) { 856 int j; 857 FreeLibrary(hComctl32); 858 SDL_free(ampescape); 859 SDL_free(wmessage); 860 SDL_free(wtitle); 861 for (j = 0; j < i; j++) { 862 SDL_free((wchar_t *) pButtons[j].pszButtonText); 863 } 864 SDL_free(pButtons); 865 return -1; 866 } 867 pButton->pszButtonText = WIN_UTF8ToString(buttontext); 868 if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { 869 TaskConfig.nDefaultButton = pButton->nButtonID; 870 } 871 } 872 TaskConfig.pButtons = pButtons; 873 874 /* Show the Task Dialog */ 875 hr = pfnTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL); 876 877 /* Free everything */ 878 FreeLibrary(hComctl32); 879 SDL_free(ampescape); 880 SDL_free(wmessage); 881 SDL_free(wtitle); 882 for (i = 0; i < messageboxdata->numbuttons; i++) { 883 SDL_free((wchar_t *) pButtons[i].pszButtonText); 884 } 885 SDL_free(pButtons); 886 887 /* Check the Task Dialog was successful and give the result */ 888 if (SUCCEEDED(hr)) { 889 if (nButton == 2) { 890 *buttonid = nCancelButton; 891 } else if (nButton > 2) { 892 *buttonid = nButton-1-1; 893 } else { 894 *buttonid = nButton-1; 895 } 896 return 0; 897 } 898 899 /* We failed showing the Task Dialog, use the old message box! */ 900 return WIN_ShowOldMessageBox(messageboxdata, buttonid); 901} 902 903#endif /* SDL_VIDEO_DRIVER_WINDOWS */ 904 905/* vi: set ts=4 sw=4 expandtab: */ 906[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.