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