SDL  2.0
SDL_x11messagebox.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 
31 #include <X11/keysym.h>
32 #include <locale.h>
33 
34 
35 #define SDL_FORK_MESSAGEBOX 1
36 #define SDL_SET_LOCALE 1
37 
38 #if SDL_FORK_MESSAGEBOX
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #endif
44 
45 #define MAX_BUTTONS 8 /* Maximum number of buttons supported */
46 #define MAX_TEXT_LINES 32 /* Maximum number of text lines 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[] = "-*-*-*-*-*-*-*-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[ MAX_TEXT_LINES ];
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 /* Calculate and initialize text and button locations. */
226 static int
227 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
228 {
229  int i;
230  int ybuttons;
231  int text_width_max = 0;
232  int button_text_height = 0;
233  int button_width = MIN_BUTTON_WIDTH;
234  const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
235 
236  /* Go over text and break linefeeds into separate lines. */
237  if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
238  const char *text = messageboxdata->message;
239  TextLineData *plinedata = data->linedata;
240 
241  for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
242  int height;
243  char *lf = SDL_strchr( ( char * )text, '\n' );
244 
245  data->numlines++;
246 
247  /* Only grab length up to lf if it exists and isn't the last line. */
248  plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
249  plinedata->text = text;
250 
251  GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
252 
253  /* Text and widths are the largest we've ever seen. */
254  data->text_height = IntMax( data->text_height, height );
255  text_width_max = IntMax( text_width_max, plinedata->width );
256 
257  if (lf && (lf > text) && (lf[-1] == '\r')) {
258  plinedata->length--;
259  }
260 
261  text += plinedata->length + 1;
262 
263  /* Break if there are no more linefeeds. */
264  if ( !lf )
265  break;
266  }
267 
268  /* Bump up the text height slightly. */
269  data->text_height += 2;
270  }
271 
272  /* Loop through all buttons and calculate the button widths and height. */
273  for ( i = 0; i < data->numbuttons; i++ ) {
274  int height;
275 
276  data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
277  data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
278 
279  GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
280  &data->buttonpos[ i ].text_width, &height );
281 
282  button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
283  button_text_height = IntMax( button_text_height, height );
284  }
285 
286  if ( data->numlines ) {
287  /* x,y for this line of text. */
288  data->xtext = data->text_height;
289  data->ytext = data->text_height + data->text_height;
290 
291  /* Bump button y down to bottom of text. */
292  ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
293 
294  /* Bump the dialog box width and height up if needed. */
295  data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
296  data->dialog_height = IntMax( data->dialog_height, ybuttons );
297  } else {
298  /* Button y starts at height of button text. */
299  ybuttons = button_text_height;
300  }
301 
302  if ( data->numbuttons ) {
303  int x, y;
304  int width_of_buttons;
305  int button_spacing = button_text_height;
306  int button_height = 2 * button_text_height;
307 
308  /* Bump button width up a bit. */
309  button_width += button_text_height;
310 
311  /* Get width of all buttons lined up. */
312  width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
313 
314  /* Bump up dialog width and height if buttons are wider than text. */
315  data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
316  data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
317 
318  /* Location for first button. */
319  x = ( data->dialog_width - width_of_buttons ) / 2;
320  y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
321 
322  for ( i = 0; i < data->numbuttons; i++ ) {
323  /* Button coordinates. */
324  data->buttonpos[ i ].rect.x = x;
325  data->buttonpos[ i ].rect.y = y;
326  data->buttonpos[ i ].rect.w = button_width;
327  data->buttonpos[ i ].rect.h = button_height;
328 
329  /* Button text coordinates. */
330  data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
331  data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
332 
333  /* Scoot over for next button. */
334  x += button_width + button_spacing;
335  }
336  }
337 
338  return 0;
339 }
340 
341 /* Free SDL_MessageBoxData data. */
342 static void
343 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
344 {
345  if ( data->font_set != NULL ) {
346  X11_XFreeFontSet( data->display, data->font_set );
347  data->font_set = NULL;
348  }
349 
350  if ( data->font_struct != NULL ) {
351  X11_XFreeFont( data->display, data->font_struct );
352  data->font_struct = NULL;
353  }
354 
355 #if SDL_VIDEO_DRIVER_X11_XDBE
356  if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
357  X11_XdbeDeallocateBackBufferName(data->display, data->buf);
358  }
359 #endif
360 
361  if ( data->display ) {
362  if ( data->window != None ) {
363  X11_XWithdrawWindow( data->display, data->window, data->screen );
364  X11_XDestroyWindow( data->display, data->window );
365  data->window = None;
366  }
367 
368  X11_XCloseDisplay( data->display );
369  data->display = NULL;
370  }
371 }
372 
373 /* Create and set up our X11 dialog box indow. */
374 static int
375 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
376 {
377  int x, y;
378  XSizeHints *sizehints;
379  XSetWindowAttributes wnd_attr;
380  Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME, UTF8_STRING;
381  Display *display = data->display;
382  SDL_WindowData *windowdata = NULL;
383  const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
384 
385  if ( messageboxdata->window ) {
386  SDL_DisplayData *displaydata =
388  windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
389  data->screen = displaydata->screen;
390  } else {
391  data->screen = DefaultScreen( display );
392  }
393 
394  data->event_mask = ExposureMask |
395  ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
396  StructureNotifyMask | FocusChangeMask | PointerMotionMask;
397  wnd_attr.event_mask = data->event_mask;
398 
399  data->window = X11_XCreateWindow(
400  display, RootWindow(display, data->screen),
401  0, 0,
402  data->dialog_width, data->dialog_height,
403  0, CopyFromParent, InputOutput, CopyFromParent,
404  CWEventMask, &wnd_attr );
405  if ( data->window == None ) {
406  return SDL_SetError("Couldn't create X window");
407  }
408 
409  if ( windowdata ) {
410  /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
411  X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
412  }
413 
414  X11_XStoreName( display, data->window, messageboxdata->title );
415  _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
416  UTF8_STRING = X11_XInternAtom(display, "UTF8_STRING", False);
417  X11_XChangeProperty(display, data->window, _NET_WM_NAME, UTF8_STRING, 8,
418  PropModeReplace, (unsigned char *) messageboxdata->title,
419  strlen(messageboxdata->title) + 1 );
420 
421  /* Let the window manager know this is a dialog box */
422  _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
423  _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
424  X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
425  PropModeReplace,
426  (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
427 
428  /* Allow the window to be deleted by the window manager */
429  data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
430  data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
431  X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
432 
433  if ( windowdata ) {
434  XWindowAttributes attrib;
435  Window dummy;
436 
437  X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
438  x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
439  y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
440  X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
441  } else {
442  const SDL_VideoDevice *dev = SDL_GetVideoDevice();
443  if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
444  const SDL_VideoDisplay *dpy = &dev->displays[0];
445  const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
446  x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
447  y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
448  } else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
449  x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
450  y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
451  }
452  }
453  X11_XMoveWindow( display, data->window, x, y );
454 
455  sizehints = X11_XAllocSizeHints();
456  if ( sizehints ) {
457  sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
458  sizehints->x = x;
459  sizehints->y = y;
460  sizehints->width = data->dialog_width;
461  sizehints->height = data->dialog_height;
462 
463  sizehints->min_width = sizehints->max_width = data->dialog_width;
464  sizehints->min_height = sizehints->max_height = data->dialog_height;
465 
466  X11_XSetWMNormalHints( display, data->window, sizehints );
467 
468  X11_XFree( sizehints );
469  }
470 
471  X11_XMapRaised( display, data->window );
472 
473 #if SDL_VIDEO_DRIVER_X11_XDBE
474  /* Initialise a back buffer for double buffering */
475  if (SDL_X11_HAVE_XDBE) {
476  int xdbe_major, xdbe_minor;
477  if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
478  data->xdbe = SDL_TRUE;
479  data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
480  } else {
481  data->xdbe = SDL_FALSE;
482  }
483  }
484 #endif
485 
486  return 0;
487 }
488 
489 /* Draw our message box. */
490 static void
491 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
492 {
493  int i;
494  Drawable window = data->window;
495  Display *display = data->display;
496 
497 #if SDL_VIDEO_DRIVER_X11_XDBE
498  if (SDL_X11_HAVE_XDBE && data->xdbe) {
499  window = data->buf;
500  X11_XdbeBeginIdiom(data->display);
501  }
502 #endif
503 
504  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
505  X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
506 
507  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
508  for ( i = 0; i < data->numlines; i++ ) {
509  TextLineData *plinedata = &data->linedata[ i ];
510 
511  if (SDL_X11_HAVE_UTF8) {
512  X11_Xutf8DrawString( display, window, data->font_set, ctx,
513  data->xtext, data->ytext + i * data->text_height,
514  plinedata->text, plinedata->length );
515  } else {
516  X11_XDrawString( display, window, ctx,
517  data->xtext, data->ytext + i * data->text_height,
518  plinedata->text, plinedata->length );
519  }
520  }
521 
522  for ( i = 0; i < data->numbuttons; i++ ) {
523  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
524  const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
525  int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
526  int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
527 
528  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
529  X11_XFillRectangle( display, window, ctx,
530  buttondatax11->rect.x - border, buttondatax11->rect.y - border,
531  buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
532 
533  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
534  X11_XDrawRectangle( display, window, ctx,
535  buttondatax11->rect.x, buttondatax11->rect.y,
536  buttondatax11->rect.w, buttondatax11->rect.h );
537 
538  X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
539  data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
540  data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
541 
542  if (SDL_X11_HAVE_UTF8) {
543  X11_Xutf8DrawString( display, window, data->font_set, ctx,
544  buttondatax11->x + offset,
545  buttondatax11->y + offset,
546  buttondata->text, buttondatax11->length );
547  } else {
548  X11_XDrawString( display, window, ctx,
549  buttondatax11->x + offset, buttondatax11->y + offset,
550  buttondata->text, buttondatax11->length );
551  }
552  }
553 
554 #if SDL_VIDEO_DRIVER_X11_XDBE
555  if (SDL_X11_HAVE_XDBE && data->xdbe) {
556  XdbeSwapInfo swap_info;
557  swap_info.swap_window = data->window;
558  swap_info.swap_action = XdbeUndefined;
559  X11_XdbeSwapBuffers(data->display, &swap_info, 1);
560  X11_XdbeEndIdiom(data->display);
561  }
562 #endif
563 }
564 
565 static Bool
566 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
567 {
568  const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
569  return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
570 }
571 
572 /* Loop and handle message box event messages until something kills it. */
573 static int
574 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
575 {
576  GC ctx;
577  XGCValues ctx_vals;
578  SDL_bool close_dialog = SDL_FALSE;
579  SDL_bool has_focus = SDL_TRUE;
580  KeySym last_key_pressed = XK_VoidSymbol;
581  unsigned long gcflags = GCForeground | GCBackground;
582 
583  SDL_zero(ctx_vals);
584  ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
585  ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
586 
587  if (!SDL_X11_HAVE_UTF8) {
588  gcflags |= GCFont;
589  ctx_vals.font = data->font_struct->fid;
590  }
591 
592  ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
593  if ( ctx == None ) {
594  return SDL_SetError("Couldn't create graphics context");
595  }
596 
597  data->button_press_index = -1; /* Reset what button is currently depressed. */
598  data->mouse_over_index = -1; /* Reset what button the mouse is over. */
599 
600  while( !close_dialog ) {
601  XEvent e;
602  SDL_bool draw = SDL_TRUE;
603 
604  /* can't use XWindowEvent() because it can't handle ClientMessage events. */
605  /* can't use XNextEvent() because we only want events for this window. */
606  X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
607 
608  /* If X11_XFilterEvent returns True, then some input method has filtered the
609  event, and the client should discard the event. */
610  if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
611  continue;
612 
613  switch( e.type ) {
614  case Expose:
615  if ( e.xexpose.count > 0 ) {
616  draw = SDL_FALSE;
617  }
618  break;
619 
620  case FocusIn:
621  /* Got focus. */
622  has_focus = SDL_TRUE;
623  break;
624 
625  case FocusOut:
626  /* lost focus. Reset button and mouse info. */
627  has_focus = SDL_FALSE;
628  data->button_press_index = -1;
629  data->mouse_over_index = -1;
630  break;
631 
632  case MotionNotify:
633  if ( has_focus ) {
634  /* Mouse moved... */
635  const int previndex = data->mouse_over_index;
636  data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
637  if (data->mouse_over_index == previndex) {
638  draw = SDL_FALSE;
639  }
640  }
641  break;
642 
643  case ClientMessage:
644  if ( e.xclient.message_type == data->wm_protocols &&
645  e.xclient.format == 32 &&
646  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
647  close_dialog = SDL_TRUE;
648  }
649  break;
650 
651  case KeyPress:
652  /* Store key press - we make sure in key release that we got both. */
653  last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
654  break;
655 
656  case KeyRelease: {
657  Uint32 mask = 0;
658  KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
659 
660  /* If this is a key release for something we didn't get the key down for, then bail. */
661  if ( key != last_key_pressed )
662  break;
663 
664  if ( key == XK_Escape )
666  else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
668 
669  if ( mask ) {
670  int i;
671 
672  /* Look for first button with this mask set, and return it if found. */
673  for ( i = 0; i < data->numbuttons; i++ ) {
674  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
675 
676  if ( buttondatax11->buttondata->flags & mask ) {
677  *data->pbuttonid = buttondatax11->buttondata->buttonid;
678  close_dialog = SDL_TRUE;
679  break;
680  }
681  }
682  }
683  break;
684  }
685 
686  case ButtonPress:
687  data->button_press_index = -1;
688  if ( e.xbutton.button == Button1 ) {
689  /* Find index of button they clicked on. */
690  data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
691  }
692  break;
693 
694  case ButtonRelease:
695  /* If button is released over the same button that was clicked down on, then return it. */
696  if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
697  int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
698 
699  if ( data->button_press_index == button ) {
700  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
701 
702  *data->pbuttonid = buttondatax11->buttondata->buttonid;
703  close_dialog = SDL_TRUE;
704  }
705  }
706  data->button_press_index = -1;
707  break;
708  }
709 
710  if ( draw ) {
711  /* Draw our dialog box. */
712  X11_MessageBoxDraw( data, ctx );
713  }
714  }
715 
716  X11_XFreeGC( data->display, ctx );
717  return 0;
718 }
719 
720 static int
721 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
722 {
723  int ret;
724  SDL_MessageBoxDataX11 data;
725 #if SDL_SET_LOCALE
726  char *origlocale;
727 #endif
728 
729  SDL_zero(data);
730 
731  if ( !SDL_X11_LoadSymbols() )
732  return -1;
733 
734 #if SDL_SET_LOCALE
735  origlocale = setlocale(LC_ALL, NULL);
736  if (origlocale != NULL) {
737  origlocale = SDL_strdup(origlocale);
738  if (origlocale == NULL) {
739  return SDL_OutOfMemory();
740  }
741  setlocale(LC_ALL, "");
742  }
743 #endif
744 
745  /* This code could get called from multiple threads maybe? */
746  X11_XInitThreads();
747 
748  /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
749  *buttonid = -1;
750 
751  /* Init and display the message box. */
752  ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
753  if ( ret != -1 ) {
754  ret = X11_MessageBoxInitPositions( &data );
755  if ( ret != -1 ) {
756  ret = X11_MessageBoxCreateWindow( &data );
757  if ( ret != -1 ) {
758  ret = X11_MessageBoxLoop( &data );
759  }
760  }
761  }
762 
763  X11_MessageBoxShutdown( &data );
764 
765 #if SDL_SET_LOCALE
766  if (origlocale) {
767  setlocale(LC_ALL, origlocale);
768  SDL_free(origlocale);
769  }
770 #endif
771 
772  return ret;
773 }
774 
775 /* Display an x11 message box. */
776 int
777 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
778 {
779 #if SDL_FORK_MESSAGEBOX
780  /* Use a child process to protect against setlocale(). Annoying. */
781  pid_t pid;
782  int fds[2];
783  int status = 0;
784 
785  if (pipe(fds) == -1) {
786  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
787  }
788 
789  pid = fork();
790  if (pid == -1) { /* failed */
791  close(fds[0]);
792  close(fds[1]);
793  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
794  } else if (pid == 0) { /* we're the child */
795  int exitcode = 0;
796  close(fds[0]);
797  status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
798  if (write(fds[1], &status, sizeof (int)) != sizeof (int))
799  exitcode = 1;
800  else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
801  exitcode = 1;
802  close(fds[1]);
803  _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
804  } else { /* we're the parent */
805  pid_t rc;
806  close(fds[1]);
807  do {
808  rc = waitpid(pid, &status, 0);
809  } while ((rc == -1) && (errno == EINTR));
810 
811  SDL_assert(rc == pid); /* not sure what to do if this fails. */
812 
813  if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
814  return SDL_SetError("msgbox child process failed");
815  }
816 
817  if (read(fds[0], &status, sizeof (int)) != sizeof (int))
818  status = -1;
819  else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
820  status = -1;
821  close(fds[0]);
822 
823  return status;
824  }
825 #else
826  return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
827 #endif
828 }
829 #endif /* SDL_VIDEO_DRIVER_X11 */
830 
831 /* vi: set ts=4 sw=4 expandtab: */
const char * message
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2072
SDL_Texture * button
GLint GLint GLsizei width
Definition: SDL_opengl.h:1565
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:1567
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
static SDL_Window * window
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:159
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1565
RGB value used in a message box color scheme.
#define SDL_strchr
Individual button data.
GLboolean GLboolean g
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
struct _cl_event * event
void SDL_free(void *mem)
int SDL_X11_LoadSymbols(void)
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:130
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:293
#define SDL_zero(x)
Definition: SDL_stdinc.h:359
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.
GLenum GLint GLuint mask
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:167
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:130
GLintptr offset
#define SDL_SetError
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1058
#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:1565
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:571
#define SDL_INLINE
Definition: begin_code.h:120
void * driverdata
Definition: SDL_sysvideo.h:109
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 GLboolean b
int y
Definition: SDL_rect.h:66
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64