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: */