src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 18 Oct 2013 01:36:41 -0400
changeset 7827 a03ec8de0426
parent 7522 b186724247dd
child 7860 2b0bcdea3a79
permissions -rw-r--r--
Don't supply duplicate X11 symbols inside SDL.

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