src/video/x11/SDL_x11messagebox.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 18 May 2013 14:17:52 -0700
changeset 7191 75360622e65f
parent 7037 3fedf1f25b94
child 7522 b186724247dd
permissions -rw-r--r--
File style cleanup for the SDL 2.0 release
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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 0
    35 #define SDL_SET_LOCALE      0
    36 
    37 #if SDL_FORK_MESSAGEBOX
    38 #include <sys/types.h>
    39 #include <sys/wait.h>
    40 #include <unistd.h>
    41 #include <errno.h>
    42 #endif
    43 
    44 #define MAX_BUTTONS             8       /* Maximum number of buttons supported */
    45 #define MAX_TEXT_LINES          32      /* Maximum number of text lines 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[] = "-*-*-*-*-*-*-*-*-*-*-*-*-*-*";
    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     XFontSet font_set;                  /* for UTF-8 systems */
    83     XFontStruct *font_struct;            /* Latin1 (ASCII) fallback. */
    84     Window window;
    85     Display *display;
    86     long event_mask;
    87     Atom wm_protocols;
    88     Atom wm_delete_message;
    89 
    90     int dialog_width;                   /* Dialog box width. */
    91     int dialog_height;                  /* Dialog box height. */
    92 
    93     int xtext, ytext;                   /* Text position to start drawing at. */
    94     int numlines;                       /* Count of Text lines. */
    95     int text_height;                    /* Height for text lines. */
    96     TextLineData linedata[ MAX_TEXT_LINES ];
    97 
    98     int *pbuttonid;                     /* Pointer to user return buttonid value. */
    99 
   100     int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
   101     int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
   102 
   103     int numbuttons;                     /* Count of buttons. */
   104     const SDL_MessageBoxButtonData *buttondata;
   105     SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
   106 
   107     Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
   108 
   109     const SDL_MessageBoxData *messageboxdata;
   110 } SDL_MessageBoxDataX11;
   111 
   112 /* Maximum helper for ints. */
   113 static __inline__ int
   114 IntMax( int a, int b )
   115 {
   116     return ( a > b  ) ? a : b;
   117 }
   118 
   119 /* Return width and height for a string. */
   120 static void
   121 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
   122 {
   123     if (SDL_X11_HAVE_UTF8) {
   124         XRectangle overall_ink, overall_logical;
   125         Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
   126         *pwidth = overall_logical.width;
   127         *pheight = overall_logical.height;
   128     } else {
   129         XCharStruct text_structure;
   130         int font_direction, font_ascent, font_descent;
   131         XTextExtents( data->font_struct, str, nbytes,
   132                       &font_direction, &font_ascent, &font_descent,
   133                       &text_structure );
   134         *pwidth = text_structure.width;
   135         *pheight = text_structure.ascent + text_structure.descent;
   136     }
   137 }
   138 
   139 /* Return index of button if position x,y is contained therein. */
   140 static int
   141 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
   142 {
   143     int i;
   144     int numbuttons = data->numbuttons;
   145     SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
   146 
   147     for ( i = 0; i < numbuttons; i++ ) {
   148         SDL_Rect *rect = &buttonpos[ i ].rect;
   149 
   150         if ( ( x >= rect->x ) &&
   151              ( x <= ( rect->x + rect->w ) ) &&
   152              ( y >= rect->y ) &&
   153              ( y <= ( rect->y + rect->h ) ) ) {
   154             return i;
   155         }
   156     }
   157 
   158     return -1;
   159 }
   160 
   161 /* Initialize SDL_MessageBoxData structure and Display, etc. */
   162 static int
   163 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
   164 {
   165     int i;
   166     int numbuttons = messageboxdata->numbuttons;
   167     const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
   168     const SDL_MessageBoxColor *colorhints;
   169 
   170     if ( numbuttons > MAX_BUTTONS ) {
   171         return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
   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         return SDL_SetError("Couldn't open X11 display");
   184     }
   185 
   186     if (SDL_X11_HAVE_UTF8) {
   187         char **missing = NULL;
   188         int num_missing = 0;
   189         data->font_set = XCreateFontSet(data->display, g_MessageBoxFont,
   190                                         &missing, &num_missing, NULL);
   191         if ( missing != NULL ) {
   192             XFreeStringList(missing);
   193         }
   194         if ( data->font_set == NULL ) {
   195             return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
   196         }
   197     } else {
   198         data->font_struct = XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
   199         if ( data->font_struct == NULL ) {
   200             return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
   201         }
   202     }
   203 
   204     if ( messageboxdata->colorScheme ) {
   205         colorhints = messageboxdata->colorScheme->colors;
   206     } else {
   207         colorhints = g_default_colors;
   208     }
   209 
   210     /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
   211     for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
   212         data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
   213     }
   214 
   215     return 0;
   216 }
   217 
   218 /* Calculate and initialize text and button locations. */
   219 static int
   220 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
   221 {
   222     int i;
   223     int ybuttons;
   224     int text_width_max = 0;
   225     int button_text_height = 0;
   226     int button_width = MIN_BUTTON_WIDTH;
   227     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   228 
   229     /* Go over text and break linefeeds into separate lines. */
   230     if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
   231         const char *text = messageboxdata->message;
   232         TextLineData *plinedata = data->linedata;
   233 
   234         for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
   235             int height;
   236             char *lf = SDL_strchr( ( char * )text, '\n' );
   237 
   238             data->numlines++;
   239 
   240             /* Only grab length up to lf if it exists and isn't the last line. */
   241             plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
   242             plinedata->text = text;
   243 
   244             GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
   245 
   246             /* Text and widths are the largest we've ever seen. */
   247             data->text_height = IntMax( data->text_height, height );
   248             text_width_max = IntMax( text_width_max, plinedata->width );
   249 
   250             if (lf && (lf > text) && (lf[-1] == '\r')) {
   251                 plinedata->length--;
   252             }
   253 
   254             text += plinedata->length + 1;
   255 
   256             /* Break if there are no more linefeeds. */
   257             if ( !lf )
   258                 break;
   259         }
   260 
   261         /* Bump up the text height slightly. */
   262         data->text_height += 2;
   263     }
   264 
   265     /* Loop through all buttons and calculate the button widths and height. */
   266     for ( i = 0; i < data->numbuttons; i++ ) {
   267         int height;
   268 
   269         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
   270         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
   271 
   272         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
   273                             &data->buttonpos[ i ].text_width, &height );
   274 
   275         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
   276         button_text_height = IntMax( button_text_height, height );
   277     }
   278 
   279     if ( data->numlines ) {
   280         /* x,y for this line of text. */
   281         data->xtext = data->text_height;
   282         data->ytext = data->text_height + data->text_height;
   283 
   284         /* Bump button y down to bottom of text. */
   285         ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
   286 
   287         /* Bump the dialog box width and height up if needed. */
   288         data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
   289         data->dialog_height = IntMax( data->dialog_height, ybuttons );
   290     } else {
   291         /* Button y starts at height of button text. */
   292         ybuttons = button_text_height;
   293     }
   294 
   295     if ( data->numbuttons ) {
   296         int x, y;
   297         int width_of_buttons;
   298         int button_spacing = button_text_height;
   299         int button_height = 2 * button_text_height;
   300 
   301         /* Bump button width up a bit. */
   302         button_width += button_text_height;
   303 
   304         /* Get width of all buttons lined up. */
   305         width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
   306 
   307         /* Bump up dialog width and height if buttons are wider than text. */
   308         data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
   309         data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
   310 
   311         /* Location for first button. */
   312         x = ( data->dialog_width - width_of_buttons ) / 2;
   313         y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
   314 
   315         for ( i = 0; i < data->numbuttons; i++ ) {
   316             /* Button coordinates. */
   317             data->buttonpos[ i ].rect.x = x;
   318             data->buttonpos[ i ].rect.y = y;
   319             data->buttonpos[ i ].rect.w = button_width;
   320             data->buttonpos[ i ].rect.h = button_height;
   321 
   322             /* Button text coordinates. */
   323             data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
   324             data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
   325 
   326             /* Scoot over for next button. */
   327             x += button_width + button_spacing;
   328         }
   329     }
   330 
   331     return 0;
   332 }
   333 
   334 /* Free SDL_MessageBoxData data. */
   335 static void
   336 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
   337 {
   338     if ( data->font_set != NULL ) {
   339         XFreeFontSet( data->display, data->font_set );
   340         data->font_set = NULL;
   341     }
   342 
   343     if ( data->font_struct != NULL ) {
   344         XFreeFont( data->display, data->font_struct );
   345         data->font_struct = NULL;
   346     }
   347 
   348     if ( data->display ) {
   349         if ( data->window != None ) {
   350             XUnmapWindow( data->display, data->window );
   351             XDestroyWindow( data->display, data->window );
   352             data->window = None;
   353         }
   354 
   355         XCloseDisplay( data->display );
   356         data->display = NULL;
   357     }
   358 }
   359 
   360 /* Create and set up our X11 dialog box indow. */
   361 static int
   362 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
   363 {
   364     int x, y;
   365     XSizeHints *sizehints;
   366     XSetWindowAttributes wnd_attr;
   367     Display *display = data->display;
   368     SDL_WindowData *windowdata = NULL;
   369     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   370 
   371     if ( messageboxdata->window ) {
   372         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   373     }
   374 
   375     data->event_mask = ExposureMask |
   376                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   377                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   378     wnd_attr.event_mask = data->event_mask;
   379 
   380     data->window = XCreateWindow(
   381                        display, DefaultRootWindow( display ),
   382                        0, 0,
   383                        data->dialog_width, data->dialog_height,
   384                        0, CopyFromParent, InputOutput, CopyFromParent,
   385                        CWEventMask, &wnd_attr );
   386     if ( data->window == None ) {
   387         return SDL_SetError("Couldn't create X window");
   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         return SDL_SetError("Couldn't create graphics context");
   519     }
   520 
   521     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   522     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   523 
   524     while( !close_dialog ) {
   525         XEvent e;
   526         SDL_bool draw = SDL_TRUE;
   527 
   528         XWindowEvent( data->display, data->window, data->event_mask, &e );
   529 
   530         /* If XFilterEvent returns True, then some input method has filtered the
   531            event, and the client should discard the event. */
   532         if ( ( e.type != Expose ) && XFilterEvent( &e, None ) )
   533             continue;
   534 
   535         switch( e.type ) {
   536         case Expose:
   537             if ( e.xexpose.count > 0 ) {
   538                 draw = SDL_FALSE;
   539             }
   540             break;
   541 
   542         case FocusIn:
   543             /* Got focus. */
   544             has_focus = SDL_TRUE;
   545             break;
   546 
   547         case FocusOut:
   548             /* lost focus. Reset button and mouse info. */
   549             has_focus = SDL_FALSE;
   550             data->button_press_index = -1;
   551             data->mouse_over_index = -1;
   552             break;
   553 
   554         case MotionNotify:
   555             if ( has_focus ) {
   556                 /* Mouse moved... */
   557                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   558             }
   559             break;
   560 
   561         case ClientMessage:
   562             if ( e.xclient.message_type == data->wm_protocols &&
   563                  e.xclient.format == 32 &&
   564                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   565                 close_dialog = SDL_TRUE;
   566             }
   567             break;
   568 
   569         case KeyPress:
   570             /* Store key press - we make sure in key release that we got both. */
   571             last_key_pressed = XLookupKeysym( &e.xkey, 0 );
   572             break;
   573 
   574         case KeyRelease: {
   575             Uint32 mask = 0;
   576             KeySym key = XLookupKeysym( &e.xkey, 0 );
   577 
   578             /* If this is a key release for something we didn't get the key down for, then bail. */
   579             if ( key != last_key_pressed )
   580                 break;
   581 
   582             if ( key == XK_Escape )
   583                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   584             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   585                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   586 
   587             if ( mask ) {
   588                 int i;
   589 
   590                 /* Look for first button with this mask set, and return it if found. */
   591                 for ( i = 0; i < data->numbuttons; i++ ) {
   592                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   593 
   594                     if ( buttondatax11->buttondata->flags & mask ) {
   595                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   596                         close_dialog = SDL_TRUE;
   597                         break;
   598                     }
   599                 }
   600             }
   601             break;
   602         }
   603 
   604         case ButtonPress:
   605             data->button_press_index = -1;
   606             if ( e.xbutton.button == Button1 ) {
   607                 /* Find index of button they clicked on. */
   608                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   609             }
   610             break;
   611 
   612         case ButtonRelease:
   613             /* If button is released over the same button that was clicked down on, then return it. */
   614             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   615                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   616 
   617                 if ( data->button_press_index == button ) {
   618                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   619 
   620                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   621                     close_dialog = SDL_TRUE;
   622                 }
   623             }
   624             data->button_press_index = -1;
   625             break;
   626         }
   627 
   628         if ( draw ) {
   629             /* Draw our dialog box. */
   630             X11_MessageBoxDraw( data, ctx );
   631         }
   632     }
   633 
   634     XFreeGC( data->display, ctx );
   635     return 0;
   636 }
   637 
   638 static int
   639 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   640 {
   641     int ret;
   642     SDL_MessageBoxDataX11 data;
   643 #if SDL_SET_LOCALE
   644     char *origlocale;
   645 #endif
   646 
   647     SDL_zero(data);
   648 
   649     if ( !SDL_X11_LoadSymbols() )
   650         return -1;
   651 
   652 #if SDL_SET_LOCALE
   653     origlocale = setlocale(LC_ALL, NULL);
   654     if (origlocale != NULL) {
   655         origlocale = SDL_strdup(origlocale);
   656         if (origlocale == NULL) {
   657             return SDL_OutOfMemory();
   658         }
   659         setlocale(LC_ALL, "");
   660     }
   661 #endif
   662 
   663     /* This code could get called from multiple threads maybe? */
   664     XInitThreads();
   665 
   666     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   667     *buttonid = -1;
   668 
   669     /* Init and display the message box. */
   670     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   671     if ( ret != -1 ) {
   672         ret = X11_MessageBoxInitPositions( &data );
   673         if ( ret != -1 ) {
   674             ret = X11_MessageBoxCreateWindow( &data );
   675             if ( ret != -1 ) {
   676                 ret = X11_MessageBoxLoop( &data );
   677             }
   678         }
   679     }
   680 
   681     X11_MessageBoxShutdown( &data );
   682 
   683 #if SDL_SET_LOCALE
   684     if (origlocale) {
   685         setlocale(LC_ALL, origlocale);
   686         SDL_free(origlocale);
   687     }
   688 #endif
   689 
   690     return ret;
   691 }
   692 
   693 /* Display an x11 message box. */
   694 int
   695 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   696 {
   697 #if SDL_FORK_MESSAGEBOX
   698     /* Use a child process to protect against setlocale(). Annoying. */
   699     pid_t pid;
   700     int fds[2];
   701     int status = 0;
   702 
   703     /* Need to flush here in case someone has turned grab off and it hasn't gone through yet, etc. */
   704     XFlush(data->display);
   705 
   706     if (pipe(fds) == -1) {
   707         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   708     }
   709 
   710     pid = fork();
   711     if (pid == -1) {  /* failed */
   712         close(fds[0]);
   713         close(fds[1]);
   714         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   715     } else if (pid == 0) {  /* we're the child */
   716         int exitcode = 0;
   717         close(fds[0]);
   718         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   719         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   720             exitcode = 1;
   721         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   722             exitcode = 1;
   723         close(fds[1]);
   724         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   725     } else {  /* we're the parent */
   726         pid_t rc;
   727         close(fds[1]);
   728         do {
   729             rc = waitpid(pid, &status, 0);
   730         } while ((rc == -1) && (errno == EINTR));
   731 
   732         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   733 
   734         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   735             return SDL_SetError("msgbox child process failed");
   736         }
   737 
   738         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   739             status = -1;
   740         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   741             status = -1;
   742         close(fds[0]);
   743 
   744         return status;
   745     }
   746 #else
   747     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   748 #endif
   749 }
   750 #endif /* SDL_VIDEO_DRIVER_X11 */
   751 
   752 /* vi: set ts=4 sw=4 expandtab: */