SDL 2.0
SDL_windowsmessagebox.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
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"
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) {
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) {
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) {
579 icon = (Uint16)(size_t)IDI_ERROR;
580 break;
582 icon = (Uint16)(size_t)IDI_WARNING;
583 break;
585 icon = (Uint16)(size_t)IDI_INFORMATION;
586 break;
587 }
588
589 /* Jan 25th, 2013 - dant@fleetsa.com
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
716 defbuttoncount++;
717 if (defbuttoncount == 1) {
718 isdefault = SDL_TRUE;
719 }
720 }
721
722 buttontext = EscapeAmpersands(&ampescape, &ampescapesize, 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];
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(&ampescape, &ampescapesize, 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);
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: */
#define SDL_assert(condition)
Definition: SDL_assert.h:169
unsigned int size_t
#define SUCCEEDED(x)
Definition: SDL_directx.h:51
#define FAR
Definition: SDL_directx.h:37
#define SDL_SetError
#define SDL_malloc
#define SDL_realloc
#define SDL_free
#define SDL_memcpy
#define SDL_calloc
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
@ SDL_MESSAGEBOX_ERROR
@ SDL_MESSAGEBOX_WARNING
@ SDL_MESSAGEBOX_INFORMATION
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
GLenum src
GLuint64EXT * result
GLuint id
GLenum GLenum dst
GLsizeiptr size
GLfloat GLfloat p
GLbitfield flags
const GLchar * marker
GLfloat GLfloat GLfloat GLfloat h
GLubyte GLubyte GLubyte GLubyte w
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
uint16_t Uint16
Definition: SDL_stdinc.h:191
uint8_t Uint8
Definition: SDL_stdinc.h:179
#define SIZE_MAX
Definition: SDL_wave.c:27
#define WIN_UTF8ToString(S)
Definition: SDL_windows.h:47
#define TD_INFORMATION_ICON
#define TD_WARNING_ICON
@ TDF_SIZE_TO_CONTENT
#define TD_ERROR_ICON
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
#define TRUE
Definition: edid-parse.c:33
#define FALSE
Definition: edid-parse.c:34
Individual button data.
MessageBox structure containing title, text, window, etc.
const SDL_MessageBoxButtonData * buttons
SDL_Window * window
const char * title
const char * message
void * driverdata
Definition: SDL_sysvideo.h:111
TASKDIALOG_FLAGS dwFlags
const TASKDIALOG_BUTTON * pButtons
SDL_bool retval
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47