src/video/x11/SDL_x11messagebox.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 13 Jun 2015 13:34:30 -0700
changeset 9733 dd3c3024723c
parent 9698 bf0257e323d2
child 9801 752406828724
permissions -rw-r--r--
Fixed bug 3010 - SDL_x11messagebox.c needs including X11/keysym.h

Ozkan Sezer

SDL_x11messagebox.c needs including <X11/keysym.h> otherwise XK_Escape,
etc might not be available to it. Seen this with x.org-x11-6.8.2.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2015 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_internal.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 <X11/keysym.h>
    32 #include <locale.h>
    33 
    34 
    35 #define SDL_FORK_MESSAGEBOX 1
    36 #define SDL_SET_LOCALE      1
    37 
    38 #if SDL_FORK_MESSAGEBOX
    39 #include <sys/types.h>
    40 #include <sys/wait.h>
    41 #include <unistd.h>
    42 #include <errno.h>
    43 #endif
    44 
    45 #define MAX_BUTTONS             8       /* Maximum number of buttons supported */
    46 #define MAX_TEXT_LINES          32      /* Maximum number of text lines supported */
    47 #define MIN_BUTTON_WIDTH        64      /* Minimum button width */
    48 #define MIN_DIALOG_WIDTH        200     /* Minimum dialog width */
    49 #define MIN_DIALOG_HEIGHT       100     /* Minimum dialog height */
    50 
    51 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
    52 static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-120-*-*-*-*-*-*";
    53 
    54 static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = {
    55     { 56,  54,  53  }, /* SDL_MESSAGEBOX_COLOR_BACKGROUND, */
    56     { 209, 207, 205 }, /* SDL_MESSAGEBOX_COLOR_TEXT, */
    57     { 140, 135, 129 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, */
    58     { 105, 102, 99  }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, */
    59     { 205, 202, 53  }, /* SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, */
    60 };
    61 
    62 #define SDL_MAKE_RGB( _r, _g, _b )  ( ( ( Uint32 )( _r ) << 16 ) | \
    63                                       ( ( Uint32 )( _g ) << 8 ) |  \
    64                                       ( ( Uint32 )( _b ) ) )
    65 
    66 typedef struct SDL_MessageBoxButtonDataX11 {
    67     int x, y;                           /* Text position */
    68     int length;                         /* Text length */
    69     int text_width;                     /* Text width */
    70 
    71     SDL_Rect rect;                      /* Rectangle for entire button */
    72 
    73     const SDL_MessageBoxButtonData *buttondata;   /* Button data from caller */
    74 } SDL_MessageBoxButtonDataX11;
    75 
    76 typedef struct TextLineData {
    77     int width;                          /* Width of this text line */
    78     int length;                         /* String length of this text line */
    79     const char *text;                   /* Text for this line */
    80 } TextLineData;
    81 
    82 typedef struct SDL_MessageBoxDataX11
    83 {
    84     Display *display;
    85     int screen;
    86     Window window;
    87 #if SDL_VIDEO_DRIVER_X11_XDBE
    88     XdbeBackBuffer buf;
    89     SDL_bool xdbe;                      /* Whether Xdbe is present or not */
    90 #endif
    91     long event_mask;
    92     Atom wm_protocols;
    93     Atom wm_delete_message;
    94 
    95     int dialog_width;                   /* Dialog box width. */
    96     int dialog_height;                  /* Dialog box height. */
    97 
    98     XFontSet font_set;                  /* for UTF-8 systems */
    99     XFontStruct *font_struct;           /* Latin1 (ASCII) fallback. */
   100     int xtext, ytext;                   /* Text position to start drawing at. */
   101     int numlines;                       /* Count of Text lines. */
   102     int text_height;                    /* Height for text lines. */
   103     TextLineData linedata[ MAX_TEXT_LINES ];
   104 
   105     int *pbuttonid;                     /* Pointer to user return buttonid value. */
   106 
   107     int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
   108     int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
   109 
   110     int numbuttons;                     /* Count of buttons. */
   111     const SDL_MessageBoxButtonData *buttondata;
   112     SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
   113 
   114     Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
   115 
   116     const SDL_MessageBoxData *messageboxdata;
   117 } SDL_MessageBoxDataX11;
   118 
   119 /* Maximum helper for ints. */
   120 static SDL_INLINE int
   121 IntMax( int a, int b )
   122 {
   123     return ( a > b  ) ? a : b;
   124 }
   125 
   126 /* Return width and height for a string. */
   127 static void
   128 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
   129 {
   130     if (SDL_X11_HAVE_UTF8) {
   131         XRectangle overall_ink, overall_logical;
   132         X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
   133         *pwidth = overall_logical.width;
   134         *pheight = overall_logical.height;
   135     } else {
   136         XCharStruct text_structure;
   137         int font_direction, font_ascent, font_descent;
   138         X11_XTextExtents( data->font_struct, str, nbytes,
   139                       &font_direction, &font_ascent, &font_descent,
   140                       &text_structure );
   141         *pwidth = text_structure.width;
   142         *pheight = text_structure.ascent + text_structure.descent;
   143     }
   144 }
   145 
   146 /* Return index of button if position x,y is contained therein. */
   147 static int
   148 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
   149 {
   150     int i;
   151     int numbuttons = data->numbuttons;
   152     SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
   153 
   154     for ( i = 0; i < numbuttons; i++ ) {
   155         SDL_Rect *rect = &buttonpos[ i ].rect;
   156 
   157         if ( ( x >= rect->x ) &&
   158              ( x <= ( rect->x + rect->w ) ) &&
   159              ( y >= rect->y ) &&
   160              ( y <= ( rect->y + rect->h ) ) ) {
   161             return i;
   162         }
   163     }
   164 
   165     return -1;
   166 }
   167 
   168 /* Initialize SDL_MessageBoxData structure and Display, etc. */
   169 static int
   170 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
   171 {
   172     int i;
   173     int numbuttons = messageboxdata->numbuttons;
   174     const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
   175     const SDL_MessageBoxColor *colorhints;
   176 
   177     if ( numbuttons > MAX_BUTTONS ) {
   178         return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
   179     }
   180 
   181     data->dialog_width = MIN_DIALOG_WIDTH;
   182     data->dialog_height = MIN_DIALOG_HEIGHT;
   183     data->messageboxdata = messageboxdata;
   184     data->buttondata = buttondata;
   185     data->numbuttons = numbuttons;
   186     data->pbuttonid = pbuttonid;
   187 
   188     data->display = X11_XOpenDisplay( NULL );
   189     if ( !data->display ) {
   190         return SDL_SetError("Couldn't open X11 display");
   191     }
   192 
   193     if (SDL_X11_HAVE_UTF8) {
   194         char **missing = NULL;
   195         int num_missing = 0;
   196         data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
   197                                         &missing, &num_missing, NULL);
   198         if ( missing != NULL ) {
   199             X11_XFreeStringList(missing);
   200         }
   201         if ( data->font_set == NULL ) {
   202             return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
   203         }
   204     } else {
   205         data->font_struct = X11_XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
   206         if ( data->font_struct == NULL ) {
   207             return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
   208         }
   209     }
   210 
   211     if ( messageboxdata->colorScheme ) {
   212         colorhints = messageboxdata->colorScheme->colors;
   213     } else {
   214         colorhints = g_default_colors;
   215     }
   216 
   217     /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
   218     for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
   219         data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
   220     }
   221 
   222     return 0;
   223 }
   224 
   225 /* Calculate and initialize text and button locations. */
   226 static int
   227 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
   228 {
   229     int i;
   230     int ybuttons;
   231     int text_width_max = 0;
   232     int button_text_height = 0;
   233     int button_width = MIN_BUTTON_WIDTH;
   234     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   235 
   236     /* Go over text and break linefeeds into separate lines. */
   237     if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
   238         const char *text = messageboxdata->message;
   239         TextLineData *plinedata = data->linedata;
   240 
   241         for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
   242             int height;
   243             char *lf = SDL_strchr( ( char * )text, '\n' );
   244 
   245             data->numlines++;
   246 
   247             /* Only grab length up to lf if it exists and isn't the last line. */
   248             plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
   249             plinedata->text = text;
   250 
   251             GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
   252 
   253             /* Text and widths are the largest we've ever seen. */
   254             data->text_height = IntMax( data->text_height, height );
   255             text_width_max = IntMax( text_width_max, plinedata->width );
   256 
   257             if (lf && (lf > text) && (lf[-1] == '\r')) {
   258                 plinedata->length--;
   259             }
   260 
   261             text += plinedata->length + 1;
   262 
   263             /* Break if there are no more linefeeds. */
   264             if ( !lf )
   265                 break;
   266         }
   267 
   268         /* Bump up the text height slightly. */
   269         data->text_height += 2;
   270     }
   271 
   272     /* Loop through all buttons and calculate the button widths and height. */
   273     for ( i = 0; i < data->numbuttons; i++ ) {
   274         int height;
   275 
   276         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
   277         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
   278 
   279         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
   280                             &data->buttonpos[ i ].text_width, &height );
   281 
   282         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
   283         button_text_height = IntMax( button_text_height, height );
   284     }
   285 
   286     if ( data->numlines ) {
   287         /* x,y for this line of text. */
   288         data->xtext = data->text_height;
   289         data->ytext = data->text_height + data->text_height;
   290 
   291         /* Bump button y down to bottom of text. */
   292         ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
   293 
   294         /* Bump the dialog box width and height up if needed. */
   295         data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
   296         data->dialog_height = IntMax( data->dialog_height, ybuttons );
   297     } else {
   298         /* Button y starts at height of button text. */
   299         ybuttons = button_text_height;
   300     }
   301 
   302     if ( data->numbuttons ) {
   303         int x, y;
   304         int width_of_buttons;
   305         int button_spacing = button_text_height;
   306         int button_height = 2 * button_text_height;
   307 
   308         /* Bump button width up a bit. */
   309         button_width += button_text_height;
   310 
   311         /* Get width of all buttons lined up. */
   312         width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
   313 
   314         /* Bump up dialog width and height if buttons are wider than text. */
   315         data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
   316         data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
   317 
   318         /* Location for first button. */
   319         x = ( data->dialog_width - width_of_buttons ) / 2;
   320         y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
   321 
   322         for ( i = 0; i < data->numbuttons; i++ ) {
   323             /* Button coordinates. */
   324             data->buttonpos[ i ].rect.x = x;
   325             data->buttonpos[ i ].rect.y = y;
   326             data->buttonpos[ i ].rect.w = button_width;
   327             data->buttonpos[ i ].rect.h = button_height;
   328 
   329             /* Button text coordinates. */
   330             data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
   331             data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
   332 
   333             /* Scoot over for next button. */
   334             x += button_width + button_spacing;
   335         }
   336     }
   337 
   338     return 0;
   339 }
   340 
   341 /* Free SDL_MessageBoxData data. */
   342 static void
   343 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
   344 {
   345     if ( data->font_set != NULL ) {
   346         X11_XFreeFontSet( data->display, data->font_set );
   347         data->font_set = NULL;
   348     }
   349 
   350     if ( data->font_struct != NULL ) {
   351         X11_XFreeFont( data->display, data->font_struct );
   352         data->font_struct = NULL;
   353     }
   354 
   355 #if SDL_VIDEO_DRIVER_X11_XDBE
   356     if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
   357         X11_XdbeDeallocateBackBufferName(data->display, data->buf);
   358     }
   359 #endif
   360 
   361     if ( data->display ) {
   362         if ( data->window != None ) {
   363             X11_XWithdrawWindow( data->display, data->window, data->screen );
   364             X11_XDestroyWindow( data->display, data->window );
   365             data->window = None;
   366         }
   367 
   368         X11_XCloseDisplay( data->display );
   369         data->display = NULL;
   370     }
   371 }
   372 
   373 /* Create and set up our X11 dialog box indow. */
   374 static int
   375 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
   376 {
   377     int x, y;
   378     XSizeHints *sizehints;
   379     XSetWindowAttributes wnd_attr;
   380     Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME, UTF8_STRING;
   381     Display *display = data->display;
   382     SDL_WindowData *windowdata = NULL;
   383     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   384 
   385     if ( messageboxdata->window ) {
   386         SDL_DisplayData *displaydata =
   387             (SDL_DisplayData *) SDL_GetDisplayForWindow(messageboxdata->window)->driverdata;
   388         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   389         data->screen = displaydata->screen;
   390     } else {
   391         data->screen = DefaultScreen( display );
   392     }
   393 
   394     data->event_mask = ExposureMask |
   395                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   396                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   397     wnd_attr.event_mask = data->event_mask;
   398 
   399     data->window = X11_XCreateWindow(
   400                        display, RootWindow(display, data->screen),
   401                        0, 0,
   402                        data->dialog_width, data->dialog_height,
   403                        0, CopyFromParent, InputOutput, CopyFromParent,
   404                        CWEventMask, &wnd_attr );
   405     if ( data->window == None ) {
   406         return SDL_SetError("Couldn't create X window");
   407     }
   408 
   409     if ( windowdata ) {
   410         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
   411         X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
   412     }
   413 
   414     X11_XStoreName( display, data->window, messageboxdata->title );
   415     _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
   416     UTF8_STRING = X11_XInternAtom(display, "UTF8_STRING", False);
   417     X11_XChangeProperty(display, data->window, _NET_WM_NAME, UTF8_STRING, 8,
   418                     PropModeReplace, (unsigned char *) messageboxdata->title,
   419                     strlen(messageboxdata->title) + 1 );
   420 
   421     /* Let the window manager know this is a dialog box */
   422     _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
   423     _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
   424     X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   425                     PropModeReplace,
   426                     (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
   427 
   428     /* Allow the window to be deleted by the window manager */
   429     data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
   430     data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
   431     X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
   432 
   433     if ( windowdata ) {
   434         XWindowAttributes attrib;
   435         Window dummy;
   436 
   437         X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
   438         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
   439         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
   440         X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
   441     } else {
   442         x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
   443         y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
   444     }
   445     X11_XMoveWindow( display, data->window, x, y );
   446 
   447     sizehints = X11_XAllocSizeHints();
   448     if ( sizehints ) {
   449         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
   450         sizehints->x = x;
   451         sizehints->y = y;
   452         sizehints->width = data->dialog_width;
   453         sizehints->height = data->dialog_height;
   454 
   455         sizehints->min_width = sizehints->max_width = data->dialog_width;
   456         sizehints->min_height = sizehints->max_height = data->dialog_height;
   457 
   458         X11_XSetWMNormalHints( display, data->window, sizehints );
   459 
   460         X11_XFree( sizehints );
   461     }
   462 
   463     X11_XMapRaised( display, data->window );
   464 
   465 #if SDL_VIDEO_DRIVER_X11_XDBE
   466     /* Initialise a back buffer for double buffering */
   467     if (SDL_X11_HAVE_XDBE) {
   468         int xdbe_major, xdbe_minor;
   469         if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
   470             data->xdbe = SDL_TRUE;
   471             data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
   472         } else {
   473             data->xdbe = SDL_FALSE;
   474         }
   475     }
   476 #endif
   477 
   478     return 0;
   479 }
   480 
   481 /* Draw our message box. */
   482 static void
   483 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
   484 {
   485     int i;
   486     Drawable window = data->window;
   487     Display *display = data->display;
   488 
   489 #if SDL_VIDEO_DRIVER_X11_XDBE
   490     if (SDL_X11_HAVE_XDBE && data->xdbe) {
   491         window = data->buf;
   492         X11_XdbeBeginIdiom(data->display);
   493     }
   494 #endif
   495 
   496     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
   497     X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
   498 
   499     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   500     for ( i = 0; i < data->numlines; i++ ) {
   501         TextLineData *plinedata = &data->linedata[ i ];
   502 
   503         if (SDL_X11_HAVE_UTF8) {
   504             X11_Xutf8DrawString( display, window, data->font_set, ctx,
   505                              data->xtext, data->ytext + i * data->text_height,
   506                              plinedata->text, plinedata->length );
   507         } else {
   508             X11_XDrawString( display, window, ctx,
   509                          data->xtext, data->ytext + i * data->text_height,
   510                          plinedata->text, plinedata->length );
   511         }
   512     }
   513 
   514     for ( i = 0; i < data->numbuttons; i++ ) {
   515         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   516         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
   517         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
   518         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
   519 
   520         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
   521         X11_XFillRectangle( display, window, ctx,
   522                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
   523                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
   524 
   525         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
   526         X11_XDrawRectangle( display, window, ctx,
   527                         buttondatax11->rect.x, buttondatax11->rect.y,
   528                         buttondatax11->rect.w, buttondatax11->rect.h );
   529 
   530         X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
   531                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
   532                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   533 
   534         if (SDL_X11_HAVE_UTF8) {
   535             X11_Xutf8DrawString( display, window, data->font_set, ctx,
   536                              buttondatax11->x + offset,
   537                              buttondatax11->y + offset,
   538                              buttondata->text, buttondatax11->length );
   539         } else {
   540             X11_XDrawString( display, window, ctx,
   541                          buttondatax11->x + offset, buttondatax11->y + offset,
   542                          buttondata->text, buttondatax11->length );
   543         }
   544     }
   545 
   546 #if SDL_VIDEO_DRIVER_X11_XDBE
   547     if (SDL_X11_HAVE_XDBE && data->xdbe) {
   548         XdbeSwapInfo swap_info;
   549         swap_info.swap_window = data->window;
   550         swap_info.swap_action = XdbeUndefined;
   551         X11_XdbeSwapBuffers(data->display, &swap_info, 1);
   552         X11_XdbeEndIdiom(data->display);
   553     }
   554 #endif
   555 }
   556 
   557 static Bool
   558 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
   559 {
   560     const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
   561     return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
   562 }
   563 
   564 /* Loop and handle message box event messages until something kills it. */
   565 static int
   566 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
   567 {
   568     GC ctx;
   569     XGCValues ctx_vals;
   570     SDL_bool close_dialog = SDL_FALSE;
   571     SDL_bool has_focus = SDL_TRUE;
   572     KeySym last_key_pressed = XK_VoidSymbol;
   573     unsigned long gcflags = GCForeground | GCBackground;
   574 
   575     SDL_zero(ctx_vals);
   576     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   577     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   578 
   579     if (!SDL_X11_HAVE_UTF8) {
   580         gcflags |= GCFont;
   581         ctx_vals.font = data->font_struct->fid;
   582     }
   583 
   584     ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
   585     if ( ctx == None ) {
   586         return SDL_SetError("Couldn't create graphics context");
   587     }
   588 
   589     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   590     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   591 
   592     while( !close_dialog ) {
   593         XEvent e;
   594         SDL_bool draw = SDL_TRUE;
   595 
   596         /* can't use XWindowEvent() because it can't handle ClientMessage events. */
   597         /* can't use XNextEvent() because we only want events for this window. */
   598         X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
   599 
   600         /* If X11_XFilterEvent returns True, then some input method has filtered the
   601            event, and the client should discard the event. */
   602         if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
   603             continue;
   604 
   605         switch( e.type ) {
   606         case Expose:
   607             if ( e.xexpose.count > 0 ) {
   608                 draw = SDL_FALSE;
   609             }
   610             break;
   611 
   612         case FocusIn:
   613             /* Got focus. */
   614             has_focus = SDL_TRUE;
   615             break;
   616 
   617         case FocusOut:
   618             /* lost focus. Reset button and mouse info. */
   619             has_focus = SDL_FALSE;
   620             data->button_press_index = -1;
   621             data->mouse_over_index = -1;
   622             break;
   623 
   624         case MotionNotify:
   625             if ( has_focus ) {
   626                 /* Mouse moved... */
   627                 const int previndex = data->mouse_over_index;
   628                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   629                 if (data->mouse_over_index == previndex) {
   630                     draw = SDL_FALSE;
   631                 }
   632             }
   633             break;
   634 
   635         case ClientMessage:
   636             if ( e.xclient.message_type == data->wm_protocols &&
   637                  e.xclient.format == 32 &&
   638                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   639                 close_dialog = SDL_TRUE;
   640             }
   641             break;
   642 
   643         case KeyPress:
   644             /* Store key press - we make sure in key release that we got both. */
   645             last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
   646             break;
   647 
   648         case KeyRelease: {
   649             Uint32 mask = 0;
   650             KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
   651 
   652             /* If this is a key release for something we didn't get the key down for, then bail. */
   653             if ( key != last_key_pressed )
   654                 break;
   655 
   656             if ( key == XK_Escape )
   657                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   658             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   659                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   660 
   661             if ( mask ) {
   662                 int i;
   663 
   664                 /* Look for first button with this mask set, and return it if found. */
   665                 for ( i = 0; i < data->numbuttons; i++ ) {
   666                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   667 
   668                     if ( buttondatax11->buttondata->flags & mask ) {
   669                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   670                         close_dialog = SDL_TRUE;
   671                         break;
   672                     }
   673                 }
   674             }
   675             break;
   676         }
   677 
   678         case ButtonPress:
   679             data->button_press_index = -1;
   680             if ( e.xbutton.button == Button1 ) {
   681                 /* Find index of button they clicked on. */
   682                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   683             }
   684             break;
   685 
   686         case ButtonRelease:
   687             /* If button is released over the same button that was clicked down on, then return it. */
   688             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   689                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   690 
   691                 if ( data->button_press_index == button ) {
   692                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   693 
   694                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   695                     close_dialog = SDL_TRUE;
   696                 }
   697             }
   698             data->button_press_index = -1;
   699             break;
   700         }
   701 
   702         if ( draw ) {
   703             /* Draw our dialog box. */
   704             X11_MessageBoxDraw( data, ctx );
   705         }
   706     }
   707 
   708     X11_XFreeGC( data->display, ctx );
   709     return 0;
   710 }
   711 
   712 static int
   713 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   714 {
   715     int ret;
   716     SDL_MessageBoxDataX11 data;
   717 #if SDL_SET_LOCALE
   718     char *origlocale;
   719 #endif
   720 
   721     SDL_zero(data);
   722 
   723     if ( !SDL_X11_LoadSymbols() )
   724         return -1;
   725 
   726 #if SDL_SET_LOCALE
   727     origlocale = setlocale(LC_ALL, NULL);
   728     if (origlocale != NULL) {
   729         origlocale = SDL_strdup(origlocale);
   730         if (origlocale == NULL) {
   731             return SDL_OutOfMemory();
   732         }
   733         setlocale(LC_ALL, "");
   734     }
   735 #endif
   736 
   737     /* This code could get called from multiple threads maybe? */
   738     X11_XInitThreads();
   739 
   740     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   741     *buttonid = -1;
   742 
   743     /* Init and display the message box. */
   744     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   745     if ( ret != -1 ) {
   746         ret = X11_MessageBoxInitPositions( &data );
   747         if ( ret != -1 ) {
   748             ret = X11_MessageBoxCreateWindow( &data );
   749             if ( ret != -1 ) {
   750                 ret = X11_MessageBoxLoop( &data );
   751             }
   752         }
   753     }
   754 
   755     X11_MessageBoxShutdown( &data );
   756 
   757 #if SDL_SET_LOCALE
   758     if (origlocale) {
   759         setlocale(LC_ALL, origlocale);
   760         SDL_free(origlocale);
   761     }
   762 #endif
   763 
   764     return ret;
   765 }
   766 
   767 /* Display an x11 message box. */
   768 int
   769 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   770 {
   771 #if SDL_FORK_MESSAGEBOX
   772     /* Use a child process to protect against setlocale(). Annoying. */
   773     pid_t pid;
   774     int fds[2];
   775     int status = 0;
   776 
   777     if (pipe(fds) == -1) {
   778         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   779     }
   780 
   781     pid = fork();
   782     if (pid == -1) {  /* failed */
   783         close(fds[0]);
   784         close(fds[1]);
   785         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   786     } else if (pid == 0) {  /* we're the child */
   787         int exitcode = 0;
   788         close(fds[0]);
   789         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   790         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   791             exitcode = 1;
   792         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   793             exitcode = 1;
   794         close(fds[1]);
   795         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   796     } else {  /* we're the parent */
   797         pid_t rc;
   798         close(fds[1]);
   799         do {
   800             rc = waitpid(pid, &status, 0);
   801         } while ((rc == -1) && (errno == EINTR));
   802 
   803         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   804 
   805         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   806             return SDL_SetError("msgbox child process failed");
   807         }
   808 
   809         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   810             status = -1;
   811         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   812             status = -1;
   813         close(fds[0]);
   814 
   815         return status;
   816     }
   817 #else
   818     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   819 #endif
   820 }
   821 #endif /* SDL_VIDEO_DRIVER_X11 */
   822 
   823 /* vi: set ts=4 sw=4 expandtab: */