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