Atlas - SDL_x11messagebox.c

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