src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 08 Dec 2012 15:33:14 -0500
changeset 6726 f2898757eaae
parent 6725 a7a99a9d419a
child 6758 2a218048b45f
permissions -rw-r--r--
Fork a child process for X11 message boxes, so setlocale() doesn't break stuff.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.h"
    23 
    24 #if SDL_VIDEO_DRIVER_X11
    25 
    26 #include "SDL.h"
    27 #include "SDL_x11video.h"
    28 #include "SDL_x11dyn.h"
    29 #include "SDL_assert.h"
    30 
    31 #include <locale.h>
    32 
    33 
    34 #define SDL_FORK_MESSAGEBOX 1
    35 
    36 #if SDL_FORK_MESSAGEBOX
    37 #include <sys/types.h>
    38 #include <sys/wait.h>
    39 #include <unistd.h>
    40 #include <errno.h>
    41 #endif
    42 
    43 #define MAX_BUTTONS             8       /* Maximum number of buttons supported */
    44 #define MAX_TEXT_LINES          32      /* Maximum number of text lines supported */
    45 #define MIN_BUTTON_WIDTH        64      /* Minimum button width */
    46 #define MIN_DIALOG_WIDTH        200     /* Minimum dialog width */
    47 #define MIN_DIALOG_HEIGHT       100     /* Minimum dialog height */
    48 
    49 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
    50 static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-*-*-*-*-*-*-*";
    51 
    52 static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = {
    53     { 56,  54,  53  }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
    54     { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT,
    55     { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER,
    56     { 105, 102, 99  }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND,
    57     { 205, 202, 53  }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED,
    58 };
    59 
    60 #define SDL_MAKE_RGB( _r, _g, _b )  ( ( ( Uint32 )( _r ) << 16 ) | \
    61                                       ( ( Uint32 )( _g ) << 8 ) |  \
    62                                       ( ( Uint32 )( _b ) ) )
    63 
    64 typedef struct SDL_MessageBoxButtonDataX11 {
    65     int x, y;                           /* Text position */
    66     int length;                         /* Text length */
    67     int text_width;                     /* Text width */
    68 
    69     SDL_Rect rect;                      /* Rectangle for entire button */
    70 
    71     const SDL_MessageBoxButtonData *buttondata;   /* Button data from caller */
    72 } SDL_MessageBoxButtonDataX11;
    73 
    74 typedef struct TextLineData {
    75     int width;                          /* Width of this text line */
    76     int length;                         /* String length of this text line */
    77     const char *text;                   /* Text for this line */
    78 } TextLineData;
    79 
    80 typedef struct SDL_MessageBoxDataX11 {
    81     XFontSet font_set;                  /* for UTF-8 systems */
    82     XFontStruct *font_struct;            /* Latin1 (ASCII) fallback. */
    83     Window window;
    84     Display *display;
    85     long event_mask;
    86     Atom wm_protocols;
    87     Atom wm_delete_message;
    88 
    89     int dialog_width;                   /* Dialog box width. */
    90     int dialog_height;                  /* Dialog box height. */
    91 
    92     int xtext, ytext;                   /* Text position to start drawing at. */
    93     int numlines;                       /* Count of Text lines. */
    94     int text_height;                    /* Height for text lines. */
    95     TextLineData linedata[ MAX_TEXT_LINES ];
    96 
    97     int *pbuttonid;                     /* Pointer to user return buttonid value. */
    98 
    99     int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
   100     int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
   101 
   102     int numbuttons;                     /* Count of buttons. */
   103     const SDL_MessageBoxButtonData *buttondata;
   104     SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
   105 
   106     Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
   107 
   108     const SDL_MessageBoxData *messageboxdata;
   109 } SDL_MessageBoxDataX11;
   110 
   111 /* Maximum helper for ints. */
   112 static __inline__ int
   113 IntMax( int a, int b )
   114 {
   115     return ( a > b  ) ? a : b;
   116 }
   117 
   118 /* Return width and height for a string. */
   119 static void
   120 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
   121 {
   122     if (SDL_X11_HAVE_UTF8) {
   123         XRectangle overall_ink, overall_logical;
   124         Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
   125         *pwidth = overall_logical.width;
   126         *pheight = overall_logical.height;
   127     } else {
   128         XCharStruct text_structure;
   129         int font_direction, font_ascent, font_descent;
   130         XTextExtents( data->font_struct, str, nbytes,
   131                       &font_direction, &font_ascent, &font_descent,
   132                       &text_structure );
   133         *pwidth = text_structure.width;
   134         *pheight = text_structure.ascent + text_structure.descent;
   135     }
   136 }
   137 
   138 /* Return index of button if position x,y is contained therein. */
   139 static int
   140 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
   141 {
   142     int i;
   143     int numbuttons = data->numbuttons;
   144     SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
   145 
   146     for ( i = 0; i < numbuttons; i++ ) {
   147         SDL_Rect *rect = &buttonpos[ i ].rect;
   148 
   149         if ( ( x >= rect->x ) &&
   150              ( x <= ( rect->x + rect->w ) ) &&
   151              ( y >= rect->y ) &&
   152              ( y <= ( rect->y + rect->h ) ) ) {
   153             return i;
   154         }
   155     }
   156 
   157     return -1;
   158 }
   159 
   160 /* Initialize SDL_MessageBoxData structure and Display, etc. */
   161 static int
   162 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
   163 {
   164     int i;
   165     int numbuttons = messageboxdata->numbuttons;
   166     const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
   167     const SDL_MessageBoxColor *colorhints;
   168 
   169     if ( numbuttons > MAX_BUTTONS ) {
   170         SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
   171         return -1;
   172     }
   173 
   174     data->dialog_width = MIN_DIALOG_WIDTH;
   175     data->dialog_height = MIN_DIALOG_HEIGHT;
   176     data->messageboxdata = messageboxdata;
   177     data->buttondata = buttondata;
   178     data->numbuttons = numbuttons;
   179     data->pbuttonid = pbuttonid;
   180 
   181     data->display = XOpenDisplay( NULL );
   182     if ( !data->display ) {
   183         SDL_SetError("Couldn't open X11 display");
   184         return -1;
   185     }
   186 
   187     if (SDL_X11_HAVE_UTF8) {
   188         char **missing = NULL;
   189         int num_missing = 0;
   190         data->font_set = XCreateFontSet(data->display, g_MessageBoxFont,
   191                                         &missing, &num_missing, NULL);
   192         if ( missing != NULL ) {
   193             XFreeStringList(missing);
   194         }
   195         if ( data->font_set == NULL ) {
   196             SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
   197             return -1;
   198         }
   199     } else {
   200         data->font_struct = XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
   201         if ( data->font_struct == NULL ) {
   202             SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
   203             return -1;
   204         }
   205     }
   206 
   207     if ( messageboxdata->colorScheme ) {
   208         colorhints = messageboxdata->colorScheme->colors;
   209     } else {
   210         colorhints = g_default_colors;
   211     }
   212 
   213     /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
   214     for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
   215         data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
   216     }
   217 
   218     return 0;
   219 }
   220 
   221 /* Calculate and initialize text and button locations. */
   222 static int
   223 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
   224 {
   225     int i;
   226     int ybuttons;
   227     int text_width_max = 0;
   228     int button_text_height = 0;
   229     int button_width = MIN_BUTTON_WIDTH;
   230     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   231 
   232     /* Go over text and break linefeeds into separate lines. */
   233     if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
   234         const char *text = messageboxdata->message;
   235         TextLineData *plinedata = data->linedata;
   236 
   237         for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
   238             int height;
   239             char *lf = SDL_strchr( ( char * )text, '\n' );
   240 
   241             data->numlines++;
   242 
   243             /* Only grab length up to lf if it exists and isn't the last line. */
   244             plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
   245             plinedata->text = text;
   246 
   247             GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
   248 
   249             /* Text and widths are the largest we've ever seen. */
   250             data->text_height = IntMax( data->text_height, height );
   251             text_width_max = IntMax( text_width_max, plinedata->width );
   252 
   253             text += plinedata->length + 1;
   254 
   255             /* Break if there are no more linefeeds. */
   256             if ( !lf )
   257                 break;
   258         }
   259 
   260         /* Bump up the text height slightly. */
   261         data->text_height += 2;
   262     }
   263 
   264     /* Loop through all buttons and calculate the button widths and height. */
   265     for ( i = 0; i < data->numbuttons; i++ ) {
   266         int height;
   267 
   268         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
   269         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
   270 
   271         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
   272                             &data->buttonpos[ i ].text_width, &height );
   273 
   274         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
   275         button_text_height = IntMax( button_text_height, height );
   276     }
   277 
   278     if ( data->numlines ) {
   279         /* x,y for this line of text. */
   280         data->xtext = data->text_height;
   281         data->ytext = data->text_height + data->text_height;
   282 
   283         /* Bump button y down to bottom of text. */
   284         ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
   285 
   286         /* Bump the dialog box width and height up if needed. */
   287         data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
   288         data->dialog_height = IntMax( data->dialog_height, ybuttons );
   289     } else {
   290         /* Button y starts at height of button text. */
   291         ybuttons = button_text_height;
   292     }
   293 
   294     if ( data->numbuttons ) {
   295         int x, y;
   296         int width_of_buttons;
   297         int button_spacing = button_text_height;
   298         int button_height = 2 * button_text_height;
   299 
   300         /* Bump button width up a bit. */
   301         button_width += button_text_height;
   302 
   303         /* Get width of all buttons lined up. */
   304         width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
   305 
   306         /* Bump up dialog width and height if buttons are wider than text. */
   307         data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
   308         data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
   309 
   310         /* Location for first button. */
   311         x = ( data->dialog_width - width_of_buttons ) / 2;
   312         y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
   313 
   314         for ( i = 0; i < data->numbuttons; i++ ) {
   315             /* Button coordinates. */
   316             data->buttonpos[ i ].rect.x = x;
   317             data->buttonpos[ i ].rect.y = y;
   318             data->buttonpos[ i ].rect.w = button_width;
   319             data->buttonpos[ i ].rect.h = button_height;
   320 
   321             /* Button text coordinates. */
   322             data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
   323             data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
   324 
   325             /* Scoot over for next button. */
   326             x += button_width + button_spacing;
   327         }
   328     }
   329 
   330     return 0;
   331 }
   332 
   333 /* Free SDL_MessageBoxData data. */
   334 static void
   335 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
   336 {
   337     if ( data->font_set != NULL ) {
   338         XFreeFontSet( data->display, data->font_set );
   339         data->font_set = NULL;
   340     }
   341 
   342     if ( data->font_struct != NULL ) {
   343         XFreeFont( data->display, data->font_struct );
   344         data->font_struct = NULL;
   345     }
   346 
   347     if ( data->display ) {
   348         if ( data->window != None ) {
   349             XUnmapWindow( data->display, data->window );
   350             XDestroyWindow( data->display, data->window );
   351             data->window = None;
   352         }
   353 
   354         XCloseDisplay( data->display );
   355         data->display = NULL;
   356     }
   357 }
   358 
   359 /* Create and set up our X11 dialog box indow. */
   360 static int
   361 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
   362 {
   363     int x, y;
   364     XSizeHints *sizehints;
   365     XSetWindowAttributes wnd_attr;
   366     Display *display = data->display;
   367     SDL_WindowData *windowdata = NULL;
   368     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   369 
   370     if ( messageboxdata->window ) {
   371         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   372     }
   373 
   374     data->event_mask = ExposureMask |
   375                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   376                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   377     wnd_attr.event_mask = data->event_mask;
   378 
   379     data->window = XCreateWindow(
   380                        display, DefaultRootWindow( display ),
   381                        0, 0,
   382                        data->dialog_width, data->dialog_height,
   383                        0, CopyFromParent, InputOutput, CopyFromParent,
   384                        CWEventMask, &wnd_attr );
   385     if ( data->window == None ) {
   386         SDL_SetError("Couldn't create X window");
   387         return -1;
   388     }
   389 
   390     if ( windowdata ) {
   391         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
   392         XSetTransientForHint( display, data->window, windowdata->xwindow );
   393     }
   394 
   395     XStoreName( display, data->window, messageboxdata->title );
   396 
   397     /* Allow the window to be deleted by the window manager */
   398     data->wm_protocols = XInternAtom( display, "WM_PROTOCOLS", False );
   399     data->wm_delete_message = XInternAtom( display, "WM_DELETE_WINDOW", False );
   400     XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
   401 
   402     if ( windowdata ) {
   403         XWindowAttributes attrib;
   404         Window dummy;
   405 
   406         XGetWindowAttributes(display, windowdata->xwindow, &attrib);
   407         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
   408         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
   409         XTranslateCoordinates(display, windowdata->xwindow, DefaultRootWindow( display ), x, y, &x, &y, &dummy);
   410     } else {
   411         int screen = DefaultScreen( display );
   412         x = ( DisplayWidth( display, screen ) - data->dialog_width ) / 2;
   413         y = ( DisplayHeight( display, screen ) - data->dialog_height ) / 3 ;
   414     }
   415     XMoveWindow( display, data->window, x, y );
   416 
   417     sizehints = XAllocSizeHints();
   418     if ( sizehints ) {
   419         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
   420         sizehints->x = x;
   421         sizehints->y = y;
   422         sizehints->width = data->dialog_width;
   423         sizehints->height = data->dialog_height;
   424 
   425         sizehints->min_width = sizehints->max_width = data->dialog_width;
   426         sizehints->min_height = sizehints->max_height = data->dialog_height;
   427 
   428         XSetWMNormalHints( display, data->window, sizehints );
   429 
   430         XFree( sizehints );
   431     }
   432 
   433     XMapRaised( display, data->window );
   434     return 0;
   435 }
   436 
   437 /* Draw our message box. */
   438 static void
   439 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
   440 {
   441     int i;
   442     Window window = data->window;
   443     Display *display = data->display;
   444 
   445     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
   446     XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
   447 
   448     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   449     for ( i = 0; i < data->numlines; i++ ) {
   450         TextLineData *plinedata = &data->linedata[ i ];
   451 
   452         if (SDL_X11_HAVE_UTF8) {
   453             Xutf8DrawString( display, window, data->font_set, ctx,
   454                              data->xtext, data->ytext + i * data->text_height,
   455                              plinedata->text, plinedata->length );
   456         } else {
   457             XDrawString( display, window, ctx,
   458                          data->xtext, data->ytext + i * data->text_height,
   459                          plinedata->text, plinedata->length );
   460         }
   461     }
   462 
   463     for ( i = 0; i < data->numbuttons; i++ ) {
   464         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   465         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
   466         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
   467         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
   468 
   469         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
   470         XFillRectangle( display, window, ctx,
   471                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
   472                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
   473 
   474         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
   475         XDrawRectangle( display, window, ctx,
   476                         buttondatax11->rect.x, buttondatax11->rect.y,
   477                         buttondatax11->rect.w, buttondatax11->rect.h );
   478 
   479         XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
   480                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
   481                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   482 
   483         if (SDL_X11_HAVE_UTF8) {
   484             Xutf8DrawString( display, window, data->font_set, ctx,
   485                              buttondatax11->x + offset,
   486                              buttondatax11->y + offset,
   487                              buttondata->text, buttondatax11->length );
   488         } else {
   489             XDrawString( display, window, ctx,
   490                          buttondatax11->x + offset, buttondatax11->y + offset,
   491                          buttondata->text, buttondatax11->length );
   492         }
   493     }
   494 }
   495 
   496 /* Loop and handle message box event messages until something kills it. */
   497 static int
   498 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
   499 {
   500     GC ctx;
   501     XGCValues ctx_vals;
   502     SDL_bool close_dialog = SDL_FALSE;
   503     SDL_bool has_focus = SDL_TRUE;
   504     KeySym last_key_pressed = XK_VoidSymbol;
   505     unsigned long gcflags = GCForeground | GCBackground;
   506 
   507     SDL_zero(ctx_vals);
   508     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   509     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   510 
   511     if (!SDL_X11_HAVE_UTF8) {
   512         gcflags |= GCFont;
   513         ctx_vals.font = data->font_struct->fid;
   514     }
   515 
   516     ctx = XCreateGC( data->display, data->window, gcflags, &ctx_vals );
   517     if ( ctx == None ) {
   518         SDL_SetError("Couldn't create graphics context");
   519         return -1;
   520     }
   521 
   522     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   523     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   524 
   525     while( !close_dialog ) {
   526         XEvent e;
   527         SDL_bool draw = SDL_TRUE;
   528 
   529         XWindowEvent( data->display, data->window, data->event_mask, &e );
   530 
   531         /* If XFilterEvent returns True, then some input method has filtered the
   532            event, and the client should discard the event. */
   533         if ( ( e.type != Expose ) && XFilterEvent( &e, None ) )
   534             continue;
   535 
   536         switch( e.type ) {
   537         case Expose:
   538             if ( e.xexpose.count > 0 ) {
   539                 draw = SDL_FALSE;
   540             }
   541             break;
   542 
   543         case FocusIn:
   544             /* Got focus. */
   545             has_focus = SDL_TRUE;
   546             break;
   547 
   548         case FocusOut:
   549             /* lost focus. Reset button and mouse info. */
   550             has_focus = SDL_FALSE;
   551             data->button_press_index = -1;
   552             data->mouse_over_index = -1;
   553             break;
   554 
   555         case MotionNotify:
   556             if ( has_focus ) {
   557                 /* Mouse moved... */
   558                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   559             }
   560             break;
   561 
   562         case ClientMessage:
   563             if ( e.xclient.message_type == data->wm_protocols &&
   564                  e.xclient.format == 32 &&
   565                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   566                 close_dialog = SDL_TRUE;
   567             }
   568             break;
   569 
   570         case KeyPress:
   571             /* Store key press - we make sure in key release that we got both. */
   572             last_key_pressed = XLookupKeysym( &e.xkey, 0 );
   573             break;
   574 
   575         case KeyRelease: {
   576             Uint32 mask = 0;
   577             KeySym key = XLookupKeysym( &e.xkey, 0 );
   578 
   579             /* If this is a key release for something we didn't get the key down for, then bail. */
   580             if ( key != last_key_pressed )
   581                 break;
   582 
   583             if ( key == XK_Escape )
   584                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   585             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   586                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   587 
   588             if ( mask ) {
   589                 int i;
   590 
   591                 /* Look for first button with this mask set, and return it if found. */
   592                 for ( i = 0; i < data->numbuttons; i++ ) {
   593                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   594 
   595                     if ( buttondatax11->buttondata->flags & mask ) {
   596                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   597                         close_dialog = SDL_TRUE;
   598                         break;
   599                     }
   600                 }
   601             }
   602             break;
   603         }
   604 
   605         case ButtonPress:
   606             data->button_press_index = -1;
   607             if ( e.xbutton.button == Button1 ) {
   608                 /* Find index of button they clicked on. */
   609                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   610             }
   611             break;
   612 
   613         case ButtonRelease:
   614             /* If button is released over the same button that was clicked down on, then return it. */
   615             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   616                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   617 
   618                 if ( data->button_press_index == button ) {
   619                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   620 
   621                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   622                     close_dialog = SDL_TRUE;
   623                 }
   624             }
   625             data->button_press_index = -1;
   626             break;
   627         }
   628 
   629         if ( draw ) {
   630             /* Draw our dialog box. */
   631             X11_MessageBoxDraw( data, ctx );
   632         }
   633     }
   634 
   635     XFreeGC( data->display, ctx );
   636     return 0;
   637 }
   638 
   639 static int
   640 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   641 {
   642     int ret;
   643     SDL_MessageBoxDataX11 data;
   644     char *origlocale;
   645 
   646     SDL_zero(data);
   647 
   648     if ( !SDL_X11_LoadSymbols() )
   649         return -1;
   650 
   651     origlocale = setlocale(LC_ALL, NULL);
   652     if (origlocale != NULL) {
   653         origlocale = SDL_strdup(origlocale);
   654         if (origlocale == NULL) {
   655             SDL_OutOfMemory();
   656             return -1;
   657         }
   658         setlocale(LC_ALL, "");
   659     }
   660 
   661     /* This code could get called from multiple threads maybe? */
   662     XInitThreads();
   663 
   664     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   665     *buttonid = -1;
   666 
   667     /* Init and display the message box. */
   668     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   669     if ( ret != -1 ) {
   670         ret = X11_MessageBoxInitPositions( &data );
   671         if ( ret != -1 ) {
   672             ret = X11_MessageBoxCreateWindow( &data );
   673             if ( ret != -1 ) {
   674                 ret = X11_MessageBoxLoop( &data );
   675             }
   676         }
   677     }
   678 
   679     X11_MessageBoxShutdown( &data );
   680 
   681     if (origlocale) {
   682         setlocale(LC_ALL, origlocale);
   683         SDL_free(origlocale);
   684     }
   685 
   686     return ret;
   687 }
   688 
   689 /* Display an x11 message box. */
   690 int
   691 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   692 {
   693 #if SDL_FORK_MESSAGEBOX
   694     /* Use a child process to protect against setlocale(). Annoying. */
   695     pid_t pid;
   696     int fds[2];
   697     int status = 0;
   698 
   699     if (pipe(fds) == -1) {
   700         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   701     }
   702 
   703     pid = fork();
   704     if (pid == -1) {  /* failed */
   705         close(fds[0]);
   706         close(fds[1]);
   707         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   708     } else if (pid == 0) {  /* we're the child */
   709         int exitcode = 0;
   710         close(fds[0]);
   711         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   712         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   713             exitcode = 1;
   714         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   715             exitcode = 1;
   716         close(fds[1]);
   717         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   718     } else {  /* we're the parent */
   719         pid_t rc;
   720         close(fds[1]);
   721         do {
   722             rc = waitpid(pid, &status, 0);
   723         } while ((rc == -1) && (errno == EINTR));
   724 
   725         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   726 
   727         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   728             SDL_SetError("msgbox child process failed");
   729             return -1;
   730         }
   731 
   732         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   733             status = -1;
   734         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   735             status = -1;
   736         close(fds[0]);
   737 
   738         return status;
   739     }
   740 #else
   741     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   742 #endif
   743 }
   744 #endif /* SDL_VIDEO_DRIVER_X11 */
   745 
   746 /* vi: set ts=4 sw=4 expandtab: */