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 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  /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
432  X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
433  }
434 
435  X11_XStoreName( display, data->window, messageboxdata->title );
436  _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
437 
438  title_locale = SDL_iconv_utf8_locale(messageboxdata->title);
439  if (title_locale) {
440  XTextProperty titleprop;
441  Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
442  SDL_free(title_locale);
443  if (status) {
444  X11_XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
445  X11_XFree(titleprop.value);
446  }
447  }
448 
449 #ifdef X_HAVE_UTF8_STRING
450  if (SDL_X11_HAVE_UTF8) {
451  XTextProperty titleprop;
452  Status status = X11_Xutf8TextListToTextProperty(display, (char **) &messageboxdata->title, 1,
453  XUTF8StringStyle, &titleprop);
454  if (status == Success) {
455  X11_XSetTextProperty(display, data->window, &titleprop,
456  _NET_WM_NAME);
457  X11_XFree(titleprop.value);
458  }
459  }
460 #endif
461 
462  /* Let the window manager know this is a dialog box */
463  _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
464  _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
465  X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
466  PropModeReplace,
467  (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
468 
469  /* Allow the window to be deleted by the window manager */
470  data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
471  data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
472  X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
473 
474  if ( windowdata ) {
475  XWindowAttributes attrib;
476  Window dummy;
477 
478  X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
479  x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
480  y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
481  X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
482  } else {
483  const SDL_VideoDevice *dev = SDL_GetVideoDevice();
484  if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
485  const SDL_VideoDisplay *dpy = &dev->displays[0];
486  const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
487  x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
488  y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
489  } else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
490  x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
491  y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
492  }
493  }
494  X11_XMoveWindow( display, data->window, x, y );
495 
496  sizehints = X11_XAllocSizeHints();
497  if ( sizehints ) {
498  sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
499  sizehints->x = x;
500  sizehints->y = y;
501  sizehints->width = data->dialog_width;
502  sizehints->height = data->dialog_height;
503 
504  sizehints->min_width = sizehints->max_width = data->dialog_width;
505  sizehints->min_height = sizehints->max_height = data->dialog_height;
506 
507  X11_XSetWMNormalHints( display, data->window, sizehints );
508 
509  X11_XFree( sizehints );
510  }
511 
512  X11_XMapRaised( display, data->window );
513 
514 #if SDL_VIDEO_DRIVER_X11_XDBE
515  /* Initialise a back buffer for double buffering */
516  if (SDL_X11_HAVE_XDBE) {
517  int xdbe_major, xdbe_minor;
518  if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
519  data->xdbe = SDL_TRUE;
520  data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
521  } else {
522  data->xdbe = SDL_FALSE;
523  }
524  }
525 #endif
526 
527  return 0;
528 }
529 
530 /* Draw our message box. */
531 static void
532 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
533 {
534  int i;
535  Drawable window = data->window;
536  Display *display = data->display;
537 
538 #if SDL_VIDEO_DRIVER_X11_XDBE
539  if (SDL_X11_HAVE_XDBE && data->xdbe) {
540  window = data->buf;
541  X11_XdbeBeginIdiom(data->display);
542  }
543 #endif
544 
545  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
546  X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
547 
548  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
549  for ( i = 0; i < data->numlines; i++ ) {
550  TextLineData *plinedata = &data->linedata[ i ];
551 
552  if (SDL_X11_HAVE_UTF8) {
553  X11_Xutf8DrawString( display, window, data->font_set, ctx,
554  data->xtext, data->ytext + i * data->text_height,
555  plinedata->text, plinedata->length );
556  } else {
557  X11_XDrawString( display, window, ctx,
558  data->xtext, data->ytext + i * data->text_height,
559  plinedata->text, plinedata->length );
560  }
561  }
562 
563  for ( i = 0; i < data->numbuttons; i++ ) {
564  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
565  const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
566  int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
567  int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
568 
569  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
570  X11_XFillRectangle( display, window, ctx,
571  buttondatax11->rect.x - border, buttondatax11->rect.y - border,
572  buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
573 
574  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
575  X11_XDrawRectangle( display, window, ctx,
576  buttondatax11->rect.x, buttondatax11->rect.y,
577  buttondatax11->rect.w, buttondatax11->rect.h );
578 
579  X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
580  data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
581  data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
582 
583  if (SDL_X11_HAVE_UTF8) {
584  X11_Xutf8DrawString( display, window, data->font_set, ctx,
585  buttondatax11->x + offset,
586  buttondatax11->y + offset,
587  buttondata->text, buttondatax11->length );
588  } else {
589  X11_XDrawString( display, window, ctx,
590  buttondatax11->x + offset, buttondatax11->y + offset,
591  buttondata->text, buttondatax11->length );
592  }
593  }
594 
595 #if SDL_VIDEO_DRIVER_X11_XDBE
596  if (SDL_X11_HAVE_XDBE && data->xdbe) {
597  XdbeSwapInfo swap_info;
598  swap_info.swap_window = data->window;
599  swap_info.swap_action = XdbeUndefined;
600  X11_XdbeSwapBuffers(data->display, &swap_info, 1);
601  X11_XdbeEndIdiom(data->display);
602  }
603 #endif
604 }
605 
606 static Bool
607 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
608 {
609  const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
610  return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
611 }
612 
613 /* Loop and handle message box event messages until something kills it. */
614 static int
615 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
616 {
617  GC ctx;
618  XGCValues ctx_vals;
619  SDL_bool close_dialog = SDL_FALSE;
620  SDL_bool has_focus = SDL_TRUE;
621  KeySym last_key_pressed = XK_VoidSymbol;
622  unsigned long gcflags = GCForeground | GCBackground;
623 
624  SDL_zero(ctx_vals);
625  ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
626  ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
627 
628  if (!SDL_X11_HAVE_UTF8) {
629  gcflags |= GCFont;
630  ctx_vals.font = data->font_struct->fid;
631  }
632 
633  ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
634  if ( ctx == None ) {
635  return SDL_SetError("Couldn't create graphics context");
636  }
637 
638  data->button_press_index = -1; /* Reset what button is currently depressed. */
639  data->mouse_over_index = -1; /* Reset what button the mouse is over. */
640 
641  while( !close_dialog ) {
642  XEvent e;
644 
645  /* can't use XWindowEvent() because it can't handle ClientMessage events. */
646  /* can't use XNextEvent() because we only want events for this window. */
647  X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
648 
649  /* If X11_XFilterEvent returns True, then some input method has filtered the
650  event, and the client should discard the event. */
651  if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
652  continue;
653 
654  switch( e.type ) {
655  case Expose:
656  if ( e.xexpose.count > 0 ) {
657  draw = SDL_FALSE;
658  }
659  break;
660 
661  case FocusIn:
662  /* Got focus. */
663  has_focus = SDL_TRUE;
664  break;
665 
666  case FocusOut:
667  /* lost focus. Reset button and mouse info. */
668  has_focus = SDL_FALSE;
669  data->button_press_index = -1;
670  data->mouse_over_index = -1;
671  break;
672 
673  case MotionNotify:
674  if ( has_focus ) {
675  /* Mouse moved... */
676  const int previndex = data->mouse_over_index;
677  data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
678  if (data->mouse_over_index == previndex) {
679  draw = SDL_FALSE;
680  }
681  }
682  break;
683 
684  case ClientMessage:
685  if ( e.xclient.message_type == data->wm_protocols &&
686  e.xclient.format == 32 &&
687  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
688  close_dialog = SDL_TRUE;
689  }
690  break;
691 
692  case KeyPress:
693  /* Store key press - we make sure in key release that we got both. */
694  last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
695  break;
696 
697  case KeyRelease: {
698  Uint32 mask = 0;
699  KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
700 
701  /* If this is a key release for something we didn't get the key down for, then bail. */
702  if ( key != last_key_pressed )
703  break;
704 
705  if ( key == XK_Escape )
707  else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
709 
710  if ( mask ) {
711  int i;
712 
713  /* Look for first button with this mask set, and return it if found. */
714  for ( i = 0; i < data->numbuttons; i++ ) {
715  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
716 
717  if ( buttondatax11->buttondata->flags & mask ) {
718  *data->pbuttonid = buttondatax11->buttondata->buttonid;
719  close_dialog = SDL_TRUE;
720  break;
721  }
722  }
723  }
724  break;
725  }
726 
727  case ButtonPress:
728  data->button_press_index = -1;
729  if ( e.xbutton.button == Button1 ) {
730  /* Find index of button they clicked on. */
731  data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
732  }
733  break;
734 
735  case ButtonRelease:
736  /* If button is released over the same button that was clicked down on, then return it. */
737  if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
738  int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
739 
740  if ( data->button_press_index == button ) {
741  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
742 
743  *data->pbuttonid = buttondatax11->buttondata->buttonid;
744  close_dialog = SDL_TRUE;
745  }
746  }
747  data->button_press_index = -1;
748  break;
749  }
750 
751  if ( draw ) {
752  /* Draw our dialog box. */
753  X11_MessageBoxDraw( data, ctx );
754  }
755  }
756 
757  X11_XFreeGC( data->display, ctx );
758  return 0;
759 }
760 
761 static int
762 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
763 {
764  int ret;
765  SDL_MessageBoxDataX11 data;
766 #if SDL_SET_LOCALE
767  char *origlocale;
768 #endif
769 
770  SDL_zero(data);
771 
772  if ( !SDL_X11_LoadSymbols() )
773  return -1;
774 
775 #if SDL_SET_LOCALE
776  origlocale = setlocale(LC_ALL, NULL);
777  if (origlocale != NULL) {
778  origlocale = SDL_strdup(origlocale);
779  if (origlocale == NULL) {
780  return SDL_OutOfMemory();
781  }
782  setlocale(LC_ALL, "");
783  }
784 #endif
785 
786  /* This code could get called from multiple threads maybe? */
787  X11_XInitThreads();
788 
789  /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
790  *buttonid = -1;
791 
792  /* Init and display the message box. */
793  ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
794  if ( ret != -1 ) {
795  ret = X11_MessageBoxInitPositions( &data );
796  if ( ret != -1 ) {
797  ret = X11_MessageBoxCreateWindow( &data );
798  if ( ret != -1 ) {
799  ret = X11_MessageBoxLoop( &data );
800  }
801  }
802  }
803 
804  X11_MessageBoxShutdown( &data );
805 
806 #if SDL_SET_LOCALE
807  if (origlocale) {
808  setlocale(LC_ALL, origlocale);
809  SDL_free(origlocale);
810  }
811 #endif
812 
813  return ret;
814 }
815 
816 /* Display an x11 message box. */
817 int
818 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
819 {
820 #if SDL_FORK_MESSAGEBOX
821  /* Use a child process to protect against setlocale(). Annoying. */
822  pid_t pid;
823  int fds[2];
824  int status = 0;
825 
826  if (pipe(fds) == -1) {
827  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
828  }
829 
830  pid = fork();
831  if (pid == -1) { /* failed */
832  close(fds[0]);
833  close(fds[1]);
834  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
835  } else if (pid == 0) { /* we're the child */
836  int exitcode = 0;
837  close(fds[0]);
838  status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
839  if (write(fds[1], &status, sizeof (int)) != sizeof (int))
840  exitcode = 1;
841  else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
842  exitcode = 1;
843  close(fds[1]);
844  _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
845  } else { /* we're the parent */
846  pid_t rc;
847  close(fds[1]);
848  do {
849  rc = waitpid(pid, &status, 0);
850  } while ((rc == -1) && (errno == EINTR));
851 
852  SDL_assert(rc == pid); /* not sure what to do if this fails. */
853 
854  if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
855  return SDL_SetError("msgbox child process failed");
856  }
857 
858  if (read(fds[0], &status, sizeof (int)) != sizeof (int))
859  status = -1;
860  else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
861  status = -1;
862  close(fds[0]);
863 
864  return status;
865  }
866 #else
867  return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
868 #endif
869 }
870 #endif /* SDL_VIDEO_DRIVER_X11 */
871 
872 /* 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
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:562
SDL_bool retval
void draw()
Definition: keyboard.c:199
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:316
#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:161
#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:1092
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
#define SDL_strlen
int h
Definition: SDL_rect.h:67
#define SDL_strdup
uint32_t Uint32
Definition: SDL_stdinc.h:203
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
#define SDL_malloc
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