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