2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #include "../../SDL_internal.h"
24 #if SDL_VIDEO_DRIVER_X11
27 #include "SDL_x11video.h"
28 #include "SDL_x11dyn.h"
29 #include "SDL_assert.h"
31 #include <X11/keysym.h>
35 #define SDL_FORK_MESSAGEBOX 1
36 #define SDL_SET_LOCALE 1
38 #if SDL_FORK_MESSAGEBOX
39 #include <sys/types.h>
45 #define MAX_BUTTONS 8 /* Maximum number of buttons supported */
46 #define MAX_TEXT_LINES 32 /* Maximum number of text lines supported */
47 #define MIN_BUTTON_WIDTH 64 /* Minimum button width */
48 #define MIN_DIALOG_WIDTH 200 /* Minimum dialog width */
49 #define MIN_DIALOG_HEIGHT 100 /* Minimum dialog height */
51 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
52 static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-120-*-*-*-*-*-*";
54 static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = {
55 { 56, 54, 53 }, /* SDL_MESSAGEBOX_COLOR_BACKGROUND, */
56 { 209, 207, 205 }, /* SDL_MESSAGEBOX_COLOR_TEXT, */
57 { 140, 135, 129 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, */
58 { 105, 102, 99 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, */
59 { 205, 202, 53 }, /* SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, */
62 #define SDL_MAKE_RGB( _r, _g, _b ) ( ( ( Uint32 )( _r ) << 16 ) | \
63 ( ( Uint32 )( _g ) << 8 ) | \
64 ( ( Uint32 )( _b ) ) )
66 typedef struct SDL_MessageBoxButtonDataX11 {
67 int x, y; /* Text position */
68 int length; /* Text length */
69 int text_width; /* Text width */
71 SDL_Rect rect; /* Rectangle for entire button */
73 const SDL_MessageBoxButtonData *buttondata; /* Button data from caller */
74 } SDL_MessageBoxButtonDataX11;
76 typedef struct TextLineData {
77 int width; /* Width of this text line */
78 int length; /* String length of this text line */
79 const char *text; /* Text for this line */
82 typedef struct SDL_MessageBoxDataX11
87 #if SDL_VIDEO_DRIVER_X11_XDBE
89 SDL_bool xdbe; /* Whether Xdbe is present or not */
93 Atom wm_delete_message;
95 int dialog_width; /* Dialog box width. */
96 int dialog_height; /* Dialog box height. */
98 XFontSet font_set; /* for UTF-8 systems */
99 XFontStruct *font_struct; /* Latin1 (ASCII) fallback. */
100 int xtext, ytext; /* Text position to start drawing at. */
101 int numlines; /* Count of Text lines. */
102 int text_height; /* Height for text lines. */
103 TextLineData linedata[ MAX_TEXT_LINES ];
105 int *pbuttonid; /* Pointer to user return buttonid value. */
107 int button_press_index; /* Index into buttondata/buttonpos for button which is pressed (or -1). */
108 int mouse_over_index; /* Index into buttondata/buttonpos for button mouse is over (or -1). */
110 int numbuttons; /* Count of buttons. */
111 const SDL_MessageBoxButtonData *buttondata;
112 SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
114 Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
116 const SDL_MessageBoxData *messageboxdata;
117 } SDL_MessageBoxDataX11;
119 /* Maximum helper for ints. */
120 static SDL_INLINE int
121 IntMax( int a, int b )
123 return ( a > b ) ? a : b;
126 /* Return width and height for a string. */
128 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
130 if (SDL_X11_HAVE_UTF8) {
131 XRectangle overall_ink, overall_logical;
132 X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
133 *pwidth = overall_logical.width;
134 *pheight = overall_logical.height;
136 XCharStruct text_structure;
137 int font_direction, font_ascent, font_descent;
138 X11_XTextExtents( data->font_struct, str, nbytes,
139 &font_direction, &font_ascent, &font_descent,
141 *pwidth = text_structure.width;
142 *pheight = text_structure.ascent + text_structure.descent;
146 /* Return index of button if position x,y is contained therein. */
148 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
151 int numbuttons = data->numbuttons;
152 SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
154 for ( i = 0; i < numbuttons; i++ ) {
155 SDL_Rect *rect = &buttonpos[ i ].rect;
157 if ( ( x >= rect->x ) &&
158 ( x <= ( rect->x + rect->w ) ) &&
160 ( y <= ( rect->y + rect->h ) ) ) {
168 /* Initialize SDL_MessageBoxData structure and Display, etc. */
170 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
173 int numbuttons = messageboxdata->numbuttons;
174 const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
175 const SDL_MessageBoxColor *colorhints;
177 if ( numbuttons > MAX_BUTTONS ) {
178 return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
181 data->dialog_width = MIN_DIALOG_WIDTH;
182 data->dialog_height = MIN_DIALOG_HEIGHT;
183 data->messageboxdata = messageboxdata;
184 data->buttondata = buttondata;
185 data->numbuttons = numbuttons;
186 data->pbuttonid = pbuttonid;
188 data->display = X11_XOpenDisplay( NULL );
189 if ( !data->display ) {
190 return SDL_SetError("Couldn't open X11 display");
193 if (SDL_X11_HAVE_UTF8) {
194 char **missing = NULL;
196 data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
197 &missing, &num_missing, NULL);
198 if ( missing != NULL ) {
199 X11_XFreeStringList(missing);
201 if ( data->font_set == NULL ) {
202 return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
205 data->font_struct = X11_XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
206 if ( data->font_struct == NULL ) {
207 return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
211 if ( messageboxdata->colorScheme ) {
212 colorhints = messageboxdata->colorScheme->colors;
214 colorhints = g_default_colors;
217 /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
218 for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
219 data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
225 /* Calculate and initialize text and button locations. */
227 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
231 int text_width_max = 0;
232 int button_text_height = 0;
233 int button_width = MIN_BUTTON_WIDTH;
234 const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
236 /* Go over text and break linefeeds into separate lines. */
237 if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
238 const char *text = messageboxdata->message;
239 TextLineData *plinedata = data->linedata;
241 for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
243 char *lf = SDL_strchr( ( char * )text, '\n' );
247 /* Only grab length up to lf if it exists and isn't the last line. */
248 plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
249 plinedata->text = text;
251 GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
253 /* Text and widths are the largest we've ever seen. */
254 data->text_height = IntMax( data->text_height, height );
255 text_width_max = IntMax( text_width_max, plinedata->width );
257 if (lf && (lf > text) && (lf[-1] == '\r')) {
261 text += plinedata->length + 1;
263 /* Break if there are no more linefeeds. */
268 /* Bump up the text height slightly. */
269 data->text_height += 2;
272 /* Loop through all buttons and calculate the button widths and height. */
273 for ( i = 0; i < data->numbuttons; i++ ) {
276 data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
277 data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
279 GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
280 &data->buttonpos[ i ].text_width, &height );
282 button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
283 button_text_height = IntMax( button_text_height, height );
286 if ( data->numlines ) {
287 /* x,y for this line of text. */
288 data->xtext = data->text_height;
289 data->ytext = data->text_height + data->text_height;
291 /* Bump button y down to bottom of text. */
292 ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
294 /* Bump the dialog box width and height up if needed. */
295 data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
296 data->dialog_height = IntMax( data->dialog_height, ybuttons );
298 /* Button y starts at height of button text. */
299 ybuttons = button_text_height;
302 if ( data->numbuttons ) {
304 int width_of_buttons;
305 int button_spacing = button_text_height;
306 int button_height = 2 * button_text_height;
308 /* Bump button width up a bit. */
309 button_width += button_text_height;
311 /* Get width of all buttons lined up. */
312 width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
314 /* Bump up dialog width and height if buttons are wider than text. */
315 data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
316 data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
318 /* Location for first button. */
319 x = ( data->dialog_width - width_of_buttons ) / 2;
320 y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
322 for ( i = 0; i < data->numbuttons; i++ ) {
323 /* Button coordinates. */
324 data->buttonpos[ i ].rect.x = x;
325 data->buttonpos[ i ].rect.y = y;
326 data->buttonpos[ i ].rect.w = button_width;
327 data->buttonpos[ i ].rect.h = button_height;
329 /* Button text coordinates. */
330 data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
331 data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
333 /* Scoot over for next button. */
334 x += button_width + button_spacing;
341 /* Free SDL_MessageBoxData data. */
343 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
345 if ( data->font_set != NULL ) {
346 X11_XFreeFontSet( data->display, data->font_set );
347 data->font_set = NULL;
350 if ( data->font_struct != NULL ) {
351 X11_XFreeFont( data->display, data->font_struct );
352 data->font_struct = NULL;
355 #if SDL_VIDEO_DRIVER_X11_XDBE
356 if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
357 X11_XdbeDeallocateBackBufferName(data->display, data->buf);
361 if ( data->display ) {
362 if ( data->window != None ) {
363 X11_XWithdrawWindow( data->display, data->window, data->screen );
364 X11_XDestroyWindow( data->display, data->window );
368 X11_XCloseDisplay( data->display );
369 data->display = NULL;
373 /* Create and set up our X11 dialog box indow. */
375 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
378 XSizeHints *sizehints;
379 XSetWindowAttributes wnd_attr;
380 Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME, UTF8_STRING;
381 Display *display = data->display;
382 SDL_WindowData *windowdata = NULL;
383 const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
385 if ( messageboxdata->window ) {
386 SDL_DisplayData *displaydata =
387 (SDL_DisplayData *) SDL_GetDisplayForWindow(messageboxdata->window)->driverdata;
388 windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
389 data->screen = displaydata->screen;
391 data->screen = DefaultScreen( display );
394 data->event_mask = ExposureMask |
395 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
396 StructureNotifyMask | FocusChangeMask | PointerMotionMask;
397 wnd_attr.event_mask = data->event_mask;
399 data->window = X11_XCreateWindow(
400 display, RootWindow(display, data->screen),
402 data->dialog_width, data->dialog_height,
403 0, CopyFromParent, InputOutput, CopyFromParent,
404 CWEventMask, &wnd_attr );
405 if ( data->window == None ) {
406 return SDL_SetError("Couldn't create X window");
410 /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
411 X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
414 X11_XStoreName( display, data->window, messageboxdata->title );
415 _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
416 UTF8_STRING = X11_XInternAtom(display, "UTF8_STRING", False);
417 X11_XChangeProperty(display, data->window, _NET_WM_NAME, UTF8_STRING, 8,
418 PropModeReplace, (unsigned char *) messageboxdata->title,
419 strlen(messageboxdata->title) + 1 );
421 /* Let the window manager know this is a dialog box */
422 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
423 _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
424 X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
426 (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
428 /* Allow the window to be deleted by the window manager */
429 data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
430 data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
431 X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
434 XWindowAttributes attrib;
437 X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
438 x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
439 y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
440 X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
442 const SDL_VideoDevice *dev = SDL_GetVideoDevice();
443 if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
444 const SDL_VideoDisplay *dpy = &dev->displays[0];
445 const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
446 x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
447 y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
448 } else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
449 x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
450 y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
453 X11_XMoveWindow( display, data->window, x, y );
455 sizehints = X11_XAllocSizeHints();
457 sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
460 sizehints->width = data->dialog_width;
461 sizehints->height = data->dialog_height;
463 sizehints->min_width = sizehints->max_width = data->dialog_width;
464 sizehints->min_height = sizehints->max_height = data->dialog_height;
466 X11_XSetWMNormalHints( display, data->window, sizehints );
468 X11_XFree( sizehints );
471 X11_XMapRaised( display, data->window );
473 #if SDL_VIDEO_DRIVER_X11_XDBE
474 /* Initialise a back buffer for double buffering */
475 if (SDL_X11_HAVE_XDBE) {
476 int xdbe_major, xdbe_minor;
477 if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
478 data->xdbe = SDL_TRUE;
479 data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
481 data->xdbe = SDL_FALSE;
489 /* Draw our message box. */
491 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
494 Drawable window = data->window;
495 Display *display = data->display;
497 #if SDL_VIDEO_DRIVER_X11_XDBE
498 if (SDL_X11_HAVE_XDBE && data->xdbe) {
500 X11_XdbeBeginIdiom(data->display);
504 X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
505 X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
507 X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
508 for ( i = 0; i < data->numlines; i++ ) {
509 TextLineData *plinedata = &data->linedata[ i ];
511 if (SDL_X11_HAVE_UTF8) {
512 X11_Xutf8DrawString( display, window, data->font_set, ctx,
513 data->xtext, data->ytext + i * data->text_height,
514 plinedata->text, plinedata->length );
516 X11_XDrawString( display, window, ctx,
517 data->xtext, data->ytext + i * data->text_height,
518 plinedata->text, plinedata->length );
522 for ( i = 0; i < data->numbuttons; i++ ) {
523 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
524 const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
525 int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
526 int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
528 X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
529 X11_XFillRectangle( display, window, ctx,
530 buttondatax11->rect.x - border, buttondatax11->rect.y - border,
531 buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
533 X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
534 X11_XDrawRectangle( display, window, ctx,
535 buttondatax11->rect.x, buttondatax11->rect.y,
536 buttondatax11->rect.w, buttondatax11->rect.h );
538 X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
539 data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
540 data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
542 if (SDL_X11_HAVE_UTF8) {
543 X11_Xutf8DrawString( display, window, data->font_set, ctx,
544 buttondatax11->x + offset,
545 buttondatax11->y + offset,
546 buttondata->text, buttondatax11->length );
548 X11_XDrawString( display, window, ctx,
549 buttondatax11->x + offset, buttondatax11->y + offset,
550 buttondata->text, buttondatax11->length );
554 #if SDL_VIDEO_DRIVER_X11_XDBE
555 if (SDL_X11_HAVE_XDBE && data->xdbe) {
556 XdbeSwapInfo swap_info;
557 swap_info.swap_window = data->window;
558 swap_info.swap_action = XdbeUndefined;
559 X11_XdbeSwapBuffers(data->display, &swap_info, 1);
560 X11_XdbeEndIdiom(data->display);
566 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
568 const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
569 return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
572 /* Loop and handle message box event messages until something kills it. */
574 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
578 SDL_bool close_dialog = SDL_FALSE;
579 SDL_bool has_focus = SDL_TRUE;
580 KeySym last_key_pressed = XK_VoidSymbol;
581 unsigned long gcflags = GCForeground | GCBackground;
584 ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
585 ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
587 if (!SDL_X11_HAVE_UTF8) {
589 ctx_vals.font = data->font_struct->fid;
592 ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
594 return SDL_SetError("Couldn't create graphics context");
597 data->button_press_index = -1; /* Reset what button is currently depressed. */
598 data->mouse_over_index = -1; /* Reset what button the mouse is over. */
600 while( !close_dialog ) {
602 SDL_bool draw = SDL_TRUE;
604 /* can't use XWindowEvent() because it can't handle ClientMessage events. */
605 /* can't use XNextEvent() because we only want events for this window. */
606 X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
608 /* If X11_XFilterEvent returns True, then some input method has filtered the
609 event, and the client should discard the event. */
610 if ( ( e.type != Expose ) && X11_XFilterEvent( &e, None ) )
615 if ( e.xexpose.count > 0 ) {
622 has_focus = SDL_TRUE;
626 /* lost focus. Reset button and mouse info. */
627 has_focus = SDL_FALSE;
628 data->button_press_index = -1;
629 data->mouse_over_index = -1;
635 const int previndex = data->mouse_over_index;
636 data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
637 if (data->mouse_over_index == previndex) {
644 if ( e.xclient.message_type == data->wm_protocols &&
645 e.xclient.format == 32 &&
646 e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
647 close_dialog = SDL_TRUE;
652 /* Store key press - we make sure in key release that we got both. */
653 last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
658 KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
660 /* If this is a key release for something we didn't get the key down for, then bail. */
661 if ( key != last_key_pressed )
664 if ( key == XK_Escape )
665 mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
666 else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
667 mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
672 /* Look for first button with this mask set, and return it if found. */
673 for ( i = 0; i < data->numbuttons; i++ ) {
674 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
676 if ( buttondatax11->buttondata->flags & mask ) {
677 *data->pbuttonid = buttondatax11->buttondata->buttonid;
678 close_dialog = SDL_TRUE;
687 data->button_press_index = -1;
688 if ( e.xbutton.button == Button1 ) {
689 /* Find index of button they clicked on. */
690 data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
695 /* If button is released over the same button that was clicked down on, then return it. */
696 if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
697 int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
699 if ( data->button_press_index == button ) {
700 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
702 *data->pbuttonid = buttondatax11->buttondata->buttonid;
703 close_dialog = SDL_TRUE;
706 data->button_press_index = -1;
711 /* Draw our dialog box. */
712 X11_MessageBoxDraw( data, ctx );
716 X11_XFreeGC( data->display, ctx );
721 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
724 SDL_MessageBoxDataX11 data;
731 if ( !SDL_X11_LoadSymbols() )
735 origlocale = setlocale(LC_ALL, NULL);
736 if (origlocale != NULL) {
737 origlocale = SDL_strdup(origlocale);
738 if (origlocale == NULL) {
739 return SDL_OutOfMemory();
741 setlocale(LC_ALL, "");
745 /* This code could get called from multiple threads maybe? */
748 /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
751 /* Init and display the message box. */
752 ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
754 ret = X11_MessageBoxInitPositions( &data );
756 ret = X11_MessageBoxCreateWindow( &data );
758 ret = X11_MessageBoxLoop( &data );
763 X11_MessageBoxShutdown( &data );
767 setlocale(LC_ALL, origlocale);
768 SDL_free(origlocale);
775 /* Display an x11 message box. */
777 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
779 #if SDL_FORK_MESSAGEBOX
780 /* Use a child process to protect against setlocale(). Annoying. */
785 if (pipe(fds) == -1) {
786 return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
790 if (pid == -1) { /* failed */
793 return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
794 } else if (pid == 0) { /* we're the child */
797 status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
798 if (write(fds[1], &status, sizeof (int)) != sizeof (int))
800 else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
803 _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
804 } else { /* we're the parent */
808 rc = waitpid(pid, &status, 0);
809 } while ((rc == -1) && (errno == EINTR));
811 SDL_assert(rc == pid); /* not sure what to do if this fails. */
813 if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
814 return SDL_SetError("msgbox child process failed");
817 if (read(fds[0], &status, sizeof (int)) != sizeof (int))
819 else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
826 return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
829 #endif /* SDL_VIDEO_DRIVER_X11 */
831 /* vi: set ts=4 sw=4 expandtab: */