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