src/video/x11/SDL_x11messagebox.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8132 06922987b6ff
child 9339 45c49e8c9416
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 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[] = "-*-*-*-*-*-*-*-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     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 SDL_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                 int previndex = data->mouse_over_index;
   564                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   565                 if (data->mouse_over_index == previndex) {
   566                     draw = SDL_FALSE;
   567                 }
   568             }
   569             break;
   570 
   571         case ClientMessage:
   572             if ( e.xclient.message_type == data->wm_protocols &&
   573                  e.xclient.format == 32 &&
   574                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
   575                 close_dialog = SDL_TRUE;
   576             }
   577             break;
   578 
   579         case KeyPress:
   580             /* Store key press - we make sure in key release that we got both. */
   581             last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
   582             break;
   583 
   584         case KeyRelease: {
   585             Uint32 mask = 0;
   586             KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
   587 
   588             /* If this is a key release for something we didn't get the key down for, then bail. */
   589             if ( key != last_key_pressed )
   590                 break;
   591 
   592             if ( key == XK_Escape )
   593                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
   594             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
   595                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
   596 
   597             if ( mask ) {
   598                 int i;
   599 
   600                 /* Look for first button with this mask set, and return it if found. */
   601                 for ( i = 0; i < data->numbuttons; i++ ) {
   602                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
   603 
   604                     if ( buttondatax11->buttondata->flags & mask ) {
   605                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
   606                         close_dialog = SDL_TRUE;
   607                         break;
   608                     }
   609                 }
   610             }
   611             break;
   612         }
   613 
   614         case ButtonPress:
   615             data->button_press_index = -1;
   616             if ( e.xbutton.button == Button1 ) {
   617                 /* Find index of button they clicked on. */
   618                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   619             }
   620             break;
   621 
   622         case ButtonRelease:
   623             /* If button is released over the same button that was clicked down on, then return it. */
   624             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
   625                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
   626 
   627                 if ( data->button_press_index == button ) {
   628                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
   629 
   630                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
   631                     close_dialog = SDL_TRUE;
   632                 }
   633             }
   634             data->button_press_index = -1;
   635             break;
   636         }
   637 
   638         if ( draw ) {
   639             /* Draw our dialog box. */
   640             X11_MessageBoxDraw( data, ctx );
   641         }
   642     }
   643 
   644     X11_XFreeGC( data->display, ctx );
   645     return 0;
   646 }
   647 
   648 static int
   649 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   650 {
   651     int ret;
   652     SDL_MessageBoxDataX11 data;
   653 #if SDL_SET_LOCALE
   654     char *origlocale;
   655 #endif
   656 
   657     SDL_zero(data);
   658 
   659     if ( !SDL_X11_LoadSymbols() )
   660         return -1;
   661 
   662 #if SDL_SET_LOCALE
   663     origlocale = setlocale(LC_ALL, NULL);
   664     if (origlocale != NULL) {
   665         origlocale = SDL_strdup(origlocale);
   666         if (origlocale == NULL) {
   667             return SDL_OutOfMemory();
   668         }
   669         setlocale(LC_ALL, "");
   670     }
   671 #endif
   672 
   673     /* This code could get called from multiple threads maybe? */
   674     X11_XInitThreads();
   675 
   676     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
   677     *buttonid = -1;
   678 
   679     /* Init and display the message box. */
   680     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
   681     if ( ret != -1 ) {
   682         ret = X11_MessageBoxInitPositions( &data );
   683         if ( ret != -1 ) {
   684             ret = X11_MessageBoxCreateWindow( &data );
   685             if ( ret != -1 ) {
   686                 ret = X11_MessageBoxLoop( &data );
   687             }
   688         }
   689     }
   690 
   691     X11_MessageBoxShutdown( &data );
   692 
   693 #if SDL_SET_LOCALE
   694     if (origlocale) {
   695         setlocale(LC_ALL, origlocale);
   696         SDL_free(origlocale);
   697     }
   698 #endif
   699 
   700     return ret;
   701 }
   702 
   703 /* Display an x11 message box. */
   704 int
   705 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
   706 {
   707 #if SDL_FORK_MESSAGEBOX
   708     /* Use a child process to protect against setlocale(). Annoying. */
   709     pid_t pid;
   710     int fds[2];
   711     int status = 0;
   712 
   713     /* Need to flush here in case someone has turned grab off and it hasn't gone through yet, etc. */
   714     X11_XFlush(data->display);
   715 
   716     if (pipe(fds) == -1) {
   717         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   718     }
   719 
   720     pid = fork();
   721     if (pid == -1) {  /* failed */
   722         close(fds[0]);
   723         close(fds[1]);
   724         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
   725     } else if (pid == 0) {  /* we're the child */
   726         int exitcode = 0;
   727         close(fds[0]);
   728         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   729         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
   730             exitcode = 1;
   731         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
   732             exitcode = 1;
   733         close(fds[1]);
   734         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
   735     } else {  /* we're the parent */
   736         pid_t rc;
   737         close(fds[1]);
   738         do {
   739             rc = waitpid(pid, &status, 0);
   740         } while ((rc == -1) && (errno == EINTR));
   741 
   742         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
   743 
   744         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
   745             return SDL_SetError("msgbox child process failed");
   746         }
   747 
   748         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
   749             status = -1;
   750         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
   751             status = -1;
   752         close(fds[0]);
   753 
   754         return status;
   755     }
   756 #else
   757     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
   758 #endif
   759 }
   760 #endif /* SDL_VIDEO_DRIVER_X11 */
   761 
   762 /* vi: set ts=4 sw=4 expandtab: */