src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 07 Dec 2012 20:00:42 -0500
changeset 6723 7c464a9ecf48
parent 6615 d7a463bde4d7
child 6724 6c5ed0c4cc6d
permissions -rw-r--r--
X11 message boxes should work with UTF-8 strings if possible.
     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 /* !!! FIXME: clean up differences in coding style in this file. */
    23 
    24 #include "SDL_config.h"
    25 
    26 #if SDL_VIDEO_DRIVER_X11
    27 
    28 #include "SDL.h"
    29 #include "SDL_x11video.h"
    30 #include "SDL_x11dyn.h"
    31 
    32 #include <locale.h>
    33 
    34 #define MAX_BUTTONS             8       /* Maximum number of buttons supported */
    35 #define MAX_TEXT_LINES          32      /* Maximum number of text lines supported */
    36 #define MIN_BUTTON_WIDTH        64      /* Minimum button width */
    37 #define MIN_DIALOG_WIDTH        200     /* Minimum dialog width */
    38 #define MIN_DIALOG_HEIGHT       100     /* Minimum dialog height */
    39 
    40 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
    41 static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-*-*-*-*-*-*-*";
    42 
    43 static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] =
    44 {
    45     { 56,  54,  53  }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,       
    46     { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT,             
    47     { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER,    
    48     { 105, 102, 99  }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND,
    49     { 205, 202, 53  }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED,  
    50 };
    51 
    52 #define SDL_MAKE_RGB( _r, _g, _b )  ( ( ( Uint32 )( _r ) << 16 ) | \
    53                                       ( ( Uint32 )( _g ) << 8 ) |  \
    54                                       ( ( Uint32 )( _b ) ) )
    55 
    56 typedef struct SDL_MessageBoxButtonDataX11
    57 {
    58     int x, y;                           /* Text position */
    59     int length;                         /* Text length */
    60     int text_width;                     /* Text width */
    61 
    62     SDL_Rect rect;                      /* Rectangle for entire button */
    63 
    64     const SDL_MessageBoxButtonData *buttondata;   /* Button data from caller */
    65 } SDL_MessageBoxButtonDataX11;
    66 
    67 typedef struct TextLineData
    68 {
    69     int width;                          /* Width of this text line */
    70     int length;                         /* String length of this text line */
    71     const char *text;                   /* Text for this line */
    72 } TextLineData;
    73 
    74 typedef struct SDL_MessageBoxDataX11
    75 {
    76     XFontSet font_set;                  /* for UTF-8 systems */
    77     XFontStruct *font_struct;            /* Latin1 (ASCII) fallback. */
    78     Window window;
    79     Display *display;
    80     long event_mask;
    81     Atom wm_protocols;
    82     Atom wm_delete_message;
    83 
    84     int dialog_width;                   /* Dialog box width. */
    85     int dialog_height;                  /* Dialog box height. */
    86 
    87     int xtext, ytext;                   /* Text position to start drawing at. */
    88     int numlines;                       /* Count of Text lines. */
    89     int text_height;                    /* Height for text lines. */
    90     TextLineData linedata[ MAX_TEXT_LINES ];
    91 
    92     int *pbuttonid;                     /* Pointer to user return buttonid value. */
    93 
    94     int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
    95     int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
    96 
    97     int numbuttons;                     /* Count of buttons. */
    98     const SDL_MessageBoxButtonData *buttondata;
    99     SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
   100 
   101     Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
   102 
   103     const SDL_MessageBoxData *messageboxdata;
   104 } SDL_MessageBoxDataX11;
   105 
   106 /* Maximum helper for ints. */
   107 static __inline__ int
   108 IntMax( int a, int b )
   109 {
   110     return ( a > b  ) ? a : b;
   111 }
   112 
   113 /* Return width and height for a string. */
   114 static void
   115 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
   116 {
   117     if (SDL_X11_HAVE_UTF8) {
   118         XRectangle overall_ink, overall_logical;
   119         Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
   120         *pwidth = overall_logical.width;
   121         *pheight = overall_logical.height;
   122     } else {
   123         XCharStruct text_structure;
   124         int font_direction, font_ascent, font_descent;
   125         XTextExtents( data->font_struct, str, nbytes,
   126                       &font_direction, &font_ascent, &font_descent,
   127                       &text_structure );
   128         *pwidth = text_structure.width;
   129         *pheight = text_structure.ascent + text_structure.descent;
   130     }
   131 }
   132 
   133 /* Return index of button if position x,y is contained therein. */
   134 static int
   135 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
   136 {
   137     int i;
   138     int numbuttons = data->numbuttons;
   139     SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
   140 
   141     for ( i = 0; i < numbuttons; i++ )
   142     {
   143         SDL_Rect *rect = &buttonpos[ i ].rect;
   144 
   145         if ( ( x >= rect->x ) &&
   146             ( x <= ( rect->x + rect->w ) ) &&
   147             ( y >= rect->y ) &&
   148             ( y <= ( rect->y + rect->h ) ) )
   149         {
   150             return i;
   151         }
   152     }
   153 
   154     return -1;
   155 }
   156 
   157 /* Initialize SDL_MessageBoxData structure and Display, etc. */
   158 static int
   159 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
   160 {
   161     int i;
   162     int numbuttons = messageboxdata->numbuttons;
   163     const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
   164     const SDL_MessageBoxColor *colorhints;
   165 
   166     if ( numbuttons > MAX_BUTTONS ) {
   167         SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
   168         return -1;
   169     }
   170 
   171     data->dialog_width = MIN_DIALOG_WIDTH;
   172     data->dialog_height = MIN_DIALOG_HEIGHT;
   173     data->messageboxdata = messageboxdata;
   174     data->buttondata = buttondata;
   175     data->numbuttons = numbuttons;
   176     data->pbuttonid = pbuttonid;
   177 
   178     data->display = XOpenDisplay( NULL );
   179     if ( !data->display ) {
   180         SDL_SetError("Couldn't open X11 display");
   181         return -1;
   182     }
   183 
   184     if (SDL_X11_HAVE_UTF8) {
   185         char **missing = NULL;
   186         int num_missing = 0;
   187         data->font_set = XCreateFontSet(data->display, g_MessageBoxFont,
   188                                         &missing, &num_missing, NULL);
   189         if ( missing != NULL ) {
   190             XFreeStringList(missing);
   191         }
   192         if ( data->font_set == NULL ) {
   193             SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
   194             return -1;
   195         }
   196     } else {
   197         data->font_struct = XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
   198         if ( data->font_struct == NULL ) {
   199             SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
   200             return -1;
   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     {
   232         const char *text = messageboxdata->message;
   233         TextLineData *plinedata = data->linedata;
   234 
   235         for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ )
   236         {
   237             int height;
   238             char *lf = SDL_strchr( ( char * )text, '\n' );
   239 
   240             data->numlines++;
   241 
   242             /* Only grab length up to lf if it exists and isn't the last line. */
   243             plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
   244             plinedata->text = text;
   245 
   246             GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
   247 
   248             /* Text and widths are the largest we've ever seen. */
   249             data->text_height = IntMax( data->text_height, height );
   250             text_width_max = IntMax( text_width_max, plinedata->width );
   251 
   252             text += plinedata->length + 1;
   253 
   254             /* Break if there are no more linefeeds. */
   255             if ( !lf )
   256                 break;
   257         }
   258 
   259         /* Bump up the text height slightly. */
   260         data->text_height += 2;
   261     }
   262 
   263     /* Loop through all buttons and calculate the button widths and height. */
   264     for ( i = 0; i < data->numbuttons; i++ )
   265     {
   266         int height;
   267 
   268         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
   269         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
   270 
   271         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
   272                             &data->buttonpos[ i ].text_width, &height );
   273 
   274         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
   275         button_text_height = IntMax( button_text_height, height );
   276     }
   277 
   278     if ( data->numlines )
   279     {
   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     }
   291     else
   292     {
   293         /* Button y starts at height of button text. */
   294         ybuttons = button_text_height;
   295     }
   296 
   297     if ( data->numbuttons )
   298     {
   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         {
   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     {
   344         XFreeFontSet( data->display, data->font_set );
   345         data->font_set = NULL;
   346     }
   347 
   348     if ( data->font_struct != NULL )
   349     {
   350         XFreeFont( data->display, data->font_struct );
   351         data->font_struct = NULL;
   352     }
   353 
   354     if ( data->display )
   355     {
   356         if ( data->window != None )
   357         {
   358             XUnmapWindow( data->display, data->window );
   359             XDestroyWindow( data->display, data->window );
   360             data->window = None;
   361         }
   362 
   363         XCloseDisplay( data->display );
   364         data->display = NULL;
   365     }
   366 }
   367 
   368 /* Create and set up our X11 dialog box indow. */
   369 static int
   370 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
   371 {
   372     int x, y;
   373     XSizeHints *sizehints;
   374     XSetWindowAttributes wnd_attr;
   375     Display *display = data->display;
   376     SDL_WindowData *windowdata = NULL;
   377     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   378 
   379     if ( messageboxdata->window ) {
   380         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   381     }
   382 
   383     data->event_mask = ExposureMask |
   384         ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   385         StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   386     wnd_attr.event_mask = data->event_mask;
   387 
   388     data->window = XCreateWindow(
   389                             display, DefaultRootWindow( display ),
   390                             0, 0,
   391                             data->dialog_width, data->dialog_height,
   392                             0, CopyFromParent, InputOutput, CopyFromParent,
   393                             CWEventMask, &wnd_attr );
   394     if ( data->window == None ) {
   395         SDL_SetError("Couldn't create X window");
   396         return -1;
   397     }
   398 
   399     if ( windowdata ) {
   400         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
   401         XSetTransientForHint( display, data->window, windowdata->xwindow );
   402     }
   403 
   404     XStoreName( display, data->window, messageboxdata->title );
   405 
   406     /* Allow the window to be deleted by the window manager */
   407     data->wm_protocols = XInternAtom( display, "WM_PROTOCOLS", False );
   408     data->wm_delete_message = XInternAtom( display, "WM_DELETE_WINDOW", False );
   409     XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
   410 
   411     if ( windowdata ) {
   412         XWindowAttributes attrib;
   413         Window dummy;
   414 
   415         XGetWindowAttributes(display, windowdata->xwindow, &attrib);
   416         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
   417         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
   418         XTranslateCoordinates(display, windowdata->xwindow, DefaultRootWindow( display ), x, y, &x, &y, &dummy);
   419     } else {
   420         int screen = DefaultScreen( display );
   421         x = ( DisplayWidth( display, screen ) - data->dialog_width ) / 2;
   422         y = ( DisplayHeight( display, screen ) - data->dialog_height ) / 3 ;
   423     }
   424     XMoveWindow( display, data->window, x, y );
   425 
   426     sizehints = XAllocSizeHints();
   427     if ( sizehints ) {
   428         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
   429         sizehints->x = x;
   430         sizehints->y = y;
   431         sizehints->width = data->dialog_width;
   432         sizehints->height = data->dialog_height;
   433 
   434         sizehints->min_width = sizehints->max_width = data->dialog_width;
   435         sizehints->min_height = sizehints->max_height = data->dialog_height;
   436 
   437         XSetWMNormalHints( display, data->window, sizehints );
   438 
   439         XFree( sizehints );
   440     }
   441 
   442     XMapRaised( display, data->window );
   443     return 0;
   444 }
   445 
   446 /* Draw our message box. */
   447 static void
   448 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
   449 {
   450     int i;
   451     Window window = data->window;
   452     Display *display = data->display;
   453 
   454     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
   455     XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
   456 
   457     XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   458     for ( i = 0; i < data->numlines; i++ )
   459     {
   460         TextLineData *plinedata = &data->linedata[ i ];
   461 
   462         if (SDL_X11_HAVE_UTF8) {
   463             Xutf8DrawString( display, window, data->font_set, ctx,
   464                              data->xtext, data->ytext + i * data->text_height,
   465                              plinedata->text, plinedata->length );
   466         } else {
   467             XDrawString( display, window, ctx,
   468                          data->xtext, data->ytext + i * data->text_height,
   469                          plinedata->text, plinedata->length );
   470         }
   471     }
   472 
   473     for ( i = 0; i < data->numbuttons; i++ )
   474     {
   475         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   476         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
   477         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
   478         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
   479 
   480         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
   481         XFillRectangle( display, window, ctx,
   482                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
   483                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
   484 
   485         XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
   486         XDrawRectangle( display, window, ctx,
   487                         buttondatax11->rect.x, buttondatax11->rect.y,
   488                         buttondatax11->rect.w, buttondatax11->rect.h );
   489 
   490         XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
   491                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
   492                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   493 
   494         if (SDL_X11_HAVE_UTF8) {
   495             Xutf8DrawString( display, window, data->font_set, ctx,
   496                              buttondatax11->x + offset,
   497                              buttondatax11->y + offset,
   498                              buttondata->text, buttondatax11->length );
   499         } else {
   500             XDrawString( display, window, ctx,
   501                          buttondatax11->x + offset, buttondatax11->y + offset,
   502                          buttondata->text, buttondatax11->length );
   503         }
   504     }
   505 }
   506 
   507 /* Loop and handle message box event messages until something kills it. */
   508 static int
   509 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
   510 {
   511     GC ctx;
   512     XGCValues ctx_vals;
   513     SDL_bool close_dialog = SDL_FALSE;
   514     SDL_bool has_focus = SDL_TRUE;
   515     KeySym last_key_pressed = XK_VoidSymbol;
   516     unsigned long gcflags = GCForeground | GCBackground;
   517 
   518     SDL_zero(ctx_vals);
   519     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   520     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   521 
   522     if (!SDL_X11_HAVE_UTF8) {
   523         gcflags |= GCFont;
   524         ctx_vals.font = data->font_struct->fid;
   525     }
   526 
   527     ctx = XCreateGC( data->display, data->window, gcflags, &ctx_vals );
   528     if ( ctx == None ) {
   529         SDL_SetError("Couldn't create graphics context");
   530         return -1;
   531     }
   532 
   533     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   534     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   535 
   536     while( !close_dialog ) {
   537         XEvent e;
   538         SDL_bool draw = SDL_TRUE;
   539 
   540         XWindowEvent( data->display, data->window, data->event_mask, &e );
   541 
   542         /* If XFilterEvent returns True, then some input method has filtered the
   543            event, and the client should discard the event. */
   544         if ( ( e.type != Expose ) && XFilterEvent( &e, None ) )
   545             continue;
   546 
   547         switch( e.type ) {
   548         case Expose:
   549             if ( e.xexpose.count > 0 ) {
   550                 draw = SDL_FALSE;
   551             }
   552             break;
   553 
   554         case FocusIn:
   555             /* Got focus. */
   556             has_focus = SDL_TRUE;
   557             break;
   558 
   559         case FocusOut:
   560             /* lost focus. Reset button and mouse info. */
   561             has_focus = SDL_FALSE;
   562             data->button_press_index = -1;
   563             data->mouse_over_index = -1;
   564             break;
   565 
   566         case MotionNotify:
   567             if ( has_focus ) {
   568                 /* Mouse moved... */
   569                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   570             }
   571             break;
   572 
   573         case ClientMessage:
   574             if ( e.xclient.message_type == data->wm_protocols &&
   575                  e.xclient.format == 32 &&
   576                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   577                 close_dialog = SDL_TRUE;
   578             }
   579             break;
   580 
   581         case KeyPress:
   582             /* Store key press - we make sure in key release that we got both. */
   583             last_key_pressed = XLookupKeysym( &e.xkey, 0 );
   584             break;
   585 
   586         case KeyRelease:
   587         {
   588             Uint32 mask = 0;
   589             KeySym key = XLookupKeysym( &e.xkey, 0 );
   590 
   591             /* If this is a key release for something we didn't get the key down for, then bail. */
   592             if ( key != last_key_pressed )
   593                 break;
   594 
   595             if ( key == XK_Escape )
   596                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   597             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   598                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   599 
   600             if ( mask ) {
   601                 int i;
   602 
   603                 /* Look for first button with this mask set, and return it if found. */
   604                 for ( i = 0; i < data->numbuttons; i++ ) {
   605                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   606 
   607                     if ( buttondatax11->buttondata->flags & mask ) {
   608                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   609                         close_dialog = SDL_TRUE;
   610                         break;
   611                     }
   612                 }
   613             }
   614             break;
   615         }
   616 
   617         case ButtonPress:
   618             data->button_press_index = -1;
   619             if ( e.xbutton.button == Button1 ) {
   620                 /* Find index of button they clicked on. */
   621                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   622             }
   623             break;
   624 
   625         case ButtonRelease:
   626             /* If button is released over the same button that was clicked down on, then return it. */
   627             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   628                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   629 
   630                 if ( data->button_press_index == button ) {
   631                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   632 
   633                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   634                     close_dialog = SDL_TRUE;
   635                 }
   636             }
   637             data->button_press_index = -1;
   638             break;
   639         }
   640 
   641         if ( draw ) {
   642             /* Draw our dialog box. */
   643             X11_MessageBoxDraw( data, ctx );
   644         }
   645     }
   646 
   647     XFreeGC( data->display, ctx );
   648     return 0;
   649 }
   650 
   651 /* Display an x11 message box. */
   652 int
   653 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   654 {
   655     int ret;
   656     SDL_MessageBoxDataX11 data;
   657 
   658     SDL_zero(data);
   659 
   660     setlocale(LC_ALL, "");
   661 
   662     if ( !SDL_X11_LoadSymbols() )
   663         return -1;
   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     return ret;
   685 }
   686 
   687 #endif /* SDL_VIDEO_DRIVER_X11 */
   688 
   689 /* vi: set ts=4 sw=4 expandtab: */