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