src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 16 Dec 2012 14:39:49 -0500
changeset 6758 2a218048b45f
parent 6726 f2898757eaae
child 6817 93103b719488
permissions -rw-r--r--
X11 messageboxes should treat "\r\n" as "\n" by ignoring the '\r' char.
     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             if (lf && (lf > text) && (lf[-1] == '\r')) {
   254                 plinedata->length--;
   255             }
   256 
   257             text += plinedata->length + 1;
   258 
   259             /* Break if there are no more linefeeds. */
   260             if ( !lf )
   261                 break;
   262         }
   263 
   264         /* Bump up the text height slightly. */
   265         data->text_height += 2;
   266     }
   267 
   268     /* Loop through all buttons and calculate the button widths and height. */
   269     for ( i = 0; i < data->numbuttons; i++ ) {
   270         int height;
   271 
   272         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
   273         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
   274 
   275         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
   276                             &data->buttonpos[ i ].text_width, &height );
   277 
   278         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
   279         button_text_height = IntMax( button_text_height, height );
   280     }
   281 
   282     if ( data->numlines ) {
   283         /* x,y for this line of text. */
   284         data->xtext = data->text_height;
   285         data->ytext = data->text_height + data->text_height;
   286 
   287         /* Bump button y down to bottom of text. */
   288         ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
   289 
   290         /* Bump the dialog box width and height up if needed. */
   291         data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
   292         data->dialog_height = IntMax( data->dialog_height, ybuttons );
   293     } else {
   294         /* Button y starts at height of button text. */
   295         ybuttons = button_text_height;
   296     }
   297 
   298     if ( data->numbuttons ) {
   299         int x, y;
   300         int width_of_buttons;
   301         int button_spacing = button_text_height;
   302         int button_height = 2 * button_text_height;
   303 
   304         /* Bump button width up a bit. */
   305         button_width += button_text_height;
   306 
   307         /* Get width of all buttons lined up. */
   308         width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
   309 
   310         /* Bump up dialog width and height if buttons are wider than text. */
   311         data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
   312         data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
   313 
   314         /* Location for first button. */
   315         x = ( data->dialog_width - width_of_buttons ) / 2;
   316         y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
   317 
   318         for ( i = 0; i < data->numbuttons; i++ ) {
   319             /* Button coordinates. */
   320             data->buttonpos[ i ].rect.x = x;
   321             data->buttonpos[ i ].rect.y = y;
   322             data->buttonpos[ i ].rect.w = button_width;
   323             data->buttonpos[ i ].rect.h = button_height;
   324 
   325             /* Button text coordinates. */
   326             data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
   327             data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
   328 
   329             /* Scoot over for next button. */
   330             x += button_width + button_spacing;
   331         }
   332     }
   333 
   334     return 0;
   335 }
   336 
   337 /* Free SDL_MessageBoxData data. */
   338 static void
   339 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
   340 {
   341     if ( data->font_set != NULL ) {
   342         XFreeFontSet( data->display, data->font_set );
   343         data->font_set = NULL;
   344     }
   345 
   346     if ( data->font_struct != NULL ) {
   347         XFreeFont( data->display, data->font_struct );
   348         data->font_struct = NULL;
   349     }
   350 
   351     if ( data->display ) {
   352         if ( data->window != None ) {
   353             XUnmapWindow( data->display, data->window );
   354             XDestroyWindow( data->display, data->window );
   355             data->window = None;
   356         }
   357 
   358         XCloseDisplay( data->display );
   359         data->display = NULL;
   360     }
   361 }
   362 
   363 /* Create and set up our X11 dialog box indow. */
   364 static int
   365 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
   366 {
   367     int x, y;
   368     XSizeHints *sizehints;
   369     XSetWindowAttributes wnd_attr;
   370     Display *display = data->display;
   371     SDL_WindowData *windowdata = NULL;
   372     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   373 
   374     if ( messageboxdata->window ) {
   375         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   376     }
   377 
   378     data->event_mask = ExposureMask |
   379                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   380                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   381     wnd_attr.event_mask = data->event_mask;
   382 
   383     data->window = XCreateWindow(
   384                        display, DefaultRootWindow( display ),
   385                        0, 0,
   386                        data->dialog_width, data->dialog_height,
   387                        0, CopyFromParent, InputOutput, CopyFromParent,
   388                        CWEventMask, &wnd_attr );
   389     if ( data->window == None ) {
   390         SDL_SetError("Couldn't create X window");
   391         return -1;
   392     }
   393 
   394     if ( windowdata ) {
   395         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
   396         XSetTransientForHint( display, data->window, windowdata->xwindow );
   397     }
   398 
   399     XStoreName( display, data->window, messageboxdata->title );
   400 
   401     /* Allow the window to be deleted by the window manager */
   402     data->wm_protocols = XInternAtom( display, "WM_PROTOCOLS", False );
   403     data->wm_delete_message = XInternAtom( display, "WM_DELETE_WINDOW", False );
   404     XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
   405 
   406     if ( windowdata ) {
   407         XWindowAttributes attrib;
   408         Window dummy;
   409 
   410         XGetWindowAttributes(display, windowdata->xwindow, &attrib);
   411         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
   412         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
   413         XTranslateCoordinates(display, windowdata->xwindow, DefaultRootWindow( display ), x, y, &x, &y, &dummy);
   414     } else {
   415         int screen = DefaultScreen( display );
   416         x = ( DisplayWidth( display, screen ) - data->dialog_width ) / 2;
   417         y = ( DisplayHeight( display, screen ) - data->dialog_height ) / 3 ;
   418     }
   419     XMoveWindow( display, data->window, x, y );
   420 
   421     sizehints = XAllocSizeHints();
   422     if ( sizehints ) {
   423         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
   424         sizehints->x = x;
   425         sizehints->y = y;
   426         sizehints->width = data->dialog_width;
   427         sizehints->height = data->dialog_height;
   428 
   429         sizehints->min_width = sizehints->max_width = data->dialog_width;
   430         sizehints->min_height = sizehints->max_height = data->dialog_height;
   431 
   432         XSetWMNormalHints( display, data->window, sizehints );
   433 
   434         XFree( sizehints );
   435     }
   436 
   437     XMapRaised( display, data->window );
   438     return 0;
   439 }
   440 
   441 /* Draw our message box. */
   442 static void
   443 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
   444 {
   445     int i;
   446     Window window = data->window;
   447     Display *display = data->display;
   448 
   449     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
   450     XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
   451 
   452     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   453     for ( i = 0; i < data->numlines; i++ ) {
   454         TextLineData *plinedata = &data->linedata[ i ];
   455 
   456         if (SDL_X11_HAVE_UTF8) {
   457             Xutf8DrawString( display, window, data->font_set, ctx,
   458                              data->xtext, data->ytext + i * data->text_height,
   459                              plinedata->text, plinedata->length );
   460         } else {
   461             XDrawString( display, window, ctx,
   462                          data->xtext, data->ytext + i * data->text_height,
   463                          plinedata->text, plinedata->length );
   464         }
   465     }
   466 
   467     for ( i = 0; i < data->numbuttons; i++ ) {
   468         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   469         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
   470         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
   471         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
   472 
   473         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
   474         XFillRectangle( display, window, ctx,
   475                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
   476                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
   477 
   478         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
   479         XDrawRectangle( display, window, ctx,
   480                         buttondatax11->rect.x, buttondatax11->rect.y,
   481                         buttondatax11->rect.w, buttondatax11->rect.h );
   482 
   483         XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
   484                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
   485                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   486 
   487         if (SDL_X11_HAVE_UTF8) {
   488             Xutf8DrawString( display, window, data->font_set, ctx,
   489                              buttondatax11->x + offset,
   490                              buttondatax11->y + offset,
   491                              buttondata->text, buttondatax11->length );
   492         } else {
   493             XDrawString( display, window, ctx,
   494                          buttondatax11->x + offset, buttondatax11->y + offset,
   495                          buttondata->text, buttondatax11->length );
   496         }
   497     }
   498 }
   499 
   500 /* Loop and handle message box event messages until something kills it. */
   501 static int
   502 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
   503 {
   504     GC ctx;
   505     XGCValues ctx_vals;
   506     SDL_bool close_dialog = SDL_FALSE;
   507     SDL_bool has_focus = SDL_TRUE;
   508     KeySym last_key_pressed = XK_VoidSymbol;
   509     unsigned long gcflags = GCForeground | GCBackground;
   510 
   511     SDL_zero(ctx_vals);
   512     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   513     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   514 
   515     if (!SDL_X11_HAVE_UTF8) {
   516         gcflags |= GCFont;
   517         ctx_vals.font = data->font_struct->fid;
   518     }
   519 
   520     ctx = XCreateGC( data->display, data->window, gcflags, &ctx_vals );
   521     if ( ctx == None ) {
   522         SDL_SetError("Couldn't create graphics context");
   523         return -1;
   524     }
   525 
   526     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   527     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   528 
   529     while( !close_dialog ) {
   530         XEvent e;
   531         SDL_bool draw = SDL_TRUE;
   532 
   533         XWindowEvent( data->display, data->window, data->event_mask, &e );
   534 
   535         /* If XFilterEvent returns True, then some input method has filtered the
   536            event, and the client should discard the event. */
   537         if ( ( e.type != Expose ) && XFilterEvent( &e, None ) )
   538             continue;
   539 
   540         switch( e.type ) {
   541         case Expose:
   542             if ( e.xexpose.count > 0 ) {
   543                 draw = SDL_FALSE;
   544             }
   545             break;
   546 
   547         case FocusIn:
   548             /* Got focus. */
   549             has_focus = SDL_TRUE;
   550             break;
   551 
   552         case FocusOut:
   553             /* lost focus. Reset button and mouse info. */
   554             has_focus = SDL_FALSE;
   555             data->button_press_index = -1;
   556             data->mouse_over_index = -1;
   557             break;
   558 
   559         case MotionNotify:
   560             if ( has_focus ) {
   561                 /* Mouse moved... */
   562                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   563             }
   564             break;
   565 
   566         case ClientMessage:
   567             if ( e.xclient.message_type == data->wm_protocols &&
   568                  e.xclient.format == 32 &&
   569                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   570                 close_dialog = SDL_TRUE;
   571             }
   572             break;
   573 
   574         case KeyPress:
   575             /* Store key press - we make sure in key release that we got both. */
   576             last_key_pressed = XLookupKeysym( &e.xkey, 0 );
   577             break;
   578 
   579         case KeyRelease: {
   580             Uint32 mask = 0;
   581             KeySym key = XLookupKeysym( &e.xkey, 0 );
   582 
   583             /* If this is a key release for something we didn't get the key down for, then bail. */
   584             if ( key != last_key_pressed )
   585                 break;
   586 
   587             if ( key == XK_Escape )
   588                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   589             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   590                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   591 
   592             if ( mask ) {
   593                 int i;
   594 
   595                 /* Look for first button with this mask set, and return it if found. */
   596                 for ( i = 0; i < data->numbuttons; i++ ) {
   597                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   598 
   599                     if ( buttondatax11->buttondata->flags & mask ) {
   600                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   601                         close_dialog = SDL_TRUE;
   602                         break;
   603                     }
   604                 }
   605             }
   606             break;
   607         }
   608 
   609         case ButtonPress:
   610             data->button_press_index = -1;
   611             if ( e.xbutton.button == Button1 ) {
   612                 /* Find index of button they clicked on. */
   613                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   614             }
   615             break;
   616 
   617         case ButtonRelease:
   618             /* If button is released over the same button that was clicked down on, then return it. */
   619             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   620                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   621 
   622                 if ( data->button_press_index == button ) {
   623                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   624 
   625                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   626                     close_dialog = SDL_TRUE;
   627                 }
   628             }
   629             data->button_press_index = -1;
   630             break;
   631         }
   632 
   633         if ( draw ) {
   634             /* Draw our dialog box. */
   635             X11_MessageBoxDraw( data, ctx );
   636         }
   637     }
   638 
   639     XFreeGC( data->display, ctx );
   640     return 0;
   641 }
   642 
   643 static int
   644 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   645 {
   646     int ret;
   647     SDL_MessageBoxDataX11 data;
   648     char *origlocale;
   649 
   650     SDL_zero(data);
   651 
   652     if ( !SDL_X11_LoadSymbols() )
   653         return -1;
   654 
   655     origlocale = setlocale(LC_ALL, NULL);
   656     if (origlocale != NULL) {
   657         origlocale = SDL_strdup(origlocale);
   658         if (origlocale == NULL) {
   659             SDL_OutOfMemory();
   660             return -1;
   661         }
   662         setlocale(LC_ALL, "");
   663     }
   664 
   665     /* This code could get called from multiple threads maybe? */
   666     XInitThreads();
   667 
   668     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   669     *buttonid = -1;
   670 
   671     /* Init and display the message box. */
   672     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   673     if ( ret != -1 ) {
   674         ret = X11_MessageBoxInitPositions( &data );
   675         if ( ret != -1 ) {
   676             ret = X11_MessageBoxCreateWindow( &data );
   677             if ( ret != -1 ) {
   678                 ret = X11_MessageBoxLoop( &data );
   679             }
   680         }
   681     }
   682 
   683     X11_MessageBoxShutdown( &data );
   684 
   685     if (origlocale) {
   686         setlocale(LC_ALL, origlocale);
   687         SDL_free(origlocale);
   688     }
   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     if (pipe(fds) == -1) {
   704         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   705     }
   706 
   707     pid = fork();
   708     if (pid == -1) {  /* failed */
   709         close(fds[0]);
   710         close(fds[1]);
   711         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   712     } else if (pid == 0) {  /* we're the child */
   713         int exitcode = 0;
   714         close(fds[0]);
   715         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   716         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   717             exitcode = 1;
   718         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   719             exitcode = 1;
   720         close(fds[1]);
   721         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   722     } else {  /* we're the parent */
   723         pid_t rc;
   724         close(fds[1]);
   725         do {
   726             rc = waitpid(pid, &status, 0);
   727         } while ((rc == -1) && (errno == EINTR));
   728 
   729         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   730 
   731         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   732             SDL_SetError("msgbox child process failed");
   733             return -1;
   734         }
   735 
   736         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   737             status = -1;
   738         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   739             status = -1;
   740         close(fds[0]);
   741 
   742         return status;
   743     }
   744 #else
   745     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   746 #endif
   747 }
   748 #endif /* SDL_VIDEO_DRIVER_X11 */
   749 
   750 /* vi: set ts=4 sw=4 expandtab: */