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