cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_x11messagebox.c (27244B)


      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
     50static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
     51static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-120-*-*-*-*-*-*";
     52
     53static 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
     65typedef 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
     75typedef 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
     81typedef 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. */
    115static SDL_INLINE int
    116IntMax( int a, int b )
    117{
    118    return ( a > b  ) ? a : b;
    119}
    120
    121/* Return width and height for a string. */
    122static void
    123GetTextWidthHeight( 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. */
    142static int
    143GetHitButtonIndex( 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. */
    164static int
    165X11_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. */
    221static int
    222X11_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. */
    337static void
    338X11_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. */
    363static int
    364X11_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. */
    444static void
    445X11_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. */
    503static int
    504X11_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
    648static int
    649X11_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. */
    704int
    705X11_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: */