SDL  2.0
SDL_x11messagebox.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 
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  if ( messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT ) {
338  x = data->dialog_width - ( data->dialog_width - width_of_buttons ) / 2 - ( button_width + button_spacing );
339  } else {
340  x = ( data->dialog_width - width_of_buttons ) / 2;
341  }
342  y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
343 
344  for ( i = 0; i < data->numbuttons; i++ ) {
345  /* Button coordinates. */
346  data->buttonpos[ i ].rect.x = x;
347  data->buttonpos[ i ].rect.y = y;
348  data->buttonpos[ i ].rect.w = button_width;
349  data->buttonpos[ i ].rect.h = button_height;
350 
351  /* Button text coordinates. */
352  data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
353  data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
354 
355  /* Scoot over for next button. */
356  if ( messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT ) {
357  x -= button_width + button_spacing;
358  } else {
359  x += button_width + button_spacing;
360  }
361  }
362  }
363 
364  return 0;
365 }
366 
367 /* Free SDL_MessageBoxData data. */
368 static void
369 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
370 {
371  if ( data->font_set != NULL ) {
372  X11_XFreeFontSet( data->display, data->font_set );
373  data->font_set = NULL;
374  }
375 
376  if ( data->font_struct != NULL ) {
377  X11_XFreeFont( data->display, data->font_struct );
378  data->font_struct = NULL;
379  }
380 
381 #if SDL_VIDEO_DRIVER_X11_XDBE
382  if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
383  X11_XdbeDeallocateBackBufferName(data->display, data->buf);
384  }
385 #endif
386 
387  if ( data->display ) {
388  if ( data->window != None ) {
389  X11_XWithdrawWindow( data->display, data->window, data->screen );
390  X11_XDestroyWindow( data->display, data->window );
391  data->window = None;
392  }
393 
394  X11_XCloseDisplay( data->display );
395  data->display = NULL;
396  }
397 
398  SDL_free(data->linedata);
399 }
400 
401 /* Create and set up our X11 dialog box indow. */
402 static int
403 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
404 {
405  int x, y;
406  XSizeHints *sizehints;
407  XSetWindowAttributes wnd_attr;
408  Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME;
409  Display *display = data->display;
410  SDL_WindowData *windowdata = NULL;
411  const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
412  char *title_locale = NULL;
413 
414  if ( messageboxdata->window ) {
415  SDL_DisplayData *displaydata =
417  windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
418  data->screen = displaydata->screen;
419  } else {
420  data->screen = DefaultScreen( display );
421  }
422 
423  data->event_mask = ExposureMask |
424  ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
425  StructureNotifyMask | FocusChangeMask | PointerMotionMask;
426  wnd_attr.event_mask = data->event_mask;
427 
428  data->window = X11_XCreateWindow(
429  display, RootWindow(display, data->screen),
430  0, 0,
431  data->dialog_width, data->dialog_height,
432  0, CopyFromParent, InputOutput, CopyFromParent,
433  CWEventMask, &wnd_attr );
434  if ( data->window == None ) {
435  return SDL_SetError("Couldn't create X window");
436  }
437 
438  if ( windowdata ) {
439  Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False);
440  Atom stateatoms[16];
441  size_t statecount = 0;
442  /* Set some message-boxy window states when attached to a parent window... */
443  /* we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc */
444  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
445  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
446  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False);
447  stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False);
448  SDL_assert(statecount <= SDL_arraysize(stateatoms));
449  X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32,
450  PropModeReplace, (unsigned char *)stateatoms, statecount);
451 
452  /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
453  X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
454  }
455 
456  X11_XStoreName( display, data->window, messageboxdata->title );
457  _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
458 
459  title_locale = SDL_iconv_utf8_locale(messageboxdata->title);
460  if (title_locale) {
461  XTextProperty titleprop;
462  Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
463  SDL_free(title_locale);
464  if (status) {
465  X11_XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
466  X11_XFree(titleprop.value);
467  }
468  }
469 
470 #ifdef X_HAVE_UTF8_STRING
471  if (SDL_X11_HAVE_UTF8) {
472  XTextProperty titleprop;
473  Status status = X11_Xutf8TextListToTextProperty(display, (char **) &messageboxdata->title, 1,
474  XUTF8StringStyle, &titleprop);
475  if (status == Success) {
476  X11_XSetTextProperty(display, data->window, &titleprop,
477  _NET_WM_NAME);
478  X11_XFree(titleprop.value);
479  }
480  }
481 #endif
482 
483  /* Let the window manager know this is a dialog box */
484  _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
485  _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
486  X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
487  PropModeReplace,
488  (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
489 
490  /* Allow the window to be deleted by the window manager */
491  data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
492  data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
493  X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
494 
495  if ( windowdata ) {
496  XWindowAttributes attrib;
497  Window dummy;
498 
499  X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
500  x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
501  y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
502  X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
503  } else {
504  const SDL_VideoDevice *dev = SDL_GetVideoDevice();
505  if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
506  const SDL_VideoDisplay *dpy = &dev->displays[0];
507  const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
508  x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
509  y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
510  } else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
511  x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
512  y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
513  }
514  }
515  X11_XMoveWindow( display, data->window, x, y );
516 
517  sizehints = X11_XAllocSizeHints();
518  if ( sizehints ) {
519  sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
520  sizehints->x = x;
521  sizehints->y = y;
522  sizehints->width = data->dialog_width;
523  sizehints->height = data->dialog_height;
524 
525  sizehints->min_width = sizehints->max_width = data->dialog_width;
526  sizehints->min_height = sizehints->max_height = data->dialog_height;
527 
528  X11_XSetWMNormalHints( display, data->window, sizehints );
529 
530  X11_XFree( sizehints );
531  }
532 
533  X11_XMapRaised( display, data->window );
534 
535 #if SDL_VIDEO_DRIVER_X11_XDBE
536  /* Initialise a back buffer for double buffering */
537  if (SDL_X11_HAVE_XDBE) {
538  int xdbe_major, xdbe_minor;
539  if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
540  data->xdbe = SDL_TRUE;
541  data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
542  } else {
543  data->xdbe = SDL_FALSE;
544  }
545  }
546 #endif
547 
548  return 0;
549 }
550 
551 /* Draw our message box. */
552 static void
553 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
554 {
555  int i;
556  Drawable window = data->window;
557  Display *display = data->display;
558 
559 #if SDL_VIDEO_DRIVER_X11_XDBE
560  if (SDL_X11_HAVE_XDBE && data->xdbe) {
561  window = data->buf;
562  X11_XdbeBeginIdiom(data->display);
563  }
564 #endif
565 
566  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
567  X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
568 
569  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
570  for ( i = 0; i < data->numlines; i++ ) {
571  TextLineData *plinedata = &data->linedata[ i ];
572 
573  if (SDL_X11_HAVE_UTF8) {
574  X11_Xutf8DrawString( display, window, data->font_set, ctx,
575  data->xtext, data->ytext + i * data->text_height,
576  plinedata->text, plinedata->length );
577  } else {
578  X11_XDrawString( display, window, ctx,
579  data->xtext, data->ytext + i * data->text_height,
580  plinedata->text, plinedata->length );
581  }
582  }
583 
584  for ( i = 0; i < data->numbuttons; i++ ) {
585  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
586  const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
587  int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
588  int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
589 
590  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
591  X11_XFillRectangle( display, window, ctx,
592  buttondatax11->rect.x - border, buttondatax11->rect.y - border,
593  buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
594 
595  X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
596  X11_XDrawRectangle( display, window, ctx,
597  buttondatax11->rect.x, buttondatax11->rect.y,
598  buttondatax11->rect.w, buttondatax11->rect.h );
599 
600  X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
602  data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
603 
604  if (SDL_X11_HAVE_UTF8) {
605  X11_Xutf8DrawString( display, window, data->font_set, ctx,
606  buttondatax11->x + offset,
607  buttondatax11->y + offset,
608  buttondata->text, buttondatax11->length );
609  } else {
610  X11_XDrawString( display, window, ctx,
611  buttondatax11->x + offset, buttondatax11->y + offset,
612  buttondata->text, buttondatax11->length );
613  }
614  }
615 
616 #if SDL_VIDEO_DRIVER_X11_XDBE
617  if (SDL_X11_HAVE_XDBE && data->xdbe) {
618  XdbeSwapInfo swap_info;
619  swap_info.swap_window = data->window;
620  swap_info.swap_action = XdbeUndefined;
621  X11_XdbeSwapBuffers(data->display, &swap_info, 1);
622  X11_XdbeEndIdiom(data->display);
623  }
624 #endif
625 }
626 
627 static Bool
628 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
629 {
630  const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
631  return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
632 }
633 
634 /* Loop and handle message box event messages until something kills it. */
635 static int
636 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
637 {
638  GC ctx;
639  XGCValues ctx_vals;
640  SDL_bool close_dialog = SDL_FALSE;
641  SDL_bool has_focus = SDL_TRUE;
642  KeySym last_key_pressed = XK_VoidSymbol;
643  unsigned long gcflags = GCForeground | GCBackground;
644 
645  SDL_zero(ctx_vals);
646  ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
647  ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
648 
649  if (!SDL_X11_HAVE_UTF8) {
650  gcflags |= GCFont;
651  ctx_vals.font = data->font_struct->fid;
652  }
653 
654  ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
655  if ( ctx == None ) {
656  return SDL_SetError("Couldn't create graphics context");
657  }
658 
659  data->button_press_index = -1; /* Reset what button is currently depressed. */
660  data->mouse_over_index = -1; /* Reset what button the mouse is over. */
661 
662  while( !close_dialog ) {
663  XEvent e;
665 
666  /* can't use XWindowEvent() because it can't handle ClientMessage events. */
667  /* can't use XNextEvent() because we only want events for this window. */
668  X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
669 
670  /* If X11_XFilterEvent returns True, then some input method has filtered the
671  event, and the client should discard the event. */
672  if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
673  continue;
674 
675  switch( e.type ) {
676  case Expose:
677  if ( e.xexpose.count > 0 ) {
678  draw = SDL_FALSE;
679  }
680  break;
681 
682  case FocusIn:
683  /* Got focus. */
684  has_focus = SDL_TRUE;
685  break;
686 
687  case FocusOut:
688  /* lost focus. Reset button and mouse info. */
689  has_focus = SDL_FALSE;
690  data->button_press_index = -1;
691  data->mouse_over_index = -1;
692  break;
693 
694  case MotionNotify:
695  if ( has_focus ) {
696  /* Mouse moved... */
697  const int previndex = data->mouse_over_index;
698  data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
699  if (data->mouse_over_index == previndex) {
700  draw = SDL_FALSE;
701  }
702  }
703  break;
704 
705  case ClientMessage:
706  if ( e.xclient.message_type == data->wm_protocols &&
707  e.xclient.format == 32 &&
708  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
709  close_dialog = SDL_TRUE;
710  }
711  break;
712 
713  case KeyPress:
714  /* Store key press - we make sure in key release that we got both. */
715  last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
716  break;
717 
718  case KeyRelease: {
719  Uint32 mask = 0;
720  KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
721 
722  /* If this is a key release for something we didn't get the key down for, then bail. */
723  if ( key != last_key_pressed )
724  break;
725 
726  if ( key == XK_Escape )
728  else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
730 
731  if ( mask ) {
732  int i;
733 
734  /* Look for first button with this mask set, and return it if found. */
735  for ( i = 0; i < data->numbuttons; i++ ) {
736  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
737 
738  if ( buttondatax11->buttondata->flags & mask ) {
739  *data->pbuttonid = buttondatax11->buttondata->buttonid;
740  close_dialog = SDL_TRUE;
741  break;
742  }
743  }
744  }
745  break;
746  }
747 
748  case ButtonPress:
749  data->button_press_index = -1;
750  if ( e.xbutton.button == Button1 ) {
751  /* Find index of button they clicked on. */
752  data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
753  }
754  break;
755 
756  case ButtonRelease:
757  /* If button is released over the same button that was clicked down on, then return it. */
758  if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
759  int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
760 
761  if ( data->button_press_index == button ) {
762  SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
763 
764  *data->pbuttonid = buttondatax11->buttondata->buttonid;
765  close_dialog = SDL_TRUE;
766  }
767  }
768  data->button_press_index = -1;
769  break;
770  }
771 
772  if ( draw ) {
773  /* Draw our dialog box. */
774  X11_MessageBoxDraw( data, ctx );
775  }
776  }
777 
778  X11_XFreeGC( data->display, ctx );
779  return 0;
780 }
781 
782 static int
783 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
784 {
785  int ret;
786  SDL_MessageBoxDataX11 data;
787 #if SDL_SET_LOCALE
788  char *origlocale;
789 #endif
790 
791  SDL_zero(data);
792 
793  if ( !SDL_X11_LoadSymbols() )
794  return -1;
795 
796 #if SDL_SET_LOCALE
797  origlocale = setlocale(LC_ALL, NULL);
798  if (origlocale != NULL) {
799  origlocale = SDL_strdup(origlocale);
800  if (origlocale == NULL) {
801  return SDL_OutOfMemory();
802  }
803  setlocale(LC_ALL, "");
804  }
805 #endif
806 
807  /* This code could get called from multiple threads maybe? */
808  X11_XInitThreads();
809 
810  /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
811  *buttonid = -1;
812 
813  /* Init and display the message box. */
814  ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
815  if ( ret != -1 ) {
816  ret = X11_MessageBoxInitPositions( &data );
817  if ( ret != -1 ) {
818  ret = X11_MessageBoxCreateWindow( &data );
819  if ( ret != -1 ) {
820  ret = X11_MessageBoxLoop( &data );
821  }
822  }
823  }
824 
825  X11_MessageBoxShutdown( &data );
826 
827 #if SDL_SET_LOCALE
828  if (origlocale) {
829  setlocale(LC_ALL, origlocale);
830  SDL_free(origlocale);
831  }
832 #endif
833 
834  return ret;
835 }
836 
837 /* Display an x11 message box. */
838 int
839 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
840 {
841 #if SDL_FORK_MESSAGEBOX
842  /* Use a child process to protect against setlocale(). Annoying. */
843  pid_t pid;
844  int fds[2];
845  int status = 0;
846 
847  if (pipe(fds) == -1) {
848  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
849  }
850 
851  pid = fork();
852  if (pid == -1) { /* failed */
853  close(fds[0]);
854  close(fds[1]);
855  return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
856  } else if (pid == 0) { /* we're the child */
857  int exitcode = 0;
858  close(fds[0]);
859  status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
860  if (write(fds[1], &status, sizeof (int)) != sizeof (int))
861  exitcode = 1;
862  else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
863  exitcode = 1;
864  close(fds[1]);
865  _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
866  } else { /* we're the parent */
867  pid_t rc;
868  close(fds[1]);
869  do {
870  rc = waitpid(pid, &status, 0);
871  } while ((rc == -1) && (errno == EINTR));
872 
873  SDL_assert(rc == pid); /* not sure what to do if this fails. */
874 
875  if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
876  return SDL_SetError("msgbox child process failed");
877  }
878 
879  if (read(fds[0], &status, sizeof (int)) != sizeof (int))
880  status = -1;
881  else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
882  status = -1;
883  close(fds[0]);
884 
885  return status;
886  }
887 #else
888  return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
889 #endif
890 }
891 #endif /* SDL_VIDEO_DRIVER_X11 */
892 
893 /* vi: set ts=4 sw=4 expandtab: */
SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
@ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER
Definition: SDL_messagebox.h:77
SDL.h
SDL_zero
#define SDL_zero(x)
Definition: SDL_stdinc.h:418
Uint32
uint32_t Uint32
Definition: SDL_stdinc.h:203
screen
SDL_Renderer * screen
Definition: testgamecontroller.c:64
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
Definition: SDL_messagebox.h:52
SDL_MessageBoxColorScheme::colors
SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]
Definition: SDL_messagebox.h:88
offset
GLintptr offset
Definition: SDL_opengl_glext.h:541
SDL_x11video.h
mask
GLenum GLint GLuint mask
Definition: SDL_opengl_glext.h:660
SDL_DisplayData::x
int x
Definition: SDL_x11modes.h:32
SDL_MessageBoxData::colorScheme
const SDL_MessageBoxColorScheme * colorScheme
Definition: SDL_messagebox.h:104
NULL
#define NULL
Definition: begin_code.h:167
b
GLboolean GLboolean GLboolean b
Definition: SDL_opengl_glext.h:1112
width
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
g
GLboolean GLboolean g
Definition: SDL_opengl_glext.h:1112
SDL_MessageBoxData::title
const char * title
Definition: SDL_messagebox.h:98
dpy
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display dpy)
Definition: SDL_x11sym.h:44
SDL_MESSAGEBOX_COLOR_TEXT
@ SDL_MESSAGEBOX_COLOR_TEXT
Definition: SDL_messagebox.h:76
SDL_MESSAGEBOX_COLOR_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BACKGROUND
Definition: SDL_messagebox.h:75
SDL_WindowData
Definition: SDL_androidwindow.h:38
SDL_GetDisplayForWindow
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1110
r
GLdouble GLdouble GLdouble r
Definition: SDL_opengl.h:2079
SDL_X11_LoadSymbols
int SDL_X11_LoadSymbols(void)
SDL_MessageBoxButtonData::flags
Uint32 flags
Definition: SDL_messagebox.h:60
SDL_MessageBoxData::message
const char * message
Definition: SDL_messagebox.h:99
SDL_iconv_utf8_locale
#define SDL_iconv_utf8_locale(S)
Definition: SDL_stdinc.h:571
a
GLboolean GLboolean GLboolean GLboolean a
Definition: SDL_opengl_glext.h:1112
SDL_DisplayData::y
int y
Definition: SDL_x11modes.h:33
SDL_x11messagebox.h
length
GLuint GLsizei GLsizei * length
Definition: SDL_opengl_glext.h:672
SDL_Rect::x
int x
Definition: SDL_rect.h:79
ctx
EGLContext ctx
Definition: eglext.h:208
data
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
SDL_Rect::w
int w
Definition: SDL_rect.h:80
buf
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: SDL_opengl_glext.h:2483
event
struct _cl_event * event
Definition: SDL_opengl_glext.h:2652
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_WindowData::xwindow
Window xwindow
Definition: SDL_x11window.h:46
SDL_strchr
#define SDL_strchr
Definition: SDL_dynapi_overrides.h:401
SDL_VideoDevice::num_displays
int num_displays
Definition: SDL_sysvideo.h:323
SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
@ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND
Definition: SDL_messagebox.h:78
retval
SDL_bool retval
Definition: testgamecontroller.c:65
SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT
@ SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT
Definition: SDL_messagebox.h:43
x
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
color
GLuint color
Definition: SDL_opengl_glext.h:1151
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:1025
SDL_Rect::y
int y
Definition: SDL_rect.h:79
SDL_INLINE
#define SDL_INLINE
Definition: begin_code.h:134
SDL_Rect::h
int h
Definition: SDL_rect.h:80
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_MessageBoxData::flags
Uint32 flags
Definition: SDL_messagebox.h:96
height
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
SDL_MessageBoxData
MessageBox structure containing title, text, window, etc.
Definition: SDL_messagebox.h:94
SDL_VideoDevice::displays
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:324
rect
SDL_Rect rect
Definition: testrelative.c:27
SDL_MessageBoxData::window
SDL_Window * window
Definition: SDL_messagebox.h:97
SDL_MessageBoxColor
RGB value used in a message box color scheme.
Definition: SDL_messagebox.h:68
SDL_assert.h
key
GLuint64 key
Definition: gl2ext.h:2192
text
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_MessageBoxButtonData
Individual button data.
Definition: SDL_messagebox.h:58
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_VideoDisplay::driverdata
void * driverdata
Definition: SDL_sysvideo.h:140
SDL_DisplayData
Definition: SDL_cocoamodes.h:26
SDL_VideoDevice
Definition: SDL_sysvideo.h:149
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
y
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
draw
void draw()
Definition: testoffscreen.c:36
SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
@ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED
Definition: SDL_messagebox.h:79
SDL_arraysize
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
SDL_MessageBoxData::buttons
const SDL_MessageBoxButtonData * buttons
Definition: SDL_messagebox.h:102
SDL_Window::driverdata
void * driverdata
Definition: SDL_sysvideo.h:112
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
@ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
Definition: SDL_messagebox.h:51
SDL_VideoDisplay
Definition: SDL_sysvideo.h:126
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_x11dyn.h
SDL_Rect
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:77
SDL_DisplayData::screen
int screen
Definition: SDL_x11modes.h:28
SDL_MessageBoxButtonData::text
const char * text
Definition: SDL_messagebox.h:62
e
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 Uint32 * e
Definition: SDL_dynapi_procs.h:117
SDL_strdup
#define SDL_strdup
Definition: SDL_dynapi_overrides.h:397
SDL_strlen
#define SDL_strlen
Definition: SDL_dynapi_overrides.h:393
SDL_GetVideoDevice
SDL_VideoDevice * SDL_GetVideoDevice(void)
Definition: SDL_video.c:586
SDL_MESSAGEBOX_COLOR_MAX
@ SDL_MESSAGEBOX_COLOR_MAX
Definition: SDL_messagebox.h:80
SDL_malloc
#define SDL_malloc
Definition: SDL_dynapi_overrides.h:374
fds
EGLImageKHR int * fds
Definition: eglext.h:947
border
GLint GLint GLsizei GLsizei GLsizei GLint border
Definition: SDL_opengl.h:1572
button
SDL_Texture * button
Definition: testgamecontroller.c:67
SDL_MessageBoxData::numbuttons
int numbuttons
Definition: SDL_messagebox.h:101
i
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:161