1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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 <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 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 */
50
51 static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
52 static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-120-*-*-*-*-*-*";
53
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, */
60 };
61
62 #define SDL_MAKE_RGB( _r, _g, _b ) ( ( ( Uint32 )( _r ) << 16 ) | \
63 ( ( Uint32 )( _g ) << 8 ) | \
64 ( ( Uint32 )( _b ) ) )
65
66 typedef struct SDL_MessageBoxButtonDataX11 {
67 int x, y; /* Text position */
68 int length; /* Text length */
69 int text_width; /* Text width */
70
71 SDL_Rect rect; /* Rectangle for entire button */
72
73 const SDL_MessageBoxButtonData *buttondata; /* Button data from caller */
74 } SDL_MessageBoxButtonDataX11;
75
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 */
80 } TextLineData;
81
82 typedef struct SDL_MessageBoxDataX11
83 {
84 Display *display;
85 int screen;
86 Window window;
87 #if SDL_VIDEO_DRIVER_X11_XDBE
88 XdbeBackBuffer buf;
89 SDL_bool xdbe; /* Whether Xdbe is present or not */
90 #endif
91 long event_mask;
92 Atom wm_protocols;
93 Atom wm_delete_message;
94
95 int dialog_width; /* Dialog box width. */
96 int dialog_height; /* Dialog box height. */
97
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 ];
104
105 int *pbuttonid; /* Pointer to user return buttonid value. */
106
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). */
109
110 int numbuttons; /* Count of buttons. */
111 const SDL_MessageBoxButtonData *buttondata;
112 SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
113
114 Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
115
116 const SDL_MessageBoxData *messageboxdata;
117 } SDL_MessageBoxDataX11;
118
119 /* Maximum helper for ints. */
120 static SDL_INLINE int
IntMax(int a,int b)121 IntMax( int a, int b )
122 {
123 return ( a > b ) ? a : b;
124 }
125
126 /* Return width and height for a string. */
127 static void
GetTextWidthHeight(SDL_MessageBoxDataX11 * data,const char * str,int nbytes,int * pwidth,int * pheight)128 GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
129 {
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;
135 } else {
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,
140 &text_structure );
141 *pwidth = text_structure.width;
142 *pheight = text_structure.ascent + text_structure.descent;
143 }
144 }
145
146 /* Return index of button if position x,y is contained therein. */
147 static int
GetHitButtonIndex(SDL_MessageBoxDataX11 * data,int x,int y)148 GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
149 {
150 int i;
151 int numbuttons = data->numbuttons;
152 SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
153
154 for ( i = 0; i < numbuttons; i++ ) {
155 SDL_Rect *rect = &buttonpos[ i ].rect;
156
157 if ( ( x >= rect->x ) &&
158 ( x <= ( rect->x + rect->w ) ) &&
159 ( y >= rect->y ) &&
160 ( y <= ( rect->y + rect->h ) ) ) {
161 return i;
162 }
163 }
164
165 return -1;
166 }
167
168 /* Initialize SDL_MessageBoxData structure and Display, etc. */
169 static int
X11_MessageBoxInit(SDL_MessageBoxDataX11 * data,const SDL_MessageBoxData * messageboxdata,int * pbuttonid)170 X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
171 {
172 int i;
173 int numbuttons = messageboxdata->numbuttons;
174 const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
175 const SDL_MessageBoxColor *colorhints;
176
177 if ( numbuttons > MAX_BUTTONS ) {
178 return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
179 }
180
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;
187
188 data->display = X11_XOpenDisplay( NULL );
189 if ( !data->display ) {
190 return SDL_SetError("Couldn't open X11 display");
191 }
192
193 if (SDL_X11_HAVE_UTF8) {
194 char **missing = NULL;
195 int num_missing = 0;
196 data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
197 &missing, &num_missing, NULL);
198 if ( missing != NULL ) {
199 X11_XFreeStringList(missing);
200 }
201 if ( data->font_set == NULL ) {
202 return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
203 }
204 } else {
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);
208 }
209 }
210
211 if ( messageboxdata->colorScheme ) {
212 colorhints = messageboxdata->colorScheme->colors;
213 } else {
214 colorhints = g_default_colors;
215 }
216
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 );
220 }
221
222 return 0;
223 }
224
225 /* Calculate and initialize text and button locations. */
226 static int
X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 * data)227 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
228 {
229 int i;
230 int ybuttons;
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;
235
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;
240
241 for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
242 int height;
243 char *lf = SDL_strchr( ( char * )text, '\n' );
244
245 data->numlines++;
246
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;
250
251 GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
252
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 );
256
257 if (lf && (lf > text) && (lf[-1] == '\r')) {
258 plinedata->length--;
259 }
260
261 text += plinedata->length + 1;
262
263 /* Break if there are no more linefeeds. */
264 if ( !lf )
265 break;
266 }
267
268 /* Bump up the text height slightly. */
269 data->text_height += 2;
270 }
271
272 /* Loop through all buttons and calculate the button widths and height. */
273 for ( i = 0; i < data->numbuttons; i++ ) {
274 int height;
275
276 data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
277 data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
278
279 GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
280 &data->buttonpos[ i ].text_width, &height );
281
282 button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
283 button_text_height = IntMax( button_text_height, height );
284 }
285
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;
290
291 /* Bump button y down to bottom of text. */
292 ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
293
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 );
297 } else {
298 /* Button y starts at height of button text. */
299 ybuttons = button_text_height;
300 }
301
302 if ( data->numbuttons ) {
303 int x, y;
304 int width_of_buttons;
305 int button_spacing = button_text_height;
306 int button_height = 2 * button_text_height;
307
308 /* Bump button width up a bit. */
309 button_width += button_text_height;
310
311 /* Get width of all buttons lined up. */
312 width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
313
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 );
317
318 /* Location for first button. */
319 x = ( data->dialog_width - width_of_buttons ) / 2;
320 y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
321
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;
328
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;
332
333 /* Scoot over for next button. */
334 x += button_width + button_spacing;
335 }
336 }
337
338 return 0;
339 }
340
341 /* Free SDL_MessageBoxData data. */
342 static void
X11_MessageBoxShutdown(SDL_MessageBoxDataX11 * data)343 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
344 {
345 if ( data->font_set != NULL ) {
346 X11_XFreeFontSet( data->display, data->font_set );
347 data->font_set = NULL;
348 }
349
350 if ( data->font_struct != NULL ) {
351 X11_XFreeFont( data->display, data->font_struct );
352 data->font_struct = NULL;
353 }
354
355 #if SDL_VIDEO_DRIVER_X11_XDBE
356 if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
357 X11_XdbeDeallocateBackBufferName(data->display, data->buf);
358 }
359 #endif
360
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 );
365 data->window = None;
366 }
367
368 X11_XCloseDisplay( data->display );
369 data->display = NULL;
370 }
371 }
372
373 /* Create and set up our X11 dialog box indow. */
374 static int
X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 * data)375 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
376 {
377 int x, y;
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;
384
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;
390 } else {
391 data->screen = DefaultScreen( display );
392 }
393
394 data->event_mask = ExposureMask |
395 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
396 StructureNotifyMask | FocusChangeMask | PointerMotionMask;
397 wnd_attr.event_mask = data->event_mask;
398
399 data->window = X11_XCreateWindow(
400 display, RootWindow(display, data->screen),
401 0, 0,
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");
407 }
408
409 if ( windowdata ) {
410 /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
411 X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
412 }
413
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 );
420
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,
425 PropModeReplace,
426 (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
427
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 );
432
433 if ( windowdata ) {
434 XWindowAttributes attrib;
435 Window dummy;
436
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);
441 } else {
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 ;
451 }
452 }
453 X11_XMoveWindow( display, data->window, x, y );
454
455 sizehints = X11_XAllocSizeHints();
456 if ( sizehints ) {
457 sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
458 sizehints->x = x;
459 sizehints->y = y;
460 sizehints->width = data->dialog_width;
461 sizehints->height = data->dialog_height;
462
463 sizehints->min_width = sizehints->max_width = data->dialog_width;
464 sizehints->min_height = sizehints->max_height = data->dialog_height;
465
466 X11_XSetWMNormalHints( display, data->window, sizehints );
467
468 X11_XFree( sizehints );
469 }
470
471 X11_XMapRaised( display, data->window );
472
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);
480 } else {
481 data->xdbe = SDL_FALSE;
482 }
483 }
484 #endif
485
486 return 0;
487 }
488
489 /* Draw our message box. */
490 static void
X11_MessageBoxDraw(SDL_MessageBoxDataX11 * data,GC ctx)491 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
492 {
493 int i;
494 Drawable window = data->window;
495 Display *display = data->display;
496
497 #if SDL_VIDEO_DRIVER_X11_XDBE
498 if (SDL_X11_HAVE_XDBE && data->xdbe) {
499 window = data->buf;
500 X11_XdbeBeginIdiom(data->display);
501 }
502 #endif
503
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 );
506
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 ];
510
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 );
515 } else {
516 X11_XDrawString( display, window, ctx,
517 data->xtext, data->ytext + i * data->text_height,
518 plinedata->text, plinedata->length );
519 }
520 }
521
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;
527
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 );
532
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 );
537
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 ] );
541
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 );
547 } else {
548 X11_XDrawString( display, window, ctx,
549 buttondatax11->x + offset, buttondatax11->y + offset,
550 buttondata->text, buttondatax11->length );
551 }
552 }
553
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);
561 }
562 #endif
563 }
564
565 static Bool
X11_MessageBoxEventTest(Display * display,XEvent * event,XPointer arg)566 X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
567 {
568 const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
569 return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
570 }
571
572 /* Loop and handle message box event messages until something kills it. */
573 static int
X11_MessageBoxLoop(SDL_MessageBoxDataX11 * data)574 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
575 {
576 GC ctx;
577 XGCValues ctx_vals;
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;
582
583 SDL_zero(ctx_vals);
584 ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
585 ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
586
587 if (!SDL_X11_HAVE_UTF8) {
588 gcflags |= GCFont;
589 ctx_vals.font = data->font_struct->fid;
590 }
591
592 ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
593 if ( ctx == None ) {
594 return SDL_SetError("Couldn't create graphics context");
595 }
596
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. */
599
600 while( !close_dialog ) {
601 XEvent e;
602 SDL_bool draw = SDL_TRUE;
603
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 );
607
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 ) )
611 continue;
612
613 switch( e.type ) {
614 case Expose:
615 if ( e.xexpose.count > 0 ) {
616 draw = SDL_FALSE;
617 }
618 break;
619
620 case FocusIn:
621 /* Got focus. */
622 has_focus = SDL_TRUE;
623 break;
624
625 case FocusOut:
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;
630 break;
631
632 case MotionNotify:
633 if ( has_focus ) {
634 /* Mouse moved... */
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) {
638 draw = SDL_FALSE;
639 }
640 }
641 break;
642
643 case ClientMessage:
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;
648 }
649 break;
650
651 case KeyPress:
652 /* Store key press - we make sure in key release that we got both. */
653 last_key_pressed = X11_XLookupKeysym( &e.xkey, 0 );
654 break;
655
656 case KeyRelease: {
657 Uint32 mask = 0;
658 KeySym key = X11_XLookupKeysym( &e.xkey, 0 );
659
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 )
662 break;
663
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;
668
669 if ( mask ) {
670 int i;
671
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 ];
675
676 if ( buttondatax11->buttondata->flags & mask ) {
677 *data->pbuttonid = buttondatax11->buttondata->buttonid;
678 close_dialog = SDL_TRUE;
679 break;
680 }
681 }
682 }
683 break;
684 }
685
686 case ButtonPress:
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 );
691 }
692 break;
693
694 case ButtonRelease:
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 );
698
699 if ( data->button_press_index == button ) {
700 SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
701
702 *data->pbuttonid = buttondatax11->buttondata->buttonid;
703 close_dialog = SDL_TRUE;
704 }
705 }
706 data->button_press_index = -1;
707 break;
708 }
709
710 if ( draw ) {
711 /* Draw our dialog box. */
712 X11_MessageBoxDraw( data, ctx );
713 }
714 }
715
716 X11_XFreeGC( data->display, ctx );
717 return 0;
718 }
719
720 static int
X11_ShowMessageBoxImpl(const SDL_MessageBoxData * messageboxdata,int * buttonid)721 X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
722 {
723 int ret;
724 SDL_MessageBoxDataX11 data;
725 #if SDL_SET_LOCALE
726 char *origlocale;
727 #endif
728
729 SDL_zero(data);
730
731 if ( !SDL_X11_LoadSymbols() )
732 return -1;
733
734 #if SDL_SET_LOCALE
735 origlocale = setlocale(LC_ALL, NULL);
736 if (origlocale != NULL) {
737 origlocale = SDL_strdup(origlocale);
738 if (origlocale == NULL) {
739 return SDL_OutOfMemory();
740 }
741 setlocale(LC_ALL, "");
742 }
743 #endif
744
745 /* This code could get called from multiple threads maybe? */
746 X11_XInitThreads();
747
748 /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
749 *buttonid = -1;
750
751 /* Init and display the message box. */
752 ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
753 if ( ret != -1 ) {
754 ret = X11_MessageBoxInitPositions( &data );
755 if ( ret != -1 ) {
756 ret = X11_MessageBoxCreateWindow( &data );
757 if ( ret != -1 ) {
758 ret = X11_MessageBoxLoop( &data );
759 }
760 }
761 }
762
763 X11_MessageBoxShutdown( &data );
764
765 #if SDL_SET_LOCALE
766 if (origlocale) {
767 setlocale(LC_ALL, origlocale);
768 SDL_free(origlocale);
769 }
770 #endif
771
772 return ret;
773 }
774
775 /* Display an x11 message box. */
776 int
X11_ShowMessageBox(const SDL_MessageBoxData * messageboxdata,int * buttonid)777 X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
778 {
779 #if SDL_FORK_MESSAGEBOX
780 /* Use a child process to protect against setlocale(). Annoying. */
781 pid_t pid;
782 int fds[2];
783 int status = 0;
784
785 if (pipe(fds) == -1) {
786 return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
787 }
788
789 pid = fork();
790 if (pid == -1) { /* failed */
791 close(fds[0]);
792 close(fds[1]);
793 return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
794 } else if (pid == 0) { /* we're the child */
795 int exitcode = 0;
796 close(fds[0]);
797 status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
798 if (write(fds[1], &status, sizeof (int)) != sizeof (int))
799 exitcode = 1;
800 else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
801 exitcode = 1;
802 close(fds[1]);
803 _exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
804 } else { /* we're the parent */
805 pid_t rc;
806 close(fds[1]);
807 do {
808 rc = waitpid(pid, &status, 0);
809 } while ((rc == -1) && (errno == EINTR));
810
811 SDL_assert(rc == pid); /* not sure what to do if this fails. */
812
813 if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
814 return SDL_SetError("msgbox child process failed");
815 }
816
817 if (read(fds[0], &status, sizeof (int)) != sizeof (int))
818 status = -1;
819 else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
820 status = -1;
821 close(fds[0]);
822
823 return status;
824 }
825 #else
826 return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
827 #endif
828 }
829 #endif /* SDL_VIDEO_DRIVER_X11 */
830
831 /* vi: set ts=4 sw=4 expandtab: */
832