sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_x11messagebox.c (32395B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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_x11messagebox.h"
     30 
     31 #include <X11/keysym.h>
     32 #include <locale.h>
     33 
     34 
     35 #define SDL_FORK_MESSAGEBOX 1
     36 #define SDL_SET_LOCALE      1
     37 
     38 #if SDL_FORK_MESSAGEBOX
     39 #include <sys/types.h>
     40 #include <sys/wait.h>
     41 #include <unistd.h>
     42 #include <errno.h>
     43 #endif
     44 
     45 #define MAX_BUTTONS             8       /* Maximum number of buttons 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[] = "-*-*-medium-r-normal--*-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 #if SDL_VIDEO_DRIVER_X11_XDBE
     87     XdbeBackBuffer buf;
     88     SDL_bool xdbe;                      /* Whether Xdbe is present or not */
     89 #endif
     90     long event_mask;
     91     Atom wm_protocols;
     92     Atom wm_delete_message;
     93 
     94     int dialog_width;                   /* Dialog box width. */
     95     int dialog_height;                  /* Dialog box height. */
     96 
     97     XFontSet font_set;                  /* for UTF-8 systems */
     98     XFontStruct *font_struct;           /* Latin1 (ASCII) fallback. */
     99     int xtext, ytext;                   /* Text position to start drawing at. */
    100     int numlines;                       /* Count of Text lines. */
    101     int text_height;                    /* Height for text lines. */
    102     TextLineData *linedata;
    103 
    104     int *pbuttonid;                     /* Pointer to user return buttonid value. */
    105 
    106     int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
    107     int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
    108 
    109     int numbuttons;                     /* Count of buttons. */
    110     const SDL_MessageBoxButtonData *buttondata;
    111     SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
    112 
    113     Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
    114 
    115     const SDL_MessageBoxData *messageboxdata;
    116 } SDL_MessageBoxDataX11;
    117 
    118 /* Maximum helper for ints. */
    119 static SDL_INLINE int
    120 IntMax( int a, int b )
    121 {
    122     return ( a > b  ) ? a : b;
    123 }
    124 
    125 /* Return width and height for a string. */
    126 static void
    127 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
    128 {
    129     if (SDL_X11_HAVE_UTF8) {
    130         XRectangle overall_ink, overall_logical;
    131         X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
    132         *pwidth = overall_logical.width;
    133         *pheight = overall_logical.height;
    134     } else {
    135         XCharStruct text_structure;
    136         int font_direction, font_ascent, font_descent;
    137         X11_XTextExtents( data->font_struct, str, nbytes,
    138                       &font_direction, &font_ascent, &font_descent,
    139                       &text_structure );
    140         *pwidth = text_structure.width;
    141         *pheight = text_structure.ascent + text_structure.descent;
    142     }
    143 }
    144 
    145 /* Return index of button if position x,y is contained therein. */
    146 static int
    147 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
    148 {
    149     int i;
    150     int numbuttons = data->numbuttons;
    151     SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
    152 
    153     for ( i = 0; i < numbuttons; i++ ) {
    154         SDL_Rect *rect = &buttonpos[ i ].rect;
    155 
    156         if ( ( x >= rect->x ) &&
    157              ( x <= ( rect->x + rect->w ) ) &&
    158              ( y >= rect->y ) &&
    159              ( y <= ( rect->y + rect->h ) ) ) {
    160             return i;
    161         }
    162     }
    163 
    164     return -1;
    165 }
    166 
    167 /* Initialize SDL_MessageBoxData structure and Display, etc. */
    168 static int
    169 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
    170 {
    171     int i;
    172     int numbuttons = messageboxdata->numbuttons;
    173     const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
    174     const SDL_MessageBoxColor *colorhints;
    175 
    176     if ( numbuttons > MAX_BUTTONS ) {
    177         return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
    178     }
    179 
    180     data->dialog_width = MIN_DIALOG_WIDTH;
    181     data->dialog_height = MIN_DIALOG_HEIGHT;
    182     data->messageboxdata = messageboxdata;
    183     data->buttondata = buttondata;
    184     data->numbuttons = numbuttons;
    185     data->pbuttonid = pbuttonid;
    186 
    187     data->display = X11_XOpenDisplay( NULL );
    188     if ( !data->display ) {
    189         return SDL_SetError("Couldn't open X11 display");
    190     }
    191 
    192     if (SDL_X11_HAVE_UTF8) {
    193         char **missing = NULL;
    194         int num_missing = 0;
    195         data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
    196                                         &missing, &num_missing, NULL);
    197         if ( missing != NULL ) {
    198             X11_XFreeStringList(missing);
    199         }
    200         if ( data->font_set == NULL ) {
    201             return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
    202         }
    203     } else {
    204         data->font_struct = X11_XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
    205         if ( data->font_struct == NULL ) {
    206             return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
    207         }
    208     }
    209 
    210     if ( messageboxdata->colorScheme ) {
    211         colorhints = messageboxdata->colorScheme->colors;
    212     } else {
    213         colorhints = g_default_colors;
    214     }
    215 
    216     /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
    217     for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
    218         data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
    219     }
    220 
    221     return 0;
    222 }
    223 
    224 static int
    225 CountLinesOfText(const char *text)
    226 {
    227     int retval = 0;
    228     while (text && *text) {
    229         const char *lf = SDL_strchr(text, '\n');
    230         retval++;  /* even without an endline, this counts as a line. */
    231         text = lf ? lf + 1 : NULL;
    232     }
    233     return retval;
    234 }
    235 
    236 /* Calculate and initialize text and button locations. */
    237 static int
    238 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
    239 {
    240     int i;
    241     int ybuttons;
    242     int text_width_max = 0;
    243     int button_text_height = 0;
    244     int button_width = MIN_BUTTON_WIDTH;
    245     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
    246 
    247     /* Go over text and break linefeeds into separate lines. */
    248     if ( messageboxdata->message[0] ) {
    249         const char *text = messageboxdata->message;
    250         const int linecount = CountLinesOfText(text);
    251         TextLineData *plinedata = (TextLineData *) SDL_malloc(sizeof (TextLineData) * linecount);
    252 
    253         if (!plinedata) {
    254             return SDL_OutOfMemory();
    255         }
    256 
    257         data->linedata = plinedata;
    258         data->numlines = linecount;
    259 
    260         for ( i = 0; i < linecount; i++, plinedata++ ) {
    261             const char *lf = SDL_strchr( text, '\n' );
    262             const int length = lf ? ( lf - text ) : SDL_strlen( text );
    263             int height;
    264 
    265             plinedata->text = text;
    266 
    267             GetTextWidthHeight( data, text, length, &plinedata->width, &height );
    268 
    269             /* Text and widths are the largest we've ever seen. */
    270             data->text_height = IntMax( data->text_height, height );
    271             text_width_max = IntMax( text_width_max, plinedata->width );
    272 
    273             plinedata->length = length;
    274             if (lf && (lf > text) && (lf[-1] == '\r')) {
    275                 plinedata->length--;
    276             }
    277 
    278             text += length + 1;
    279 
    280             /* Break if there are no more linefeeds. */
    281             if ( !lf )
    282                 break;
    283         }
    284 
    285         /* Bump up the text height slightly. */
    286         data->text_height += 2;
    287     }
    288 
    289     /* Loop through all buttons and calculate the button widths and height. */
    290     for ( i = 0; i < data->numbuttons; i++ ) {
    291         int height;
    292 
    293         data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
    294         data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
    295 
    296         GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
    297                             &data->buttonpos[ i ].text_width, &height );
    298 
    299         button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
    300         button_text_height = IntMax( button_text_height, height );
    301     }
    302 
    303     if ( data->numlines ) {
    304         /* x,y for this line of text. */
    305         data->xtext = data->text_height;
    306         data->ytext = data->text_height + data->text_height;
    307 
    308         /* Bump button y down to bottom of text. */
    309         ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
    310 
    311         /* Bump the dialog box width and height up if needed. */
    312         data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
    313         data->dialog_height = IntMax( data->dialog_height, ybuttons );
    314     } else {
    315         /* Button y starts at height of button text. */
    316         ybuttons = button_text_height;
    317     }
    318 
    319     if ( data->numbuttons ) {
    320         int x, y;
    321         int width_of_buttons;
    322         int button_spacing = button_text_height;
    323         int button_height = 2 * button_text_height;
    324 
    325         /* Bump button width up a bit. */
    326         button_width += button_text_height;
    327 
    328         /* Get width of all buttons lined up. */
    329         width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
    330 
    331         /* Bump up dialog width and height if buttons are wider than text. */
    332         data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
    333         data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
    334 
    335         /* Location for first button. */
    336         if ( messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT ) {
    337 			x = data->dialog_width - ( data->dialog_width - width_of_buttons ) / 2 - ( button_width + button_spacing );
    338 		} else {
    339 			x = ( data->dialog_width - width_of_buttons ) / 2;
    340 		}
    341         y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
    342 
    343         for ( i = 0; i < data->numbuttons; i++ ) {
    344             /* Button coordinates. */
    345             data->buttonpos[ i ].rect.x = x;
    346             data->buttonpos[ i ].rect.y = y;
    347             data->buttonpos[ i ].rect.w = button_width;
    348             data->buttonpos[ i ].rect.h = button_height;
    349 
    350             /* Button text coordinates. */
    351             data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
    352             data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
    353 
    354             /* Scoot over for next button. */
    355 			if ( messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT ) {
    356 				x -= button_width + button_spacing;
    357 			} else {
    358 				x += button_width + button_spacing;
    359 			}
    360         }
    361     }
    362 
    363     return 0;
    364 }
    365 
    366 /* Free SDL_MessageBoxData data. */
    367 static void
    368 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
    369 {
    370     if ( data->font_set != NULL ) {
    371         X11_XFreeFontSet( data->display, data->font_set );
    372         data->font_set = NULL;
    373     }
    374 
    375     if ( data->font_struct != NULL ) {
    376         X11_XFreeFont( data->display, data->font_struct );
    377         data->font_struct = NULL;
    378     }
    379 
    380 #if SDL_VIDEO_DRIVER_X11_XDBE
    381     if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
    382         X11_XdbeDeallocateBackBufferName(data->display, data->buf);
    383     }
    384 #endif
    385 
    386     if ( data->display ) {
    387         if ( data->window != None ) {
    388             X11_XWithdrawWindow( data->display, data->window, data->screen );
    389             X11_XDestroyWindow( data->display, data->window );
    390             data->window = None;
    391         }
    392 
    393         X11_XCloseDisplay( data->display );
    394         data->display = NULL;
    395     }
    396 
    397     SDL_free(data->linedata);
    398 }
    399 
    400 /* Create and set up our X11 dialog box indow. */
    401 static int
    402 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
    403 {
    404     int x, y;
    405     XSizeHints *sizehints;
    406     XSetWindowAttributes wnd_attr;
    407     Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME;
    408     Display *display = data->display;
    409     SDL_WindowData *windowdata = NULL;
    410     const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
    411     char *title_locale = NULL;
    412 
    413     if ( messageboxdata->window ) {
    414         SDL_DisplayData *displaydata =
    415             (SDL_DisplayData *) SDL_GetDisplayForWindow(messageboxdata->window)->driverdata;
    416         windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
    417         data->screen = displaydata->screen;
    418     } else {
    419         data->screen = DefaultScreen( display );
    420     }
    421 
    422     data->event_mask = ExposureMask |
    423                        ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
    424                        StructureNotifyMask | FocusChangeMask | PointerMotionMask;
    425     wnd_attr.event_mask = data->event_mask;
    426 
    427     data->window = X11_XCreateWindow(
    428                        display, RootWindow(display, data->screen),
    429                        0, 0,
    430                        data->dialog_width, data->dialog_height,
    431                        0, CopyFromParent, InputOutput, CopyFromParent,
    432                        CWEventMask, &wnd_attr );
    433     if ( data->window == None ) {
    434         return SDL_SetError("Couldn't create X window");
    435     }
    436 
    437     if ( windowdata ) {
    438         Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False);
    439         Atom stateatoms[16];
    440         size_t statecount = 0;
    441         /* Set some message-boxy window states when attached to a parent window... */
    442         /* we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc */
    443         stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
    444         stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
    445         stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False);
    446         stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False);
    447         SDL_assert(statecount <= SDL_arraysize(stateatoms));
    448         X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32,
    449                             PropModeReplace, (unsigned char *)stateatoms, statecount);
    450 
    451         /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
    452         X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
    453     }
    454 
    455     X11_XStoreName( display, data->window, messageboxdata->title );
    456     _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
    457 
    458     title_locale = SDL_iconv_utf8_locale(messageboxdata->title);
    459     if (title_locale) {
    460         XTextProperty titleprop;
    461         Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
    462         SDL_free(title_locale);
    463         if (status) {
    464             X11_XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
    465             X11_XFree(titleprop.value);
    466         }
    467     }
    468 
    469 #ifdef X_HAVE_UTF8_STRING
    470     if (SDL_X11_HAVE_UTF8) {
    471         XTextProperty titleprop;
    472         Status status = X11_Xutf8TextListToTextProperty(display, (char **) &messageboxdata->title, 1,
    473                                             XUTF8StringStyle, &titleprop);
    474         if (status == Success) {
    475             X11_XSetTextProperty(display, data->window, &titleprop,
    476                                  _NET_WM_NAME);
    477             X11_XFree(titleprop.value);
    478         }
    479     }
    480 #endif
    481 
    482     /* Let the window manager know this is a dialog box */
    483     _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
    484     _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
    485     X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
    486                     PropModeReplace,
    487                     (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
    488 
    489     /* Allow the window to be deleted by the window manager */
    490     data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
    491     data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
    492     X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
    493 
    494     if ( windowdata ) {
    495         XWindowAttributes attrib;
    496         Window dummy;
    497 
    498         X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
    499         x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
    500         y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
    501         X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
    502     } else {
    503         const SDL_VideoDevice *dev = SDL_GetVideoDevice();
    504         if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
    505             const SDL_VideoDisplay *dpy = &dev->displays[0];
    506             const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
    507             x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
    508             y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
    509         } else {   /* oh well. This will misposition on a multi-head setup. Init first next time. */
    510             x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
    511             y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
    512         }
    513     }
    514     X11_XMoveWindow( display, data->window, x, y );
    515 
    516     sizehints = X11_XAllocSizeHints();
    517     if ( sizehints ) {
    518         sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
    519         sizehints->x = x;
    520         sizehints->y = y;
    521         sizehints->width = data->dialog_width;
    522         sizehints->height = data->dialog_height;
    523 
    524         sizehints->min_width = sizehints->max_width = data->dialog_width;
    525         sizehints->min_height = sizehints->max_height = data->dialog_height;
    526 
    527         X11_XSetWMNormalHints( display, data->window, sizehints );
    528 
    529         X11_XFree( sizehints );
    530     }
    531 
    532     X11_XMapRaised( display, data->window );
    533 
    534 #if SDL_VIDEO_DRIVER_X11_XDBE
    535     /* Initialise a back buffer for double buffering */
    536     if (SDL_X11_HAVE_XDBE) {
    537         int xdbe_major, xdbe_minor;
    538         if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
    539             data->xdbe = SDL_TRUE;
    540             data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
    541         } else {
    542             data->xdbe = SDL_FALSE;
    543         }
    544     }
    545 #endif
    546 
    547     return 0;
    548 }
    549 
    550 /* Draw our message box. */
    551 static void
    552 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
    553 {
    554     int i;
    555     Drawable window = data->window;
    556     Display *display = data->display;
    557 
    558 #if SDL_VIDEO_DRIVER_X11_XDBE
    559     if (SDL_X11_HAVE_XDBE && data->xdbe) {
    560         window = data->buf;
    561         X11_XdbeBeginIdiom(data->display);
    562     }
    563 #endif
    564 
    565     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
    566     X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
    567 
    568     X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
    569     for ( i = 0; i < data->numlines; i++ ) {
    570         TextLineData *plinedata = &data->linedata[ i ];
    571 
    572         if (SDL_X11_HAVE_UTF8) {
    573             X11_Xutf8DrawString( display, window, data->font_set, ctx,
    574                              data->xtext, data->ytext + i * data->text_height,
    575                              plinedata->text, plinedata->length );
    576         } else {
    577             X11_XDrawString( display, window, ctx,
    578                          data->xtext, data->ytext + i * data->text_height,
    579                          plinedata->text, plinedata->length );
    580         }
    581     }
    582 
    583     for ( i = 0; i < data->numbuttons; i++ ) {
    584         SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
    585         const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
    586         int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
    587         int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
    588 
    589         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
    590         X11_XFillRectangle( display, window, ctx,
    591                         buttondatax11->rect.x - border, buttondatax11->rect.y - border,
    592                         buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
    593 
    594         X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
    595         X11_XDrawRectangle( display, window, ctx,
    596                         buttondatax11->rect.x, buttondatax11->rect.y,
    597                         buttondatax11->rect.w, buttondatax11->rect.h );
    598 
    599         X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
    600                         data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
    601                         data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
    602 
    603         if (SDL_X11_HAVE_UTF8) {
    604             X11_Xutf8DrawString( display, window, data->font_set, ctx,
    605                              buttondatax11->x + offset,
    606                              buttondatax11->y + offset,
    607                              buttondata->text, buttondatax11->length );
    608         } else {
    609             X11_XDrawString( display, window, ctx,
    610                          buttondatax11->x + offset, buttondatax11->y + offset,
    611                          buttondata->text, buttondatax11->length );
    612         }
    613     }
    614 
    615 #if SDL_VIDEO_DRIVER_X11_XDBE
    616     if (SDL_X11_HAVE_XDBE && data->xdbe) {
    617         XdbeSwapInfo swap_info;
    618         swap_info.swap_window = data->window;
    619         swap_info.swap_action = XdbeUndefined;
    620         X11_XdbeSwapBuffers(data->display, &swap_info, 1);
    621         X11_XdbeEndIdiom(data->display);
    622     }
    623 #endif
    624 }
    625 
    626 static Bool
    627 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
    628 {
    629     const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
    630     return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
    631 }
    632 
    633 /* Loop and handle message box event messages until something kills it. */
    634 static int
    635 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
    636 {
    637     GC ctx;
    638     XGCValues ctx_vals;
    639     SDL_bool close_dialog = SDL_FALSE;
    640     SDL_bool has_focus = SDL_TRUE;
    641     KeySym last_key_pressed = XK_VoidSymbol;
    642     unsigned long gcflags = GCForeground | GCBackground;
    643 
    644     SDL_zero(ctx_vals);
    645     ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
    646     ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
    647 
    648     if (!SDL_X11_HAVE_UTF8) {
    649         gcflags |= GCFont;
    650         ctx_vals.font = data->font_struct->fid;
    651     }
    652 
    653     ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
    654     if ( ctx == None ) {
    655         return SDL_SetError("Couldn't create graphics context");
    656     }
    657 
    658     data->button_press_index = -1;  /* Reset what button is currently depressed. */
    659     data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
    660 
    661     while( !close_dialog ) {
    662         XEvent e;
    663         SDL_bool draw = SDL_TRUE;
    664 
    665         /* can't use XWindowEvent() because it can't handle ClientMessage events. */
    666         /* can't use XNextEvent() because we only want events for this window. */
    667         X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
    668 
    669         /* If X11_XFilterEvent returns True, then some input method has filtered the
    670            event, and the client should discard the event. */
    671         if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
    672             continue;
    673 
    674         switch( e.type ) {
    675         case Expose:
    676             if ( e.xexpose.count > 0 ) {
    677                 draw = SDL_FALSE;
    678             }
    679             break;
    680 
    681         case FocusIn:
    682             /* Got focus. */
    683             has_focus = SDL_TRUE;
    684             break;
    685 
    686         case FocusOut:
    687             /* lost focus. Reset button and mouse info. */
    688             has_focus = SDL_FALSE;
    689             data->button_press_index = -1;
    690             data->mouse_over_index = -1;
    691             break;
    692 
    693         case MotionNotify:
    694             if ( has_focus ) {
    695                 /* Mouse moved... */
    696                 const int previndex = data->mouse_over_index;
    697                 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
    698                 if (data->mouse_over_index == previndex) {
    699                     draw = SDL_FALSE;
    700                 }
    701             }
    702             break;
    703 
    704         case ClientMessage:
    705             if ( e.xclient.message_type == data->wm_protocols &&
    706                  e.xclient.format == 32 &&
    707                  e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
    708                 close_dialog = SDL_TRUE;
    709             }
    710             break;
    711 
    712         case KeyPress:
    713             /* Store key press - we make sure in key release that we got both. */
    714             last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
    715             break;
    716 
    717         case KeyRelease: {
    718             Uint32 mask = 0;
    719             KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
    720 
    721             /* If this is a key release for something we didn't get the key down for, then bail. */
    722             if ( key != last_key_pressed )
    723                 break;
    724 
    725             if ( key == XK_Escape )
    726                 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
    727             else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
    728                 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
    729 
    730             if ( mask ) {
    731                 int i;
    732 
    733                 /* Look for first button with this mask set, and return it if found. */
    734                 for ( i = 0; i < data->numbuttons; i++ ) {
    735                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
    736 
    737                     if ( buttondatax11->buttondata->flags & mask ) {
    738                         *data->pbuttonid = buttondatax11->buttondata->buttonid;
    739                         close_dialog = SDL_TRUE;
    740                         break;
    741                     }
    742                 }
    743             }
    744             break;
    745         }
    746 
    747         case ButtonPress:
    748             data->button_press_index = -1;
    749             if ( e.xbutton.button == Button1 ) {
    750                 /* Find index of button they clicked on. */
    751                 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
    752             }
    753             break;
    754 
    755         case ButtonRelease:
    756             /* If button is released over the same button that was clicked down on, then return it. */
    757             if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
    758                 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
    759 
    760                 if ( data->button_press_index == button ) {
    761                     SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
    762 
    763                     *data->pbuttonid = buttondatax11->buttondata->buttonid;
    764                     close_dialog = SDL_TRUE;
    765                 }
    766             }
    767             data->button_press_index = -1;
    768             break;
    769         }
    770 
    771         if ( draw ) {
    772             /* Draw our dialog box. */
    773             X11_MessageBoxDraw( data, ctx );
    774         }
    775     }
    776 
    777     X11_XFreeGC( data->display, ctx );
    778     return 0;
    779 }
    780 
    781 static int
    782 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
    783 {
    784     int ret;
    785     SDL_MessageBoxDataX11 data;
    786 #if SDL_SET_LOCALE
    787     char *origlocale;
    788 #endif
    789 
    790     SDL_zero(data);
    791 
    792     if ( !SDL_X11_LoadSymbols() )
    793         return -1;
    794 
    795 #if SDL_SET_LOCALE
    796     origlocale = setlocale(LC_ALL, NULL);
    797     if (origlocale != NULL) {
    798         origlocale = SDL_strdup(origlocale);
    799         if (origlocale == NULL) {
    800             return SDL_OutOfMemory();
    801         }
    802         setlocale(LC_ALL, "");
    803     }
    804 #endif
    805 
    806     /* This code could get called from multiple threads maybe? */
    807     X11_XInitThreads();
    808 
    809     /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
    810     *buttonid = -1;
    811 
    812     /* Init and display the message box. */
    813     ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
    814     if ( ret != -1 ) {
    815         ret = X11_MessageBoxInitPositions( &data );
    816         if ( ret != -1 ) {
    817             ret = X11_MessageBoxCreateWindow( &data );
    818             if ( ret != -1 ) {
    819                 ret = X11_MessageBoxLoop( &data );
    820             }
    821         }
    822     }
    823 
    824     X11_MessageBoxShutdown( &data );
    825 
    826 #if SDL_SET_LOCALE
    827     if (origlocale) {
    828         setlocale(LC_ALL, origlocale);
    829         SDL_free(origlocale);
    830     }
    831 #endif
    832 
    833     return ret;
    834 }
    835 
    836 /* Display an x11 message box. */
    837 int
    838 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
    839 {
    840 #if SDL_FORK_MESSAGEBOX
    841     /* Use a child process to protect against setlocale(). Annoying. */
    842     pid_t pid;
    843     int fds[2];
    844     int status = 0;
    845 
    846     if (pipe(fds) == -1) {
    847         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
    848     }
    849 
    850     pid = fork();
    851     if (pid == -1) {  /* failed */
    852         close(fds[0]);
    853         close(fds[1]);
    854         return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
    855     } else if (pid == 0) {  /* we're the child */
    856         int exitcode = 0;
    857         close(fds[0]);
    858         status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
    859         if (write(fds[1], &status, sizeof (int)) != sizeof (int))
    860             exitcode = 1;
    861         else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
    862             exitcode = 1;
    863         close(fds[1]);
    864         _exit(exitcode);  /* don't run atexit() stuff, static destructors, etc. */
    865     } else {  /* we're the parent */
    866         pid_t rc;
    867         close(fds[1]);
    868         do {
    869             rc = waitpid(pid, &status, 0);
    870         } while ((rc == -1) && (errno == EINTR));
    871 
    872         SDL_assert(rc == pid);  /* not sure what to do if this fails. */
    873 
    874         if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
    875             return SDL_SetError("msgbox child process failed");
    876         }
    877 
    878         if (read(fds[0], &status, sizeof (int)) != sizeof (int))
    879             status = -1;
    880         else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
    881             status = -1;
    882         close(fds[0]);
    883 
    884         return status;
    885     }
    886 #else
    887     return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
    888 #endif
    889 }
    890 #endif /* SDL_VIDEO_DRIVER_X11 */
    891 
    892 /* vi: set ts=4 sw=4 expandtab: */