src/video/x11/SDL_x11messagebox.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 11 Feb 2015 01:48:52 -0500
changeset 9339 45c49e8c9416
parent 8149 681eb46b8ac4
child 9423 3df83030dab9
permissions -rw-r--r--
X11: Set dialog hint on message boxes (thanks, Melker!).

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