SDL  2.0
SDL_x11messagebox.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 
22 #include "../../SDL_internal.h"
23 
24 #if SDL_VIDEO_DRIVER_X11
25 
26 #include "SDL.h"
27 #include "SDL_x11video.h"
28 #include "SDL_x11dyn.h"
29 #include "SDL_assert.h"
30 #include "SDL_x11messagebox.h"
31 
32 #include <X11/keysym.h>
33 #include <locale.h>
34 
35 
36 #define SDL_FORK_MESSAGEBOX 1
37 #define SDL_SET_LOCALE 1
38 
39 #if SDL_FORK_MESSAGEBOX
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #endif
45 
46 #define MAX_BUTTONS 8 /* Maximum number of buttons supported */
47 #define MIN_BUTTON_WIDTH 64 /* Minimum button width */
48 #define MIN_DIALOG_WIDTH 200 /* Minimum dialog width */
49 #define MIN_DIALOG_HEIGHT 100 /* Minimum dialog height */
50 
51 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
52 static const char g_MessageBoxFont[] = "-*-*-medium-r-normal--*-120-*-*-*-*-*-*";
53 
54 static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = {
55  { 56, 54, 53 }, /* SDL_MESSAGEBOX_COLOR_BACKGROUND, */
56  { 209, 207, 205 }, /* SDL_MESSAGEBOX_COLOR_TEXT, */
57  { 140, 135, 129 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, */
58  { 105, 102, 99 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, */
59  { 205, 202, 53 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, */
60 };
61 
62 #define SDL_MAKE_RGB( _r, _g, _b ) ( ( ( Uint32 )( _r ) << 16 ) | \
63  ( ( Uint32 )( _g ) << 8 ) | \
64  ( ( Uint32 )( _b ) ) )
65 
66 typedef struct SDL_MessageBoxButtonDataX11 {
67  int x, y; /* Text position */
68  int length; /* Text length */
69  int text_width; /* Text width */
70 
71  SDL_Rect rect; /* Rectangle for entire button */
72 
73  const SDL_MessageBoxButtonData *buttondata; /* Button data from caller */
74 } SDL_MessageBoxButtonDataX11;
75 
76 typedef struct TextLineData {
77  int width; /* Width of this text line */
78  int length; /* String length of this text line */
79  const char *text; /* Text for this line */
80 } TextLineData;
81 
82 typedef struct SDL_MessageBoxDataX11
83 {
84  Display *display;
85  int screen;
86  Window window;
87 #if SDL_VIDEO_DRIVER_X11_XDBE
88  XdbeBackBuffer buf;
89  SDL_bool xdbe; /* Whether Xdbe is present or not */
90 #endif
91  long event_mask;
92  Atom wm_protocols;
93  Atom wm_delete_message;
94 
95  int dialog_width; /* Dialog box width. */
96  int dialog_height; /* Dialog box height. */
97 
98  XFontSet font_set; /* for UTF-8 systems */
99  XFontStruct *font_struct; /* Latin1 (ASCII) fallback. */
100  int xtext, ytext; /* Text position to start drawing at. */
101  int numlines; /* Count of Text lines. */
102  int text_height; /* Height for text lines. */
103  TextLineData *linedata;
104 
105  int *pbuttonid; /* Pointer to user return buttonid value. */
106 
107  int button_press_index; /* Index into buttondata/buttonpos for button which is pressed (or -1). */
108  int mouse_over_index; /* Index into buttondata/buttonpos for button mouse is over (or -1). */
109 
110  int numbuttons; /* Count of buttons. */
111  const SDL_MessageBoxButtonData *buttondata;
112  SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
113 
115 
116  const SDL_MessageBoxData *messageboxdata;
117 } SDL_MessageBoxDataX11;
118 
119 /* Maximum helper for ints. */
120 static SDL_INLINE int
121 IntMax( int a, int b )
122 {
123  return ( a > b ) ? a : b;
124 }
125 
126 /* Return width and height for a string. */
127 static void
128 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
129 {
130  if (SDL_X11_HAVE_UTF8) {
131  XRectangle overall_ink, overall_logical;
132  X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
133  *pwidth = overall_logical.width;
134  *pheight = overall_logical.height;
135  } else {
136  XCharStruct text_structure;
137  int font_direction, font_ascent, font_descent;
138  X11_XTextExtents( data->font_struct, str, nbytes,
139  &font_direction, &font_ascent, &font_descent,
140  &text_structure );
141  *pwidth = text_structure.width;
142  *pheight = text_structure.ascent + text_structure.descent;
143  }
144 }
145 
146 /* Return index of button if position x,y is contained therein. */
147 static int
148 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
149 {
150  int i;
151  int numbuttons = data->numbuttons;
152  SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
153 
154  for ( i = 0; i < numbuttons; i++ ) {
155  SDL_Rect *rect = &buttonpos[ i ].rect;
156 
157  if ( ( x >= rect->x ) &&
158  ( x <= ( rect->x + rect->w ) ) &&
159  ( y >= rect->y ) &&
160  ( y <= ( rect->y + rect->h ) ) ) {
161  return i;
162  }
163  }
164 
165  return -1;
166 }
167 
168 /* Initialize SDL_MessageBoxData structure and Display, etc. */
169 static int
170 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
171 {
172  int i;
173  int numbuttons = messageboxdata->numbuttons;
174  const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
175  const SDL_MessageBoxColor *colorhints;
176 
177  if ( numbuttons > MAX_BUTTONS ) {
178  return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
179  }
180 
181  data->dialog_width = MIN_DIALOG_WIDTH;
182  data->dialog_height = MIN_DIALOG_HEIGHT;
183  data->messageboxdata = messageboxdata;
184  data->buttondata = buttondata;
185  data->numbuttons = numbuttons;
186  data->pbuttonid = pbuttonid;
187 
188  data->display = X11_XOpenDisplay( NULL );
189  if ( !data->display ) {
190  return SDL_SetError("Couldn't open X11 display");
191  }
192 
193  if (SDL_X11_HAVE_UTF8) {
194  char **missing = NULL;
195  int num_missing = 0;
196  data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
197  &missing, &num_missing, NULL);
198  if ( missing != NULL ) {
199  X11_XFreeStringList(missing);
200  }
201  if ( data->font_set == NULL ) {
202  return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
203  }
204  } else {
205  data->font_struct = X11_XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
206  if ( data->font_struct == NULL ) {
207  return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
208  }
209  }
210 
211  if ( messageboxdata->colorScheme ) {
212  colorhints = messageboxdata->colorScheme->colors;
213  } else {
214  colorhints = g_default_colors;
215  }
216 
217  /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
218  for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
219  data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
220  }
221 
222  return 0;
223 }
224 
225 static int
226 CountLinesOfText(const char *text)
227 {
228  int retval = 0;
229  while (text && *text) {
230  const char *lf = SDL_strchr(text, '\n');
231  retval++; /* even without an endline, this counts as a line. */
232  text = lf ? lf + 1 : NULL;
233  }
234  return retval;
235 }
236 
237 /* Calculate and initialize text and button locations. */
238 static int
239 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
240 {
241  int i;
242  int ybuttons;
243  int text_width_max = 0;
244  int button_text_height = 0;
245  int button_width = MIN_BUTTON_WIDTH;
246  const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
247 
248  /* Go over text and break linefeeds into separate lines. */
249  if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
250  const char *text = messageboxdata->message;
251  const int linecount = CountLinesOfText(text);
252  TextLineData *plinedata = (TextLineData *) SDL_malloc(sizeof (TextLineData) * linecount);
253 
254  if (!plinedata) {
255  return SDL_OutOfMemory();
256  }
257 
258  data->linedata = plinedata;
259  data->numlines = linecount;
260 
261  for ( i = 0; i < linecount; i++, plinedata++ ) {
262  const char *lf = SDL_strchr( text, '\n' );
263  const int length = lf ? ( lf - text ) : SDL_strlen( text );
264  int height;
265 
266  plinedata->text = text;
267 
268  GetTextWidthHeight( data, text, length, &plinedata->width, &height );
269 
270  /* Text and widths are the largest we've ever seen. */
271  data->text_height = IntMax( data->text_height, height );
272  text_width_max = IntMax( text_width_max, plinedata->width );
273 
274  plinedata->length = length;
275  if (lf && (lf > text) && (lf[-1] == '\r')) {
276  plinedata->length--;
277  }
278 
279  text += length + 1;
280 
281  /* Break if there are no more linefeeds. */
282  if ( !lf )
283  break;
284  }
285 
286  /* Bump up the text height slightly. */
287  data->text_height += 2;
288  }
289 
290  /* Loop through all buttons and calculate the button widths and height. */
291  for ( i = 0; i < data->numbuttons; i++ ) {
292  int height;
293 
294  data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
295  data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
296 
297  GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
298  &data->buttonpos[ i ].text_width, &height );
299 
300  button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
301  button_text_height = IntMax( button_text_height, height );
302  }
303 
304  if ( data->numlines ) {
305  /* x,y for this line of text. */
306  data->xtext = data->text_height;
307  data->ytext = data->text_height + data->text_height;
308 
309  /* Bump button y down to bottom of text. */
310  ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
311 
312  /* Bump the dialog box width and height up if needed. */
313  data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
314  data->dialog_height = IntMax( data->dialog_height, ybuttons );
315  } else {
316  /* Button y starts at height of button text. */
317  ybuttons = button_text_height;
318  }
319 
320  if ( data->numbuttons ) {
321  int x, y;
322  int width_of_buttons;
323  int button_spacing = button_text_height;
324  int button_height = 2 * button_text_height;
325 
326  /* Bump button width up a bit. */
327  button_width += button_text_height;
328 
329  /* Get width of all buttons lined up. */
330  width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
331 
332  /* Bump up dialog width and height if buttons are wider than text. */
333  data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
334  data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
335 
336  /* Location for first button. */
337  x = ( data->dialog_width - width_of_buttons ) / 2;
338  y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
339 
340  for ( i = 0; i < data->numbuttons; i++ ) {
341  /* Button coordinates. */
342  data->buttonpos[ i ].rect.x = x;
343  data->buttonpos[ i ].rect.y = y;
344  data->buttonpos[ i ].rect.w = button_width;
345  data->buttonpos[ i ].rect.h = button_height;
346 
347  /* Button text coordinates. */
348  data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
349  data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
350 
351  /* Scoot over for next button. */
352  x += button_width + button_spacing;
353  }
354  }
355 
356  return 0;
357 }
358 
359 /* Free SDL_MessageBoxData data. */
360 static void
361 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
362 {
363  if ( data->font_set != NULL ) {
364  X11_XFreeFontSet( data->display, data->font_set );
365  data->font_set = NULL;
366  }
367 
368  if ( data->font_struct != NULL ) {
369  X11_XFreeFont( data->display, data->font_struct );
370  data->font_struct = NULL;
371  }
372 
373 #if SDL_VIDEO_DRIVER_X11_XDBE
374  if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
375  X11_XdbeDeallocateBackBufferName(data->display, data->buf);
376  }
377 #endif
378 
379  if ( data->display ) {
380  if ( data->window != None ) {
381  X11_XWithdrawWindow( data->display, data->window, data->screen );
382  X11_XDestroyWindow( data->display, data->window );
383  data->window = None;
384  }
385 
386  X11_XCloseDisplay( data->display );
387  data->display = NULL;
388  }
389 
390  SDL_free(data->linedata);
391 }
392 
393 /* Create and set up our X11 dialog box indow. */
394 static int
395 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
396 {
397  int x, y;
398  XSizeHints *sizehints;
399  XSetWindowAttributes wnd_attr;
400  Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME;
401  Display *display = data->display;
402  SDL_WindowData *windowdata = NULL;
403  const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
404  char *title_locale = NULL;
405 
406  if ( messageboxdata->window ) {
407  SDL_DisplayData *displaydata =
409  windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
410  data->screen = displaydata->screen;
411  } else {
412  data->screen = DefaultScreen( display );
413  }
414 
415  data->event_mask = ExposureMask |
416  ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
417  StructureNotifyMask | FocusChangeMask | PointerMotionMask;
418  wnd_attr.event_mask = data->event_mask;
419 
420  data->window = X11_XCreateWindow(
421  display, RootWindow(display, data->screen),
422  0, 0,
423  data->dialog_width, data->dialog_height,
424  0, CopyFromParent, InputOutput, CopyFromParent,
425  CWEventMask, &wnd_attr );
426  if ( data->window == None ) {
427  return SDL_SetError("Couldn't create X window");
428  }
429 
430  if ( windowdata ) {
431  Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False);
432  Atom stateatoms[16];
433  size_t statecount = 0;
434  /* Set some message-boxy window states when attached to a parent window... */
435  /* we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc */
436  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
437  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
438  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False);
439  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False);
440  SDL_assert(statecount <= SDL_arraysize(stateatoms));
441  X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32,
442  PropModeReplace, (unsigned char *)stateatoms, statecount);
443 
444  /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
445  X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
446  }
447 
448  X11_XStoreName( display, data->window, messageboxdata->title );
449  _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
450 
451  title_locale = SDL_iconv_utf8_locale(messageboxdata->title);
452  if (title_locale) {
453  XTextProperty titleprop;
454  Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
455  SDL_free(title_locale);
456  if (status) {
457  X11_XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
458  X11_XFree(titleprop.value);
459  }
460  }
461 
462 #ifdef X_HAVE_UTF8_STRING
463  if (SDL_X11_HAVE_UTF8) {
464  XTextProperty titleprop;
465  Status status = X11_Xutf8TextListToTextProperty(display, (char **) &messageboxdata->title, 1,
466  XUTF8StringStyle, &titleprop);
467  if (status == Success) {
468  X11_XSetTextProperty(display, data->window, &titleprop,
469  _NET_WM_NAME);
470  X11_XFree(titleprop.value);
471  }
472  }
473 #endif
474 
475  /* Let the window manager know this is a dialog box */
476  _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
477  _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
478  X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
479  PropModeReplace,
480  (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
481 
482  /* Allow the window to be deleted by the window manager */
483  data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
484  data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
485  X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
486 
487  if ( windowdata ) {
488  XWindowAttributes attrib;
489  Window dummy;
490 
491  X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
492  x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
493  y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
494  X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
495  } else {
496  const SDL_VideoDevice *dev = SDL_GetVideoDevice();
497  if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
498  const SDL_VideoDisplay *dpy = &dev->displays[0];
499  const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
500  x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
501  y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
502  } else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
503  x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
504  y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
505  }
506  }
507  X11_XMoveWindow( display, data->window, x, y );
508 
509  sizehints = X11_XAllocSizeHints();
510  if ( sizehints ) {
511  sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
512  sizehints->x = x;
513  sizehints->y = y;
514  sizehints->width = data->dialog_width;
515  sizehints->height = data->dialog_height;
516 
517  sizehints->min_width = sizehints->max_width = data->dialog_width;
518  sizehints->min_height = sizehints->max_height = data->dialog_height;
519 
520  X11_XSetWMNormalHints( display, data->window, sizehints );
521 
522  X11_XFree( sizehints );
523  }
524 
525  X11_XMapRaised( display, data->window );
526 
527 #if SDL_VIDEO_DRIVER_X11_XDBE
528  /* Initialise a back buffer for double buffering */
529  if (SDL_X11_HAVE_XDBE) {
530  int xdbe_major, xdbe_minor;
531  if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
532  data->xdbe = SDL_TRUE;
533  data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
534  } else {
535  data->xdbe = SDL_FALSE;
536  }
537  }
538 #endif
539 
540  return 0;
541 }
542 
543 /* Draw our message box. */
544 static void
545 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
546 {
547  int i;
548  Drawable window = data->window;
549  Display *display = data->display;
550 
551 #if SDL_VIDEO_DRIVER_X11_XDBE
552  if (SDL_X11_HAVE_XDBE && data->xdbe) {
553  window = data->buf;
554  X11_XdbeBeginIdiom(data->display);
555  }
556 #endif
557 
558  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
559  X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
560 
561  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
562  for ( i = 0; i < data->numlines; i++ ) {
563  TextLineData *plinedata = &data->linedata[ i ];
564 
565  if (SDL_X11_HAVE_UTF8) {
566  X11_Xutf8DrawString( display, window, data->font_set, ctx,
567  data->xtext, data->ytext + i * data->text_height,
568  plinedata->text, plinedata->length );
569  } else {
570  X11_XDrawString( display, window, ctx,
571  data->xtext, data->ytext + i * data->text_height,
572  plinedata->text, plinedata->length );
573  }
574  }
575 
576  for ( i = 0; i < data->numbuttons; i++ ) {
577  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
578  const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
579  int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
580  int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
581 
582  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
583  X11_XFillRectangle( display, window, ctx,
584  buttondatax11->rect.x - border, buttondatax11->rect.y - border,
585  buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
586 
587  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
588  X11_XDrawRectangle( display, window, ctx,
589  buttondatax11->rect.x, buttondatax11->rect.y,
590  buttondatax11->rect.w, buttondatax11->rect.h );
591 
592  X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
594  data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
595 
596  if (SDL_X11_HAVE_UTF8) {
597  X11_Xutf8DrawString( display, window, data->font_set, ctx,
598  buttondatax11->x + offset,
599  buttondatax11->y + offset,
600  buttondata->text, buttondatax11->length );
601  } else {
602  X11_XDrawString( display, window, ctx,
603  buttondatax11->x + offset, buttondatax11->y + offset,
604  buttondata->text, buttondatax11->length );
605  }
606  }
607 
608 #if SDL_VIDEO_DRIVER_X11_XDBE
609  if (SDL_X11_HAVE_XDBE && data->xdbe) {
610  XdbeSwapInfo swap_info;
611  swap_info.swap_window = data->window;
612  swap_info.swap_action = XdbeUndefined;
613  X11_XdbeSwapBuffers(data->display, &swap_info, 1);
614  X11_XdbeEndIdiom(data->display);
615  }
616 #endif
617 }
618 
619 static Bool
620 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
621 {
622  const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
623  return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
624 }
625 
626 /* Loop and handle message box event messages until something kills it. */
627 static int
628 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
629 {
630  GC ctx;
631  XGCValues ctx_vals;
632  SDL_bool close_dialog = SDL_FALSE;
633  SDL_bool has_focus = SDL_TRUE;
634  KeySym last_key_pressed = XK_VoidSymbol;
635  unsigned long gcflags = GCForeground | GCBackground;
636 
637  SDL_zero(ctx_vals);
638  ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
639  ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
640 
641  if (!SDL_X11_HAVE_UTF8) {
642  gcflags |= GCFont;
643  ctx_vals.font = data->font_struct->fid;
644  }
645 
646  ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
647  if ( ctx == None ) {
648  return SDL_SetError("Couldn't create graphics context");
649  }
650 
651  data->button_press_index = -1; /* Reset what button is currently depressed. */
652  data->mouse_over_index = -1; /* Reset what button the mouse is over. */
653 
654  while( !close_dialog ) {
655  XEvent e;
656  SDL_bool draw = SDL_TRUE;
657 
658  /* can't use XWindowEvent() because it can't handle ClientMessage events. */
659  /* can't use XNextEvent() because we only want events for this window. */
660  X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
661 
662  /* If X11_XFilterEvent returns True, then some input method has filtered the
663  event, and the client should discard the event. */
664  if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
665  continue;
666 
667  switch( e.type ) {
668  case Expose:
669  if ( e.xexpose.count > 0 ) {
670  draw = SDL_FALSE;
671  }
672  break;
673 
674  case FocusIn:
675  /* Got focus. */
676  has_focus = SDL_TRUE;
677  break;
678 
679  case FocusOut:
680  /* lost focus. Reset button and mouse info. */
681  has_focus = SDL_FALSE;
682  data->button_press_index = -1;
683  data->mouse_over_index = -1;
684  break;
685 
686  case MotionNotify:
687  if ( has_focus ) {
688  /* Mouse moved... */
689  const int previndex = data->mouse_over_index;
690  data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
691  if (data->mouse_over_index == previndex) {
692  draw = SDL_FALSE;
693  }
694  }
695  break;
696 
697  case ClientMessage:
698  if ( e.xclient.message_type == data->wm_protocols &&
699  e.xclient.format == 32 &&
700  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
701  close_dialog = SDL_TRUE;
702  }
703  break;
704 
705  case KeyPress:
706  /* Store key press - we make sure in key release that we got both. */
707  last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
708  break;
709 
710  case KeyRelease: {
711  Uint32 mask = 0;
712  KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
713 
714  /* If this is a key release for something we didn't get the key down for, then bail. */
715  if ( key != last_key_pressed )
716  break;
717 
718  if ( key == XK_Escape )
720  else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
722 
723  if ( mask ) {
724  int i;
725 
726  /* Look for first button with this mask set, and return it if found. */
727  for ( i = 0; i < data->numbuttons; i++ ) {
728  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
729 
730  if ( buttondatax11->buttondata->flags & mask ) {
731  *data->pbuttonid = buttondatax11->buttondata->buttonid;
732  close_dialog = SDL_TRUE;
733  break;
734  }
735  }
736  }
737  break;
738  }
739 
740  case ButtonPress:
741  data->button_press_index = -1;
742  if ( e.xbutton.button == Button1 ) {
743  /* Find index of button they clicked on. */
744  data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
745  }
746  break;
747 
748  case ButtonRelease:
749  /* If button is released over the same button that was clicked down on, then return it. */
750  if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
751  int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
752 
753  if ( data->button_press_index == button ) {
754  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
755 
756  *data->pbuttonid = buttondatax11->buttondata->buttonid;
757  close_dialog = SDL_TRUE;
758  }
759  }
760  data->button_press_index = -1;
761  break;
762  }
763 
764  if ( draw ) {
765  /* Draw our dialog box. */
766  X11_MessageBoxDraw( data, ctx );
767  }
768  }
769 
770  X11_XFreeGC( data->display, ctx );
771  return 0;
772 }
773 
774 static int
775 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
776 {
777  int ret;
778  SDL_MessageBoxDataX11 data;
779 #if SDL_SET_LOCALE
780  char *origlocale;
781 #endif
782 
783  SDL_zero(data);
784 
785  if ( !SDL_X11_LoadSymbols() )
786  return -1;
787 
788 #if SDL_SET_LOCALE
789  origlocale = setlocale(LC_ALL, NULL);
790  if (origlocale != NULL) {
791  origlocale = SDL_strdup(origlocale);
792  if (origlocale == NULL) {
793  return SDL_OutOfMemory();
794  }
795  setlocale(LC_ALL, "");
796  }
797 #endif
798 
799  /* This code could get called from multiple threads maybe? */
800  X11_XInitThreads();
801 
802  /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
803  *buttonid = -1;
804 
805  /* Init and display the message box. */
806  ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
807  if ( ret != -1 ) {
808  ret = X11_MessageBoxInitPositions( &data );
809  if ( ret != -1 ) {
810  ret = X11_MessageBoxCreateWindow( &data );
811  if ( ret != -1 ) {
812  ret = X11_MessageBoxLoop( &data );
813  }
814  }
815  }
816 
817  X11_MessageBoxShutdown( &data );
818 
819 #if SDL_SET_LOCALE
820  if (origlocale) {
821  setlocale(LC_ALL, origlocale);
822  SDL_free(origlocale);
823  }
824 #endif
825 
826  return ret;
827 }
828 
829 /* Display an x11 message box. */
830 int
831 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
832 {
833 #if SDL_FORK_MESSAGEBOX
834  /* Use a child process to protect against setlocale(). Annoying. */
835  pid_t pid;
836  int fds[2];
837  int status = 0;
838 
839  if (pipe(fds) == -1) {
840  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
841  }
842 
843  pid = fork();
844  if (pid == -1) { /* failed */
845  close(fds[0]);
846  close(fds[1]);
847  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
848  } else if (pid == 0) { /* we're the child */
849  int exitcode = 0;
850  close(fds[0]);
851  status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
852  if (write(fds[1], &status, sizeof (int)) != sizeof (int))
853  exitcode = 1;
854  else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
855  exitcode = 1;
856  close(fds[1]);
857  _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
858  } else { /* we're the parent */
859  pid_t rc;
860  close(fds[1]);
861  do {
862  rc = waitpid(pid, &status, 0);
863  } while ((rc == -1) && (errno == EINTR));
864 
865  SDL_assert(rc == pid); /* not sure what to do if this fails. */
866 
867  if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
868  return SDL_SetError("msgbox child process failed");
869  }
870 
871  if (read(fds[0], &status, sizeof (int)) != sizeof (int))
872  status = -1;
873  else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
874  status = -1;
875  close(fds[0]);
876 
877  return status;
878  }
879 #else
880  return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
881 #endif
882 }
883 #endif /* SDL_VIDEO_DRIVER_X11 */
884 
885 /* vi: set ts=4 sw=4 expandtab: */
SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
@ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
Definition: SDL_messagebox.h:75
SDL.h
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
screen
SDL_Renderer * screen
Definition: testgamecontroller.c:64
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:50
SDL_MessageBoxColorScheme::colors
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
Definition: SDL_messagebox.h:86
offset
GLintptr offset
Definition: SDL_opengl_glext.h:538
SDL_x11video.h
mask
GLenum GLint GLuint mask
Definition: SDL_opengl_glext.h:657
SDL_DisplayData::x
int x
Definition: SDL_x11modes.h:32
SDL_MessageBoxData::colorScheme
const SDL_MessageBoxColorScheme * colorScheme
Definition: SDL_messagebox.h:102
NULL
#define NULL
Definition: begin_code.h:167
b
GLboolean GLboolean GLboolean b
Definition: SDL_opengl_glext.h:1109
width
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
g
GLboolean GLboolean g
Definition: SDL_opengl_glext.h:1109
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:96
dpy
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display dpy)
Definition: SDL_x11sym.h:44
SDL_MESSAGEBOX_COLOR_TEXT
@ SDL_MESSAGEBOX_COLOR_TEXT
Definition: SDL_messagebox.h:74
SDL_MESSAGEBOX_COLOR_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BACKGROUND
Definition: SDL_messagebox.h:73
SDL_WindowData
Definition: SDL_androidwindow.h:38
SDL_GetDisplayForWindow
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
r
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2079
SDL_X11_LoadSymbols
int SDL_X11_LoadSymbols(void)
SDL_MessageBoxButtonData::flags
Uint32 flags
Definition: SDL_messagebox.h:58
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:97
SDL_iconv_utf8_locale
#define SDL_iconv_utf8_locale(S)
Definition: SDL_stdinc.h:562
a
GLboolean GLboolean GLboolean GLboolean a
Definition: SDL_opengl_glext.h:1109
SDL_DisplayData::y
int y
Definition: SDL_x11modes.h:33
SDL_x11messagebox.h
length
GLuint GLsizei GLsizei * length
Definition: SDL_opengl_glext.h:669
SDL_Rect::x
int x
Definition: SDL_rect.h:79
ctx
EGLContext ctx
Definition: eglext.h:208
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Rect::w
int w
Definition: SDL_rect.h:80
buf
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: SDL_opengl_glext.h:2480
event
struct _cl_event * event
Definition: SDL_opengl_glext.h:2649
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_WindowData::xwindow
Window xwindow
Definition: SDL_x11window.h:46
SDL_strchr
#define SDL_strchr
Definition: SDL_dynapi_overrides.h:401
SDL_VideoDevice::num_displays
int num_displays
Definition: SDL_sysvideo.h:315
SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
Definition: SDL_messagebox.h:76
retval
SDL_bool retval
Definition: testgamecontroller.c:65
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
color
GLuint color
Definition: SDL_opengl_glext.h:1148
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_Rect::y
int y
Definition: SDL_rect.h:79
SDL_INLINE
#define SDL_INLINE
Definition: begin_code.h:134
SDL_Rect::h
int h
Definition: SDL_rect.h:80
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
height
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:92
SDL_VideoDevice::displays
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:316
rect
SDL_Rect rect
Definition: testrelative.c:27
SDL_MessageBoxData::window
SDL_Window * window
Definition: SDL_messagebox.h:95
SDL_MessageBoxColor
RGB value used in a message box color scheme.
Definition: SDL_messagebox.h:66
SDL_assert.h
key
GLuint64 key
Definition: gl2ext.h:2192
text
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:56
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_VideoDisplay::driverdata
void * driverdata
Definition: SDL_sysvideo.h:139
SDL_DisplayData
Definition: SDL_cocoamodes.h:26
SDL_VideoDevice
Definition: SDL_sysvideo.h:148
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
@ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
Definition: SDL_messagebox.h:77
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:100
SDL_Window::driverdata
void * driverdata
Definition: SDL_sysvideo.h:111
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
Definition: SDL_messagebox.h:49
SDL_VideoDisplay
Definition: SDL_sysvideo.h:125
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_x11dyn.h
SDL_Rect
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:77
SDL_DisplayData::screen
int screen
Definition: SDL_x11modes.h:28
SDL_MessageBoxButtonData::text
const char * text
Definition: SDL_messagebox.h:60
e
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 Uint32 * e
Definition: SDL_dynapi_procs.h:117
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_strlen
#define SDL_strlen
Definition: SDL_dynapi_overrides.h:393
SDL_GetVideoDevice
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:583
SDL_MESSAGEBOX_COLOR_MAX
@ SDL_MESSAGEBOX_COLOR_MAX
Definition: SDL_messagebox.h:78
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
fds
EGLImageKHR int * fds
Definition: eglext.h:947
border
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: SDL_opengl.h:1572
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
button
SDL_Texture * button
Definition: testgamecontroller.c:67
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:99
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161