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 #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 
75 typedef 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 
89 typedef 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 
103 typedef struct
104 {
105  DLGTEMPLATEEX* lpDialog;
106  Uint8 *data;
107  size_t size;
108  size_t used;
109  WORD numbuttons;
110 } WIN_DialogData;
111 
112 static 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 
122 static 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 
190 static 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 
229 static 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 
242 static 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 
254 static 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 
282 static int s_BaseUnitsX;
283 static int s_BaseUnitsY;
284 static 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 
293 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)
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 
346 static 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 
352 static 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 
358 static 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, id, text, 0);
371 }
372 
373 static void FreeDialogData(WIN_DialogData *dialog)
374 {
375  SDL_free(dialog->data);
376  SDL_free(dialog);
377 }
378 
379 static 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  */
490 static 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. */
545 static int
546 WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
547 {
548  WIN_DialogData *dialog;
549  int i, x, y, retval;
550  HFONT DialogFont;
551  SIZE Size;
552  RECT TextSize;
553  wchar_t* wmessage;
554  TEXTMETRIC TM;
555  HDC FontDC;
556  INT_PTR result;
557  char *ampescape = NULL;
558  size_t ampescapesize = 0;
559  Uint16 defbuttoncount = 0;
560  Uint16 icon = 0;
561 
562  HWND ParentWindow = NULL;
563 
564  const int ButtonWidth = 88;
565  const int ButtonHeight = 26;
566  const int TextMargin = 16;
567  const int ButtonMargin = 12;
568  const int IconWidth = GetSystemMetrics(SM_CXICON);
569  const int IconHeight = GetSystemMetrics(SM_CYICON);
570  const int IconMargin = 20;
571 
572  if (messageboxdata->numbuttons > MAX_BUTTONS) {
573  return SDL_SetError("Number of butons exceeds limit of %d", MAX_BUTTONS);
574  }
575 
576  switch (messageboxdata->flags) {
578  icon = (Uint16)(size_t)IDI_ERROR;
579  break;
581  icon = (Uint16)(size_t)IDI_WARNING;
582  break;
584  icon = (Uint16)(size_t)IDI_INFORMATION;
585  break;
586  }
587 
588  /* Jan 25th, 2013 - dant@fleetsa.com
589  *
590  *
591  * I've tried to make this more reasonable, but I've run in to a lot
592  * of nonsense.
593  *
594  * The original issue is the code was written in pixels and not
595  * dialog units (DLUs). All DialogBox functions use DLUs, which
596  * vary based on the selected font (yay).
597  *
598  * According to MSDN, the most reliable way to convert is via
599  * MapDialogUnits, which requires an HWND, which we don't have
600  * at time of template creation.
601  *
602  * We do however have:
603  * The system font (DLU width 8 for me)
604  * The font we select for the dialog (DLU width 6 for me)
605  *
606  * Based on experimentation, *neither* of these return the value
607  * actually used. Stepping in to MapDialogUnits(), the conversion
608  * is fairly clear, and uses 7 for me.
609  *
610  * As a result, some of this is hacky to ensure the sizing is
611  * somewhat correct.
612  *
613  * Honestly, a long term solution is to use CreateWindow, not CreateDialog.
614  *
615 
616  *
617  * In order to get text dimensions we need to have a DC with the desired font.
618  * I'm assuming a dialog box in SDL is rare enough we can to the create.
619  */
620  FontDC = CreateCompatibleDC(0);
621 
622  {
623  /* Create a duplicate of the font used in system message boxes. */
624  LOGFONT lf;
625  NONCLIENTMETRICS NCM;
626  NCM.cbSize = sizeof(NCM);
627  SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &NCM, 0);
628  lf = NCM.lfMessageFont;
629  DialogFont = CreateFontIndirect(&lf);
630  }
631 
632  /* Select the font in to our DC */
633  SelectObject(FontDC, DialogFont);
634 
635  {
636  /* Get the metrics to try and figure our DLU conversion. */
637  GetTextMetrics(FontDC, &TM);
638 
639  /* Calculation from the following documentation:
640  * https://support.microsoft.com/en-gb/help/125681/how-to-calculate-dialog-base-units-with-non-system-based-font
641  * This fixes bug 2137, dialog box calculation with a fixed-width system font
642  */
643  {
644  SIZE extent;
645  GetTextExtentPoint32A(FontDC, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &extent);
646  s_BaseUnitsX = (extent.cx / 26 + 1) / 2;
647  }
648  /*s_BaseUnitsX = TM.tmAveCharWidth + 1;*/
649  s_BaseUnitsY = TM.tmHeight;
650  }
651 
652  /* Measure the *pixel* size of the string. */
653  wmessage = WIN_UTF8ToString(messageboxdata->message);
654  SDL_zero(TextSize);
655  DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EDITCONTROL);
656 
657  /* Add margins and some padding for hangs, etc. */
658  TextSize.left += TextMargin;
659  TextSize.right += TextMargin + 2;
660  TextSize.top += TextMargin;
661  TextSize.bottom += TextMargin + 2;
662 
663  /* Done with the DC, and the string */
664  DeleteDC(FontDC);
665  SDL_free(wmessage);
666 
667  /* Increase the size of the dialog by some border spacing around the text. */
668  Size.cx = TextSize.right - TextSize.left;
669  Size.cy = TextSize.bottom - TextSize.top;
670  Size.cx += TextMargin * 2;
671  Size.cy += TextMargin * 2;
672 
673  /* Make dialog wider and shift text over for the icon. */
674  if (icon) {
675  Size.cx += IconMargin + IconWidth;
676  TextSize.left += IconMargin + IconWidth;
677  TextSize.right += IconMargin + IconWidth;
678  }
679 
680  /* Ensure the size is wide enough for all of the buttons. */
681  if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
682  Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
683 
684  /* Reset the height to the icon size if it is actually bigger than the text. */
685  if (icon && Size.cy < IconMargin * 2 + IconHeight) {
686  Size.cy = IconMargin * 2 + IconHeight;
687  }
688 
689  /* Add vertical space for the buttons and border. */
690  Size.cy += ButtonHeight + TextMargin;
691 
692  dialog = CreateDialogData(Size.cx, Size.cy, messageboxdata->title);
693  if (!dialog) {
694  return -1;
695  }
696 
697  if (icon && ! AddDialogStaticIcon(dialog, IconMargin, IconMargin, IconWidth, IconHeight, icon)) {
698  FreeDialogData(dialog);
699  return -1;
700  }
701 
702  if (!AddDialogStaticText(dialog, TextSize.left, TextSize.top, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
703  FreeDialogData(dialog);
704  return -1;
705  }
706 
707  /* Align the buttons to the right/bottom. */
708  x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons;
709  y = Size.cy - ButtonHeight - ButtonMargin;
710  for (i = 0; i < messageboxdata->numbuttons; i++) {
711  SDL_bool isdefault = SDL_FALSE;
712  const char *buttontext;
713  const SDL_MessageBoxButtonData *sdlButton;
714 
715  /* We always have to create the dialog buttons from left to right
716  * so that the tab order is correct. Select the info to use
717  * depending on which order was requested. */
718  if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
719  sdlButton = &messageboxdata->buttons[i];
720  } else {
721  sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
722  }
723 
725  defbuttoncount++;
726  if (defbuttoncount == 1) {
727  isdefault = SDL_TRUE;
728  }
729  }
730 
731  buttontext = EscapeAmpersands(&ampescape, &ampescapesize, sdlButton->text);
732  /* Make sure to provide the correct ID to keep buttons indexed in the
733  * same order as how they are in messageboxdata. */
734  if (buttontext == NULL || !AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttontext, IDBUTTONINDEX0 + (int)(sdlButton - messageboxdata->buttons), isdefault)) {
735  FreeDialogData(dialog);
736  SDL_free(ampescape);
737  return -1;
738  }
739 
740  x += ButtonWidth + ButtonMargin;
741  }
742  SDL_free(ampescape);
743 
744  /* If we have a parent window, get the Instance and HWND for them
745  * so that our little dialog gets exclusive focus at all times. */
746  if (messageboxdata->window) {
747  ParentWindow = ((SDL_WindowData*)messageboxdata->window->driverdata)->hwnd;
748  }
749 
750  result = DialogBoxIndirectParam(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc, (LPARAM)messageboxdata);
751  if (result >= IDBUTTONINDEX0 && result - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
752  *buttonid = messageboxdata->buttons[result - IDBUTTONINDEX0].buttonid;
753  retval = 0;
754  } else if (result == IDCLOSED) {
755  /* Dialog window closed by user or system. */
756  /* This could use a special return code. */
757  retval = 0;
758  *buttonid = -1;
759  } else {
760  if (result == 0) {
761  SDL_SetError("Invalid parent window handle");
762  } else if (result == -1) {
763  SDL_SetError("The message box encountered an error.");
764  } else if (result == IDINVALPTRINIT || result == IDINVALPTRSETFOCUS || result == IDINVALPTRCOMMAND) {
765  SDL_SetError("Invalid message box pointer in dialog procedure");
766  } else if (result == IDINVALPTRDLGITEM) {
767  SDL_SetError("Couldn't find dialog control of the default enter-key button");
768  } else {
769  SDL_SetError("An unknown error occured");
770  }
771  retval = -1;
772  }
773 
774  FreeDialogData(dialog);
775  return retval;
776 }
777 
778 /* TaskDialogIndirect procedure
779  * This is because SDL targets Windows XP (0x501), so this is not defined in the platform SDK.
780  */
781 typedef HRESULT(FAR WINAPI *TASKDIALOGINDIRECTPROC)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
782 
783 int
784 WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
785 {
786  HWND ParentWindow = NULL;
787  wchar_t *wmessage;
788  wchar_t *wtitle;
789  TASKDIALOGCONFIG TaskConfig;
790  TASKDIALOG_BUTTON *pButtons;
791  TASKDIALOG_BUTTON *pButton;
792  HMODULE hComctl32;
793  TASKDIALOGINDIRECTPROC pfnTaskDialogIndirect;
794  HRESULT hr;
795  char *ampescape = NULL;
796  size_t ampescapesize = 0;
797  int nButton;
798  int nCancelButton;
799  int i;
800 
801  if (SIZE_MAX / sizeof(TASKDIALOG_BUTTON) < messageboxdata->numbuttons) {
802  return SDL_OutOfMemory();
803  }
804 
805  /* If we cannot load comctl32.dll use the old messagebox! */
806  hComctl32 = LoadLibrary(TEXT("Comctl32.dll"));
807  if (hComctl32 == NULL) {
808  return WIN_ShowOldMessageBox(messageboxdata, buttonid);
809  }
810 
811  /* If TaskDialogIndirect doesn't exist use the old messagebox!
812  This will fail prior to Windows Vista.
813  The manifest file in the application may require targeting version 6 of comctl32.dll, even
814  when we use LoadLibrary here!
815  If you don't want to bother with manifests, put this #pragma in your app's source code somewhere:
816  pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
817  */
818  pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC) GetProcAddress(hComctl32, "TaskDialogIndirect");
819  if (pfnTaskDialogIndirect == NULL) {
820  FreeLibrary(hComctl32);
821  return WIN_ShowOldMessageBox(messageboxdata, buttonid);
822  }
823 
824  /* If we have a parent window, get the Instance and HWND for them
825  so that our little dialog gets exclusive focus at all times. */
826  if (messageboxdata->window) {
827  ParentWindow = ((SDL_WindowData *) messageboxdata->window->driverdata)->hwnd;
828  }
829 
830  wmessage = WIN_UTF8ToString(messageboxdata->message);
831  wtitle = WIN_UTF8ToString(messageboxdata->title);
832 
833  SDL_zero(TaskConfig);
834  TaskConfig.cbSize = sizeof (TASKDIALOGCONFIG);
835  TaskConfig.hwndParent = ParentWindow;
836  TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT;
837  TaskConfig.pszWindowTitle = wtitle;
838  if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
839  TaskConfig.pszMainIcon = TD_ERROR_ICON;
840  } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
841  TaskConfig.pszMainIcon = TD_WARNING_ICON;
842  } else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) {
843  TaskConfig.pszMainIcon = TD_INFORMATION_ICON;
844  } else {
845  TaskConfig.pszMainIcon = NULL;
846  }
847 
848  TaskConfig.pszContent = wmessage;
849  TaskConfig.cButtons = messageboxdata->numbuttons;
850  pButtons = SDL_malloc(sizeof (TASKDIALOG_BUTTON) * messageboxdata->numbuttons);
851  TaskConfig.nDefaultButton = 0;
852  nCancelButton = 0;
853  for (i = 0; i < messageboxdata->numbuttons; i++)
854  {
855  const char *buttontext;
856  if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
857  pButton = &pButtons[i];
858  } else {
859  pButton = &pButtons[messageboxdata->numbuttons - 1 - i];
860  }
861  if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
862  nCancelButton = messageboxdata->buttons[i].buttonid;
863  pButton->nButtonID = IDCANCEL;
864  } else {
865  pButton->nButtonID = IDBUTTONINDEX0 + i;
866  }
867  buttontext = EscapeAmpersands(&ampescape, &ampescapesize, messageboxdata->buttons[i].text);
868  if (buttontext == NULL) {
869  int j;
870  FreeLibrary(hComctl32);
871  SDL_free(ampescape);
872  SDL_free(wmessage);
873  SDL_free(wtitle);
874  for (j = 0; j < i; j++) {
875  SDL_free((wchar_t *) pButtons[j].pszButtonText);
876  }
877  SDL_free(pButtons);
878  return -1;
879  }
880  pButton->pszButtonText = WIN_UTF8ToString(buttontext);
881  if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
882  TaskConfig.nDefaultButton = pButton->nButtonID;
883  }
884  }
885  TaskConfig.pButtons = pButtons;
886 
887  /* Show the Task Dialog */
888  hr = pfnTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL);
889 
890  /* Free everything */
891  FreeLibrary(hComctl32);
892  SDL_free(ampescape);
893  SDL_free(wmessage);
894  SDL_free(wtitle);
895  for (i = 0; i < messageboxdata->numbuttons; i++) {
896  SDL_free((wchar_t *) pButtons[i].pszButtonText);
897  }
898  SDL_free(pButtons);
899 
900  /* Check the Task Dialog was successful and give the result */
901  if (SUCCEEDED(hr)) {
902  if (nButton == IDCANCEL) {
903  *buttonid = nCancelButton;
904  } else if (nButton >= IDBUTTONINDEX0 && nButton < IDBUTTONINDEX0 + messageboxdata->numbuttons) {
905  *buttonid = messageboxdata->buttons[nButton - IDBUTTONINDEX0].buttonid;
906  } else {
907  *buttonid = -1;
908  }
909  return 0;
910  }
911 
912  /* We failed showing the Task Dialog, use the old message box! */
913  return WIN_ShowOldMessageBox(messageboxdata, buttonid);
914 }
915 
916 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
917 
918 /* vi: set ts=4 sw=4 expandtab: */
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:418
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179
TASKDIALOGCONFIG::nDefaultButton
int nDefaultButton
Definition: SDL_windowstaskdialog.h:137
SIZE_MAX
#define SIZE_MAX
Definition: SDL_wave.c:27
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:52
Uint16
uint16_t Uint16
Definition: SDL_stdinc.h:191
WIN_UTF8ToString
#define WIN_UTF8ToString(S)
Definition: SDL_windows.h:47
if
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
Definition: pixman-arm-neon-asm.h:469
NULL
#define NULL
Definition: begin_code.h:167
TRUE
#define TRUE
Definition: edid-parse.c:33
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:98
size_t
unsigned int size_t
Definition: SDL_config_windows.h:68
TD_INFORMATION_ICON
#define TD_INFORMATION_ICON
Definition: SDL_windowstaskdialog.h:106
SDL_WindowData
Definition: SDL_androidwindow.h:39
TASKDIALOGCONFIG::pszMainIcon
PCWSTR pszMainIcon
Definition: SDL_windowstaskdialog.h:131
count
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
SDL_realloc
#define SDL_realloc
Definition: SDL_dynapi_overrides.h:376
SDL_MESSAGEBOX_WARNING
@ SDL_MESSAGEBOX_WARNING
Definition: SDL_messagebox.h:40
FAR
#define FAR
Definition: SDL_directx.h:37
SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT
@ SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT
Definition: SDL_messagebox.h:42
SDL_MessageBoxButtonData::flags
Uint32 flags
Definition: SDL_messagebox.h:60
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:99
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
h
GLfloat GLfloat GLfloat GLfloat h
Definition: SDL_opengl_glext.h:1949
TD_ERROR_ICON
#define TD_ERROR_ICON
Definition: SDL_windowstaskdialog.h:105
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9435
TASKDIALOGCONFIG::dwFlags
TASKDIALOG_FLAGS dwFlags
Definition: SDL_windowstaskdialog.h:125
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
dst
GLenum GLenum dst
Definition: SDL_opengl_glext.h:1740
TASKDIALOG_BUTTON
Definition: SDL_windowstaskdialog.h:85
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
TASKDIALOGCONFIG
Definition: SDL_windowstaskdialog.h:121
TDF_SIZE_TO_CONTENT
@ TDF_SIZE_TO_CONTENT
Definition: SDL_windowstaskdialog.h:46
p
GLfloat GLfloat p
Definition: SDL_opengl_glext.h:11093
SDL_windowsvideo.h
retval
SDL_bool retval
Definition: testgamecontroller.c:65
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
marker
const GLchar * marker
Definition: SDL_opengl_glext.h:6070
SDL_MessageBoxData::flags
Uint32 flags
Definition: SDL_messagebox.h:96
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:95
SUCCEEDED
#define SUCCEEDED(x)
Definition: SDL_directx.h:51
SDL_MessageBoxData::window
SDL_Window * window
Definition: SDL_messagebox.h:97
SDL_assert.h
text
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_MESSAGEBOX_ERROR
@ SDL_MESSAGEBOX_ERROR
Definition: SDL_messagebox.h:39
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:59
TASKDIALOGCONFIG::hwndParent
HWND hwndParent
Definition: SDL_windowstaskdialog.h:123
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_MessageBoxButtonData::buttonid
int buttonid
Definition: SDL_messagebox.h:61
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_MESSAGEBOX_INFORMATION
@ SDL_MESSAGEBOX_INFORMATION
Definition: SDL_messagebox.h:41
size
GLsizeiptr size
Definition: SDL_opengl_glext.h:540
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
TASKDIALOGCONFIG::pButtons
const TASKDIALOG_BUTTON * pButtons
Definition: SDL_windowstaskdialog.h:136
id
GLuint id
Definition: SDL_opengl_glext.h:531
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:102
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_Window::driverdata
void * driverdata
Definition: SDL_sysvideo.h:112
src
GLenum src
Definition: SDL_opengl_glext.h:1740
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
Definition: SDL_messagebox.h:51
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_MessageBoxButtonData::text
const char * text
Definition: SDL_messagebox.h:62
TASKDIALOG_BUTTON::pszButtonText
PCWSTR pszButtonText
Definition: SDL_windowstaskdialog.h:87
TASKDIALOGCONFIG::cButtons
UINT cButtons
Definition: SDL_windowstaskdialog.h:135
SDL_windowstaskdialog.h
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:162
j
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
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
TASKDIALOG_BUTTON::nButtonID
int nButtonID
Definition: SDL_windowstaskdialog.h:86
TASKDIALOGCONFIG::pszWindowTitle
PCWSTR pszWindowTitle
Definition: SDL_windowstaskdialog.h:127
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
flags
GLbitfield flags
Definition: SDL_opengl_glext.h:1483
TASKDIALOGCONFIG::cbSize
UINT cbSize
Definition: SDL_windowstaskdialog.h:122
TD_WARNING_ICON
#define TD_WARNING_ICON
Definition: SDL_windowstaskdialog.h:104
type
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
signature
const char * signature
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:101
i
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
FALSE
#define FALSE
Definition: edid-parse.c:34
w
GLubyte GLubyte GLubyte GLubyte w
Definition: SDL_opengl_glext.h:734
TASKDIALOGCONFIG::pszContent
PCWSTR pszContent
Definition: SDL_windowstaskdialog.h:134