22 #include "../../SDL_internal.h"
24 #if SDL_VIDEO_DRIVER_X11
32 #include <X11/keysym.h>
36 #define SDL_FORK_MESSAGEBOX 1
37 #define SDL_SET_LOCALE 1
39 #if SDL_FORK_MESSAGEBOX
40 #include <sys/types.h>
47 #define MIN_BUTTON_WIDTH 64
48 #define MIN_DIALOG_WIDTH 200
49 #define MIN_DIALOG_HEIGHT 100
51 static const char g_MessageBoxFontLatin1[] =
"-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
52 static const char g_MessageBoxFont[] =
"-*-*-medium-r-normal--*-120-*-*-*-*-*-*";
62 #define SDL_MAKE_RGB( _r, _g, _b ) ( ( ( Uint32 )( _r ) << 16 ) | \
63 ( ( Uint32 )( _g ) << 8 ) | \
64 ( ( Uint32 )( _b ) ) )
66 typedef struct SDL_MessageBoxButtonDataX11 {
74 } SDL_MessageBoxButtonDataX11;
76 typedef struct TextLineData {
82 typedef struct SDL_MessageBoxDataX11
87 #if SDL_VIDEO_DRIVER_X11_XDBE
93 Atom wm_delete_message;
99 XFontStruct *font_struct;
103 TextLineData *linedata;
107 int button_press_index;
108 int mouse_over_index;
112 SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
117 } SDL_MessageBoxDataX11;
121 IntMax(
int a,
int b )
123 return (
a >
b ) ?
a :
b;
128 GetTextWidthHeight( SDL_MessageBoxDataX11 *
data,
const char *str,
int nbytes,
int *pwidth,
int *pheight )
130 if (SDL_X11_HAVE_UTF8) {
131 XRectangle overall_ink, overall_logical;
132 X11_Xutf8TextExtents(
data->font_set, str, nbytes, &overall_ink, &overall_logical);
133 *pwidth = overall_logical.width;
134 *pheight = overall_logical.height;
136 XCharStruct text_structure;
137 int font_direction, font_ascent, font_descent;
138 X11_XTextExtents(
data->font_struct, str, nbytes,
139 &font_direction, &font_ascent, &font_descent,
141 *pwidth = text_structure.width;
142 *pheight = text_structure.ascent + text_structure.descent;
148 GetHitButtonIndex( SDL_MessageBoxDataX11 *
data,
int x,
int y )
151 int numbuttons =
data->numbuttons;
152 SDL_MessageBoxButtonDataX11 *buttonpos =
data->buttonpos;
154 for (
i = 0;
i < numbuttons;
i++ ) {
170 X11_MessageBoxInit( SDL_MessageBoxDataX11 *
data,
const SDL_MessageBoxData * messageboxdata,
int * pbuttonid )
177 if ( numbuttons > MAX_BUTTONS ) {
178 return SDL_SetError(
"Too many buttons (%d max allowed)", MAX_BUTTONS);
181 data->dialog_width = MIN_DIALOG_WIDTH;
182 data->dialog_height = MIN_DIALOG_HEIGHT;
183 data->messageboxdata = messageboxdata;
184 data->buttondata = buttondata;
185 data->numbuttons = numbuttons;
186 data->pbuttonid = pbuttonid;
188 data->display = X11_XOpenDisplay(
NULL );
189 if ( !
data->display ) {
193 if (SDL_X11_HAVE_UTF8) {
194 char **missing =
NULL;
196 data->font_set = X11_XCreateFontSet(
data->display, g_MessageBoxFont,
197 &missing, &num_missing,
NULL);
198 if ( missing !=
NULL ) {
199 X11_XFreeStringList(missing);
202 return SDL_SetError(
"Couldn't load font %s", g_MessageBoxFont);
205 data->font_struct = X11_XLoadQueryFont(
data->display, g_MessageBoxFontLatin1 );
207 return SDL_SetError(
"Couldn't load font %s", g_MessageBoxFontLatin1);
214 colorhints = g_default_colors;
219 data->color[
i ] = SDL_MAKE_RGB( colorhints[
i ].
r, colorhints[
i ].
g, colorhints[
i ].
b );
226 CountLinesOfText(
const char *
text)
239 X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *
data )
243 int text_width_max = 0;
244 int button_text_height = 0;
245 int button_width = MIN_BUTTON_WIDTH;
251 const int linecount = CountLinesOfText(
text);
252 TextLineData *plinedata = (TextLineData *)
SDL_malloc(
sizeof (TextLineData) * linecount);
258 data->linedata = plinedata;
259 data->numlines = linecount;
261 for (
i = 0;
i < linecount;
i++, plinedata++ ) {
266 plinedata->text =
text;
272 text_width_max = IntMax( text_width_max, plinedata->width );
274 plinedata->length =
length;
275 if (lf && (lf >
text) && (lf[-1] ==
'\r')) {
287 data->text_height += 2;
291 for (
i = 0;
i <
data->numbuttons;
i++ ) {
294 data->buttonpos[
i ].buttondata = &
data->buttondata[
i ];
300 button_width = IntMax( button_width,
data->buttonpos[
i ].text_width );
301 button_text_height = IntMax( button_text_height,
height );
304 if (
data->numlines ) {
310 ybuttons = 3 *
data->ytext / 2 + (
data->numlines - 1 ) *
data->text_height;
313 data->dialog_width = IntMax(
data->dialog_width, 2 *
data->xtext + text_width_max );
314 data->dialog_height = IntMax(
data->dialog_height, ybuttons );
317 ybuttons = button_text_height;
320 if (
data->numbuttons ) {
322 int width_of_buttons;
323 int button_spacing = button_text_height;
324 int button_height = 2 * button_text_height;
327 button_width += button_text_height;
330 width_of_buttons =
data->numbuttons * button_width + (
data->numbuttons - 1 ) * button_spacing;
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 );
337 x = (
data->dialog_width - width_of_buttons ) / 2;
338 y = ybuttons + (
data->dialog_height - ybuttons - button_height ) / 2;
340 for (
i = 0;
i <
data->numbuttons;
i++ ) {
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;
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;
352 x += button_width + button_spacing;
361 X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *
data )
364 X11_XFreeFontSet(
data->display,
data->font_set );
369 X11_XFreeFont(
data->display,
data->font_struct );
373 #if SDL_VIDEO_DRIVER_X11_XDBE
374 if ( SDL_X11_HAVE_XDBE &&
data->xdbe ) {
375 X11_XdbeDeallocateBackBufferName(
data->display,
data->buf);
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 );
386 X11_XCloseDisplay(
data->display );
395 X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *
data )
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;
404 char *title_locale =
NULL;
406 if ( messageboxdata->
window ) {
412 data->screen = DefaultScreen( display );
415 data->event_mask = ExposureMask |
416 ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
417 StructureNotifyMask | FocusChangeMask | PointerMotionMask;
418 wnd_attr.event_mask =
data->event_mask;
420 data->window = X11_XCreateWindow(
421 display, RootWindow(display,
data->screen),
423 data->dialog_width,
data->dialog_height,
424 0, CopyFromParent, InputOutput, CopyFromParent,
425 CWEventMask, &wnd_attr );
426 if (
data->window == None ) {
432 X11_XSetTransientForHint( display,
data->window, windowdata->
xwindow );
435 X11_XStoreName( display,
data->window, messageboxdata->
title );
436 _NET_WM_NAME = X11_XInternAtom(display,
"_NET_WM_NAME", False);
440 XTextProperty titleprop;
441 Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
444 X11_XSetTextProperty(display,
data->window, &titleprop, XA_WM_NAME);
445 X11_XFree(titleprop.value);
449 #ifdef X_HAVE_UTF8_STRING
450 if (SDL_X11_HAVE_UTF8) {
451 XTextProperty titleprop;
452 Status status = X11_Xutf8TextListToTextProperty(display, (
char **) &messageboxdata->
title, 1,
453 XUTF8StringStyle, &titleprop);
454 if (status == Success) {
455 X11_XSetTextProperty(display,
data->window, &titleprop,
457 X11_XFree(titleprop.value);
463 _NET_WM_WINDOW_TYPE = X11_XInternAtom(display,
"_NET_WM_WINDOW_TYPE", False);
464 _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display,
"_NET_WM_WINDOW_TYPE_DIALOG", False);
465 X11_XChangeProperty(display,
data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
467 (
unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
470 data->wm_protocols = X11_XInternAtom( display,
"WM_PROTOCOLS", False );
471 data->wm_delete_message = X11_XInternAtom( display,
"WM_DELETE_WINDOW", False );
472 X11_XSetWMProtocols( display,
data->window, &
data->wm_delete_message, 1 );
475 XWindowAttributes attrib;
478 X11_XGetWindowAttributes(display, windowdata->
xwindow, &attrib);
479 x = attrib.x + ( attrib.width -
data->dialog_width ) / 2;
480 y = attrib.y + ( attrib.height -
data->dialog_height ) / 3 ;
481 X11_XTranslateCoordinates(display, windowdata->
xwindow, RootWindow(display,
data->screen),
x,
y, &
x, &
y, &dummy);
487 x = dpydata->
x + ((
dpy->current_mode.w -
data->dialog_width ) / 2);
488 y = dpydata->
y + ((
dpy->current_mode.h -
data->dialog_height ) / 3);
490 x = ( DisplayWidth( display,
data->screen ) -
data->dialog_width ) / 2;
491 y = ( DisplayHeight( display,
data->screen ) -
data->dialog_height ) / 3 ;
494 X11_XMoveWindow( display,
data->window,
x,
y );
496 sizehints = X11_XAllocSizeHints();
498 sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
501 sizehints->width =
data->dialog_width;
502 sizehints->height =
data->dialog_height;
504 sizehints->min_width = sizehints->max_width =
data->dialog_width;
505 sizehints->min_height = sizehints->max_height =
data->dialog_height;
507 X11_XSetWMNormalHints( display,
data->window, sizehints );
509 X11_XFree( sizehints );
512 X11_XMapRaised( display,
data->window );
514 #if SDL_VIDEO_DRIVER_X11_XDBE
516 if (SDL_X11_HAVE_XDBE) {
517 int xdbe_major, xdbe_minor;
518 if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
520 data->buf = X11_XdbeAllocateBackBufferName(display,
data->window, XdbeUndefined);
532 X11_MessageBoxDraw( SDL_MessageBoxDataX11 *
data, GC
ctx )
536 Display *display =
data->display;
538 #if SDL_VIDEO_DRIVER_X11_XDBE
539 if (SDL_X11_HAVE_XDBE &&
data->xdbe) {
541 X11_XdbeBeginIdiom(
data->display);
546 X11_XFillRectangle( display,
window,
ctx, 0, 0,
data->dialog_width,
data->dialog_height );
549 for (
i = 0;
i <
data->numlines;
i++ ) {
550 TextLineData *plinedata = &
data->linedata[
i ];
552 if (SDL_X11_HAVE_UTF8) {
555 plinedata->text, plinedata->length );
559 plinedata->text, plinedata->length );
563 for (
i = 0;
i <
data->numbuttons;
i++ ) {
564 SDL_MessageBoxButtonDataX11 *buttondatax11 = &
data->buttonpos[
i ];
567 int offset = ( (
data->mouse_over_index ==
i ) && (
data->button_press_index ==
data->mouse_over_index ) ) ? 1 : 0;
570 X11_XFillRectangle( display,
window,
ctx,
571 buttondatax11->rect.x -
border, buttondatax11->rect.y -
border,
572 buttondatax11->rect.w + 2 *
border, buttondatax11->rect.h + 2 *
border );
575 X11_XDrawRectangle( display,
window,
ctx,
576 buttondatax11->rect.x, buttondatax11->rect.y,
577 buttondatax11->rect.w, buttondatax11->rect.h );
579 X11_XSetForeground( display,
ctx, (
data->mouse_over_index ==
i ) ?
583 if (SDL_X11_HAVE_UTF8) {
585 buttondatax11->x +
offset,
586 buttondatax11->y +
offset,
587 buttondata->
text, buttondatax11->length );
591 buttondata->
text, buttondatax11->length );
595 #if SDL_VIDEO_DRIVER_X11_XDBE
596 if (SDL_X11_HAVE_XDBE &&
data->xdbe) {
597 XdbeSwapInfo swap_info;
598 swap_info.swap_window =
data->window;
599 swap_info.swap_action = XdbeUndefined;
600 X11_XdbeSwapBuffers(
data->display, &swap_info, 1);
601 X11_XdbeEndIdiom(
data->display);
607 X11_MessageBoxEventTest(Display *display, XEvent *
event, XPointer arg)
609 const SDL_MessageBoxDataX11 *
data = (
const SDL_MessageBoxDataX11 *) arg;
610 return ((
event->xany.display ==
data->display) && (
event->xany.window ==
data->window)) ? True : False;
615 X11_MessageBoxLoop( SDL_MessageBoxDataX11 *
data )
621 KeySym last_key_pressed = XK_VoidSymbol;
622 unsigned long gcflags = GCForeground | GCBackground;
628 if (!SDL_X11_HAVE_UTF8) {
630 ctx_vals.font =
data->font_struct->fid;
633 ctx = X11_XCreateGC(
data->display,
data->window, gcflags, &ctx_vals );
635 return SDL_SetError(
"Couldn't create graphics context");
638 data->button_press_index = -1;
639 data->mouse_over_index = -1;
641 while( !close_dialog ) {
647 X11_XIfEvent(
data->display, &
e, X11_MessageBoxEventTest, (XPointer)
data );
651 if ( (
e.type != Expose ) && X11_XFilterEvent( &
e, None ) )
656 if (
e.xexpose.count > 0 ) {
669 data->button_press_index = -1;
670 data->mouse_over_index = -1;
676 const int previndex =
data->mouse_over_index;
677 data->mouse_over_index = GetHitButtonIndex(
data,
e.xbutton.x,
e.xbutton.y );
678 if (
data->mouse_over_index == previndex) {
685 if (
e.xclient.message_type ==
data->wm_protocols &&
686 e.xclient.format == 32 &&
687 e.xclient.data.l[ 0 ] ==
data->wm_delete_message ) {
694 last_key_pressed = X11_XLookupKeysym( &
e.xkey, 0 );
699 KeySym
key = X11_XLookupKeysym( &
e.xkey, 0 );
702 if (
key != last_key_pressed )
705 if (
key == XK_Escape )
707 else if ( (
key == XK_Return ) || (
key == XK_KP_Enter ) )
714 for (
i = 0;
i <
data->numbuttons;
i++ ) {
715 SDL_MessageBoxButtonDataX11 *buttondatax11 = &
data->buttonpos[
i ];
717 if ( buttondatax11->buttondata->flags &
mask ) {
718 *
data->pbuttonid = buttondatax11->buttondata->buttonid;
728 data->button_press_index = -1;
729 if (
e.xbutton.button == Button1 ) {
731 data->button_press_index = GetHitButtonIndex(
data,
e.xbutton.x,
e.xbutton.y );
737 if ( (
e.xbutton.button == Button1 ) && (
data->button_press_index >= 0 ) ) {
738 int button = GetHitButtonIndex(
data,
e.xbutton.x,
e.xbutton.y );
741 SDL_MessageBoxButtonDataX11 *buttondatax11 = &
data->buttonpos[
button ];
743 *
data->pbuttonid = buttondatax11->buttondata->buttonid;
747 data->button_press_index = -1;
753 X11_MessageBoxDraw(
data,
ctx );
757 X11_XFreeGC(
data->display,
ctx );
765 SDL_MessageBoxDataX11
data;
776 origlocale = setlocale(LC_ALL,
NULL);
777 if (origlocale !=
NULL) {
779 if (origlocale ==
NULL) {
782 setlocale(LC_ALL,
"");
793 ret = X11_MessageBoxInit( &
data, messageboxdata, buttonid );
795 ret = X11_MessageBoxInitPositions( &
data );
797 ret = X11_MessageBoxCreateWindow( &
data );
799 ret = X11_MessageBoxLoop( &
data );
804 X11_MessageBoxShutdown( &
data );
808 setlocale(LC_ALL, origlocale);
820 #if SDL_FORK_MESSAGEBOX
826 if (pipe(
fds) == -1) {
827 return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
834 return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
835 }
else if (pid == 0) {
838 status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
839 if (write(
fds[1], &status,
sizeof (
int)) !=
sizeof (
int))
841 else if (write(
fds[1], buttonid,
sizeof (
int)) !=
sizeof (
int))
849 rc = waitpid(pid, &status, 0);
850 }
while ((rc == -1) && (errno == EINTR));
854 if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
858 if (read(
fds[0], &status,
sizeof (
int)) !=
sizeof (
int))
860 else if (read(
fds[0], buttonid,
sizeof (
int)) !=
sizeof (
int))
867 return X11_ShowMessageBoxImpl(messageboxdata, buttonid);