src/video/x11/SDL_x11messagebox.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 13 Nov 2016 22:57:41 -0800
changeset 10609 d702ecbd8ba7
parent 9998 f67cf37e9cd4
child 10737 3406a0f8b041
permissions -rw-r--r--
Patch from Sylvain to fix clang warnings
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2016 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[] = "-*-*-*-*-*-*-*-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, UTF8_STRING;
   382     Display *display = data->display;
   383     SDL_WindowData *windowdata = NULL;
   384     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
   385 
   386     if ( messageboxdata->window ) {
   387         SDL_DisplayData *displaydata =
   388             (SDL_DisplayData *) SDL_GetDisplayForWindow(messageboxdata->window)->driverdata;
   389         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
   390         data->screen = displaydata->screen;
   391     } else {
   392         data->screen = DefaultScreen( display );
   393     }
   394 
   395     data->event_mask = ExposureMask |
   396                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
   397                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
   398     wnd_attr.event_mask = data->event_mask;
   399 
   400     data->window = X11_XCreateWindow(
   401                        display, RootWindow(display, data->screen),
   402                        0, 0,
   403                        data->dialog_width, data->dialog_height,
   404                        0, CopyFromParent, InputOutput, CopyFromParent,
   405                        CWEventMask, &wnd_attr );
   406     if ( data->window == None ) {
   407         return SDL_SetError("Couldn't create X window");
   408     }
   409 
   410     if ( windowdata ) {
   411         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
   412         X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
   413     }
   414 
   415     X11_XStoreName( display, data->window, messageboxdata->title );
   416     _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
   417     UTF8_STRING = X11_XInternAtom(display, "UTF8_STRING", False);
   418     X11_XChangeProperty(display, data->window, _NET_WM_NAME, UTF8_STRING, 8,
   419                     PropModeReplace, (unsigned char *) messageboxdata->title,
   420                     strlen(messageboxdata->title) + 1 );
   421 
   422     /* Let the window manager know this is a dialog box */
   423     _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
   424     _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
   425     X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
   426                     PropModeReplace,
   427                     (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
   428 
   429     /* Allow the window to be deleted by the window manager */
   430     data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
   431     data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
   432     X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
   433 
   434     if ( windowdata ) {
   435         XWindowAttributes attrib;
   436         Window dummy;
   437 
   438         X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
   439         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
   440         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
   441         X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
   442     } else {
   443         const SDL_VideoDevice *dev = SDL_GetVideoDevice();
   444         if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
   445             const SDL_VideoDisplay *dpy = &dev->displays[0];
   446             const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
   447             x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
   448             y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
   449         } else {   /* oh well. This will misposition on a multi-head setup. Init first next time. */
   450             x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
   451             y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
   452         }
   453     }
   454     X11_XMoveWindow( display, data->window, x, y );
   455 
   456     sizehints = X11_XAllocSizeHints();
   457     if ( sizehints ) {
   458         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
   459         sizehints->x = x;
   460         sizehints->y = y;
   461         sizehints->width = data->dialog_width;
   462         sizehints->height = data->dialog_height;
   463 
   464         sizehints->min_width = sizehints->max_width = data->dialog_width;
   465         sizehints->min_height = sizehints->max_height = data->dialog_height;
   466 
   467         X11_XSetWMNormalHints( display, data->window, sizehints );
   468 
   469         X11_XFree( sizehints );
   470     }
   471 
   472     X11_XMapRaised( display, data->window );
   473 
   474 #if SDL_VIDEO_DRIVER_X11_XDBE
   475     /* Initialise a back buffer for double buffering */
   476     if (SDL_X11_HAVE_XDBE) {
   477         int xdbe_major, xdbe_minor;
   478         if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
   479             data->xdbe = SDL_TRUE;
   480             data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
   481         } else {
   482             data->xdbe = SDL_FALSE;
   483         }
   484     }
   485 #endif
   486 
   487     return 0;
   488 }
   489 
   490 /* Draw our message box. */
   491 static void
   492 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
   493 {
   494     int i;
   495     Drawable window = data->window;
   496     Display *display = data->display;
   497 
   498 #if SDL_VIDEO_DRIVER_X11_XDBE
   499     if (SDL_X11_HAVE_XDBE && data->xdbe) {
   500         window = data->buf;
   501         X11_XdbeBeginIdiom(data->display);
   502     }
   503 #endif
   504 
   505     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
   506     X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
   507 
   508     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   509     for ( i = 0; i < data->numlines; i++ ) {
   510         TextLineData *plinedata = &data->linedata[ i ];
   511 
   512         if (SDL_X11_HAVE_UTF8) {
   513             X11_Xutf8DrawString( display, window, data->font_set, ctx,
   514                              data->xtext, data->ytext + i * data->text_height,
   515                              plinedata->text, plinedata->length );
   516         } else {
   517             X11_XDrawString( display, window, ctx,
   518                          data->xtext, data->ytext + i * data->text_height,
   519                          plinedata->text, plinedata->length );
   520         }
   521     }
   522 
   523     for ( i = 0; i < data->numbuttons; i++ ) {
   524         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   525         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
   526         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
   527         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
   528 
   529         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
   530         X11_XFillRectangle( display, window, ctx,
   531                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
   532                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
   533 
   534         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
   535         X11_XDrawRectangle( display, window, ctx,
   536                         buttondatax11->rect.x, buttondatax11->rect.y,
   537                         buttondatax11->rect.w, buttondatax11->rect.h );
   538 
   539         X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
   540                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
   541                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
   542 
   543         if (SDL_X11_HAVE_UTF8) {
   544             X11_Xutf8DrawString( display, window, data->font_set, ctx,
   545                              buttondatax11->x + offset,
   546                              buttondatax11->y + offset,
   547                              buttondata->text, buttondatax11->length );
   548         } else {
   549             X11_XDrawString( display, window, ctx,
   550                          buttondatax11->x + offset, buttondatax11->y + offset,
   551                          buttondata->text, buttondatax11->length );
   552         }
   553     }
   554 
   555 #if SDL_VIDEO_DRIVER_X11_XDBE
   556     if (SDL_X11_HAVE_XDBE && data->xdbe) {
   557         XdbeSwapInfo swap_info;
   558         swap_info.swap_window = data->window;
   559         swap_info.swap_action = XdbeUndefined;
   560         X11_XdbeSwapBuffers(data->display, &swap_info, 1);
   561         X11_XdbeEndIdiom(data->display);
   562     }
   563 #endif
   564 }
   565 
   566 static Bool
   567 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
   568 {
   569     const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
   570     return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
   571 }
   572 
   573 /* Loop and handle message box event messages until something kills it. */
   574 static int
   575 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
   576 {
   577     GC ctx;
   578     XGCValues ctx_vals;
   579     SDL_bool close_dialog = SDL_FALSE;
   580     SDL_bool has_focus = SDL_TRUE;
   581     KeySym last_key_pressed = XK_VoidSymbol;
   582     unsigned long gcflags = GCForeground | GCBackground;
   583 
   584     SDL_zero(ctx_vals);
   585     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   586     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
   587 
   588     if (!SDL_X11_HAVE_UTF8) {
   589         gcflags |= GCFont;
   590         ctx_vals.font = data->font_struct->fid;
   591     }
   592 
   593     ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
   594     if ( ctx == None ) {
   595         return SDL_SetError("Couldn't create graphics context");
   596     }
   597 
   598     data->button_press_index = -1;  /* Reset what button is currently depressed. */
   599     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
   600 
   601     while( !close_dialog ) {
   602         XEvent e;
   603         SDL_bool draw = SDL_TRUE;
   604 
   605         /* can't use XWindowEvent() because it can't handle ClientMessage events. */
   606         /* can't use XNextEvent() because we only want events for this window. */
   607         X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
   608 
   609         /* If X11_XFilterEvent returns True, then some input method has filtered the
   610            event, and the client should discard the event. */
   611         if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
   612             continue;
   613 
   614         switch( e.type ) {
   615         case Expose:
   616             if ( e.xexpose.count > 0 ) {
   617                 draw = SDL_FALSE;
   618             }
   619             break;
   620 
   621         case FocusIn:
   622             /* Got focus. */
   623             has_focus = SDL_TRUE;
   624             break;
   625 
   626         case FocusOut:
   627             /* lost focus. Reset button and mouse info. */
   628             has_focus = SDL_FALSE;
   629             data->button_press_index = -1;
   630             data->mouse_over_index = -1;
   631             break;
   632 
   633         case MotionNotify:
   634             if ( has_focus ) {
   635                 /* Mouse moved... */
   636                 const int previndex = data->mouse_over_index;
   637                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   638                 if (data->mouse_over_index == previndex) {
   639                     draw = SDL_FALSE;
   640                 }
   641             }
   642             break;
   643 
   644         case ClientMessage:
   645             if ( e.xclient.message_type == data->wm_protocols &&
   646                  e.xclient.format == 32 &&
   647                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   648                 close_dialog = SDL_TRUE;
   649             }
   650             break;
   651 
   652         case KeyPress:
   653             /* Store key press - we make sure in key release that we got both. */
   654             last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
   655             break;
   656 
   657         case KeyRelease: {
   658             Uint32 mask = 0;
   659             KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
   660 
   661             /* If this is a key release for something we didn't get the key down for, then bail. */
   662             if ( key != last_key_pressed )
   663                 break;
   664 
   665             if ( key == XK_Escape )
   666                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   667             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   668                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   669 
   670             if ( mask ) {
   671                 int i;
   672 
   673                 /* Look for first button with this mask set, and return it if found. */
   674                 for ( i = 0; i < data->numbuttons; i++ ) {
   675                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   676 
   677                     if ( buttondatax11->buttondata->flags & mask ) {
   678                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   679                         close_dialog = SDL_TRUE;
   680                         break;
   681                     }
   682                 }
   683             }
   684             break;
   685         }
   686 
   687         case ButtonPress:
   688             data->button_press_index = -1;
   689             if ( e.xbutton.button == Button1 ) {
   690                 /* Find index of button they clicked on. */
   691                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   692             }
   693             break;
   694 
   695         case ButtonRelease:
   696             /* If button is released over the same button that was clicked down on, then return it. */
   697             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   698                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   699 
   700                 if ( data->button_press_index == button ) {
   701                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   702 
   703                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   704                     close_dialog = SDL_TRUE;
   705                 }
   706             }
   707             data->button_press_index = -1;
   708             break;
   709         }
   710 
   711         if ( draw ) {
   712             /* Draw our dialog box. */
   713             X11_MessageBoxDraw( data, ctx );
   714         }
   715     }
   716 
   717     X11_XFreeGC( data->display, ctx );
   718     return 0;
   719 }
   720 
   721 static int
   722 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   723 {
   724     int ret;
   725     SDL_MessageBoxDataX11 data;
   726 #if SDL_SET_LOCALE
   727     char *origlocale;
   728 #endif
   729 
   730     SDL_zero(data);
   731 
   732     if ( !SDL_X11_LoadSymbols() )
   733         return -1;
   734 
   735 #if SDL_SET_LOCALE
   736     origlocale = setlocale(LC_ALL, NULL);
   737     if (origlocale != NULL) {
   738         origlocale = SDL_strdup(origlocale);
   739         if (origlocale == NULL) {
   740             return SDL_OutOfMemory();
   741         }
   742         setlocale(LC_ALL, "");
   743     }
   744 #endif
   745 
   746     /* This code could get called from multiple threads maybe? */
   747     X11_XInitThreads();
   748 
   749     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   750     *buttonid = -1;
   751 
   752     /* Init and display the message box. */
   753     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   754     if ( ret != -1 ) {
   755         ret = X11_MessageBoxInitPositions( &data );
   756         if ( ret != -1 ) {
   757             ret = X11_MessageBoxCreateWindow( &data );
   758             if ( ret != -1 ) {
   759                 ret = X11_MessageBoxLoop( &data );
   760             }
   761         }
   762     }
   763 
   764     X11_MessageBoxShutdown( &data );
   765 
   766 #if SDL_SET_LOCALE
   767     if (origlocale) {
   768         setlocale(LC_ALL, origlocale);
   769         SDL_free(origlocale);
   770     }
   771 #endif
   772 
   773     return ret;
   774 }
   775 
   776 /* Display an x11 message box. */
   777 int
   778 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   779 {
   780 #if SDL_FORK_MESSAGEBOX
   781     /* Use a child process to protect against setlocale(). Annoying. */
   782     pid_t pid;
   783     int fds[2];
   784     int status = 0;
   785 
   786     if (pipe(fds) == -1) {
   787         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   788     }
   789 
   790     pid = fork();
   791     if (pid == -1) {  /* failed */
   792         close(fds[0]);
   793         close(fds[1]);
   794         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   795     } else if (pid == 0) {  /* we're the child */
   796         int exitcode = 0;
   797         close(fds[0]);
   798         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   799         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   800             exitcode = 1;
   801         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   802             exitcode = 1;
   803         close(fds[1]);
   804         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   805     } else {  /* we're the parent */
   806         pid_t rc;
   807         close(fds[1]);
   808         do {
   809             rc = waitpid(pid, &status, 0);
   810         } while ((rc == -1) && (errno == EINTR));
   811 
   812         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   813 
   814         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   815             return SDL_SetError("msgbox child process failed");
   816         }
   817 
   818         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   819             status = -1;
   820         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   821             status = -1;
   822         close(fds[0]);
   823 
   824         return status;
   825     }
   826 #else
   827     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   828 #endif
   829 }
   830 #endif /* SDL_VIDEO_DRIVER_X11 */
   831 
   832 /* vi: set ts=4 sw=4 expandtab: */