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