src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 28 May 2015 00:30:21 -0400
changeset 9657 fbc01731d914
parent 9619 b94b6d0bff0f
child 9695 363a7880b4f7
permissions -rw-r--r--
X11: Add Xdbe support to message boxes (thanks, Melker!).

Without this, message boxes with a lot of text will noticibly flicker as
you mouse over buttons.

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