From 642cdee8658165f5ec469cbbf5083ada76b610a3 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 23 Oct 2012 17:11:22 -0700 Subject: [PATCH] Added API for simple messagebox, courtesy of Mike Sartain --- include/SDL.h | 1 + include/SDL_messagebox.h | 140 +++++++ src/video/SDL_sysvideo.h | 4 + src/video/SDL_video.c | 51 +++ src/video/x11/SDL_x11messagebox.c | 642 ++++++++++++++++++++++++++++++ src/video/x11/SDL_x11sym.h | 12 + test/common.c | 6 + 7 files changed, 856 insertions(+) create mode 100644 include/SDL_messagebox.h create mode 100644 src/video/x11/SDL_x11messagebox.c diff --git a/include/SDL.h b/include/SDL.h index aabbabaa3..186ab8a18 100644 --- a/include/SDL.h +++ b/include/SDL.h @@ -82,6 +82,7 @@ #include "SDL_hints.h" #include "SDL_loadso.h" #include "SDL_log.h" +#include "SDL_messagebox.h" #include "SDL_mutex.h" #include "SDL_power.h" #include "SDL_render.h" diff --git a/include/SDL_messagebox.h b/include/SDL_messagebox.h new file mode 100644 index 000000000..fd61189d2 --- /dev/null +++ b/include/SDL_messagebox.h @@ -0,0 +1,140 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _SDL_messagebox_h +#define _SDL_messagebox_h + +#include "SDL_stdinc.h" +#include "SDL_video.h" /* For SDL_Window */ + +#include "begin_code.h" +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief SDL_MessageBox flags. If supported will display warning icon, etc. + */ +typedef enum +{ + SDL_MESSAGEBOX_ERROR = 0x00000010, /**< error dialog */ + SDL_MESSAGEBOX_WARNING = 0x00000020, /**< warning dialog */ + SDL_MESSAGEBOX_INFORMATION = 0x00000040, /**< informational dialog */ +} SDL_MessageBoxFlags; + +/** + * \brief Flags for SDL_MessageBoxButtonData. + */ +typedef enum +{ + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT = 0x00000001, /**< Marks the default button when return is hit */ + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT = 0x00000002, /**< Marks the default button when escape is hit */ +} SDL_MessageBoxButtonFlags; + +/** + * \brief Individual button data. + */ +typedef struct +{ + Uint32 flags; /**< ::SDL_MessageBoxButtonFlags */ + int buttonid; /**< User defined button id (value returned via SDL_MessageBox) */ + const char * text; /**< The UTF-8 button text */ +} SDL_MessageBoxButtonData; + +/** + * \brief RGB value used in a message box color scheme + */ +typedef struct +{ + Uint8 r, g, b; +} SDL_MessageBoxColor; + +typedef enum +{ + SDL_MESSAGEBOX_COLOR_BACKGROUND, + SDL_MESSAGEBOX_COLOR_TEXT, + SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, + SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, + SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, + SDL_MESSAGEBOX_COLOR_MAX +} SDL_MessageBoxColorType; + +/** + * \brief A set of colors to use for message box dialogs + */ +typedef struct +{ + SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]; +} SDL_MessageBoxColorScheme; + +/** + * \brief MessageBox structure containing title, text, window, etc. + */ +typedef struct +{ + Uint32 flags; /**< ::SDL_MessageBoxFlags */ + SDL_Window *window; /**< Parent window, can be NULL */ + const char *title; /**< UTF-8 title */ + const char *message; /**< UTF-8 message text */ + + int numbuttons; + const SDL_MessageBoxButtonData *buttons; + + const SDL_MessageBoxColorScheme *colorScheme; /**< ::SDL_MessageBoxColorScheme, can be NULL to use system settings */ +} SDL_MessageBoxData; + +/** + * \brief Create a modal message box. + * + * \param messagebox The SDL_MessageBox structure with title, text, etc. + * + * \return -1 on error, otherwise 0 and buttonid contains user id of button + * hit or -1 if dialog was closed. + */ +extern DECLSPEC int SDLCALL SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +/** + * \brief Create a simple modal message box + * + * \param flags ::SDL_MessageBoxFlags + * \param title UTF-8 title text + * \param message UTF-8 message text + * \param window The parent window, or NULL for no parent + * + * \return 0 on success, -1 on error + */ +extern DECLSPEC int SDLCALL SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window); + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif +#include "close_code.h" + +#endif /* _SDL_messagebox_h */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index ae260c91b..25c4f437f 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -23,6 +23,7 @@ #ifndef _SDL_sysvideo_h #define _SDL_sysvideo_h +#include "SDL_messagebox.h" #include "SDL_shape.h" /* The SDL video driver */ @@ -246,6 +247,9 @@ struct SDL_VideoDevice char * (*GetClipboardText) (_THIS); SDL_bool (*HasClipboardText) (_THIS); + /* MessageBox */ + int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid); + /* * * */ /* Data common to all drivers */ SDL_bool suspend_screensaver; diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 24c72dcde..aa6362070 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -2839,4 +2839,55 @@ SDL_IsScreenKeyboardShown(SDL_Window *window) return SDL_FALSE; } +#if SDL_VIDEO_DRIVER_X11 +extern int X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); +#endif + +int +SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ + int dummybutton; + + if (!buttonid) { + buttonid = &dummybutton; + } + if (_this && _this->ShowMessageBox) { + if (_this->ShowMessageBox(_this, messageboxdata, buttonid) == 0) { + return 0; + } + } + + /* It's completely fine to call this function before video is initialized */ +#if SDL_VIDEO_DRIVER_X11 + if (X11_ShowMessageBox(messageboxdata, buttonid) == 0) { + return 0; + } +#endif + + SDL_SetError("No message system available"); + return -1; +} + +int +SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window) +{ + SDL_MessageBoxData data; + SDL_MessageBoxButtonData button; + + SDL_zero(data); + data.flags = flags; + data.title = title; + data.message = message; + data.numbuttons = 1; + data.buttons = &button; + data.window = window; + + SDL_zero(button); + button.flags |= SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; + button.flags |= SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; + button.text = "OK"; + + return SDL_ShowMessageBox(&data, NULL); +} + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c new file mode 100644 index 000000000..e8e13d8e9 --- /dev/null +++ b/src/video/x11/SDL_x11messagebox.c @@ -0,0 +1,642 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#if SDL_VIDEO_DRIVER_X11 + +#include "SDL.h" +#include "SDL_x11video.h" +#include "SDL_x11dyn.h" + +#define MAX_BUTTONS 8 /* Maximum number of buttons supported */ +#define MAX_TEXT_LINES 32 /* Maximum number of text lines supported */ +#define MIN_BUTTON_WIDTH 64 /* Minimum button width */ +#define MIN_DIALOG_WIDTH 200 /* Minimum dialog width */ +#define MIN_DIALOG_HEIGHT 100 /* Minimum dialog height */ + +static const char g_MessageBoxFont[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1"; + +static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = +{ + { 56, 54, 53 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, + { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT, + { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, + { 105, 102, 99 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, + { 205, 202, 53 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, +}; + +#define SDL_MAKE_RGB( _r, _g, _b ) ( ( ( Uint32 )( _r ) << 16 ) | \ + ( ( Uint32 )( _g ) << 8 ) | \ + ( ( Uint32 )( _b ) ) ) + +typedef struct SDL_MessageBoxButtonDataX11 +{ + int x, y; /* Text position */ + int length; /* Text length */ + int text_width; /* Text width */ + + SDL_Rect rect; /* Rectangle for entire button */ + + const SDL_MessageBoxButtonData *buttondata; /* Button data from caller */ +} SDL_MessageBoxButtonDataX11; + +typedef struct TextLineData +{ + int width; /* Width of this text line */ + int length; /* String length of this text line */ + const char *text; /* Text for this line */ +} TextLineData; + +typedef struct SDL_MessageBoxDataX11 +{ + Font hfont; + Window window; + Display *display; + long event_mask; + Atom wm_protocols; + Atom wm_delete_message; + + int dialog_width; /* Dialog box width. */ + int dialog_height; /* Dialog box height. */ + + int xtext, ytext; /* Text position to start drawing at. */ + int numlines; /* Count of Text lines. */ + int text_height; /* Height for text lines. */ + TextLineData linedata[ MAX_TEXT_LINES ]; + + int *pbuttonid; /* Pointer to user return buttonid value. */ + + int button_press_index; /* Index into buttondata/buttonpos for button which is pressed (or -1). */ + int mouse_over_index; /* Index into buttondata/buttonpos for button mouse is over (or -1). */ + + int numbuttons; /* Count of buttons. */ + const SDL_MessageBoxButtonData *buttondata; + SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ]; + + Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ]; + + const SDL_MessageBoxData *messageboxdata; +} SDL_MessageBoxDataX11; + +/* Maximum helper for ints. */ +static __inline__ int +IntMax( int a, int b ) +{ + return ( a > b ) ? a : b; +} + +/* Return width and height for a string. */ +static void +GetTextWidthHeight( XFontStruct *font_struct, const char *str, int nchars, int *pwidth, int *pheight ) +{ + XCharStruct text_structure; + int font_direction, font_ascent, font_descent; + + XTextExtents( font_struct, str, nchars, + &font_direction, &font_ascent, &font_descent, + &text_structure ); + + *pwidth = text_structure.width; + *pheight = text_structure.ascent + text_structure.descent; +} + +/* Return index of button if position x,y is contained therein. */ +static int +GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y ) +{ + int i; + int numbuttons = data->numbuttons; + SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos; + + for ( i = 0; i < numbuttons; i++ ) + { + SDL_Rect *rect = &buttonpos[ i ].rect; + + if ( ( x >= rect->x ) && + ( x <= ( rect->x + rect->w ) ) && + ( y >= rect->y ) && + ( y <= ( rect->y + rect->h ) ) ) + { + return i; + } + } + + return -1; +} + +/* Initialize SDL_MessageBoxData structure and Display, etc. */ +static int +X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid ) +{ + int i; + int numbuttons = messageboxdata->numbuttons; + const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons; + const SDL_MessageBoxColor *colorhints; + + if ( numbuttons > MAX_BUTTONS ) { + SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS); + return -1; + } + + data->dialog_width = MIN_DIALOG_WIDTH; + data->dialog_height = MIN_DIALOG_HEIGHT; + data->messageboxdata = messageboxdata; + data->buttondata = buttondata; + data->numbuttons = numbuttons; + data->pbuttonid = pbuttonid; + + data->display = XOpenDisplay( NULL ); + if ( !data->display ) { + SDL_SetError("Couldn't open X11 display"); + return -1; + } + + data->hfont = XLoadFont( data->display, g_MessageBoxFont ); + if ( data->hfont == None ) { + SDL_SetError("Couldn't load font %s", g_MessageBoxFont); + return -1; + } + + if ( messageboxdata->colorScheme ) { + colorhints = messageboxdata->colorScheme->colors; + } else { + colorhints = g_default_colors; + } + + /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */ + for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) { + data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b ); + } + + return 0; +} + +/* Calculate and initialize text and button locations. */ +static int +X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data ) +{ + int i; + int ybuttons; + int text_width_max = 0; + int button_text_height = 0; + int button_width = MIN_BUTTON_WIDTH; + const SDL_MessageBoxData *messageboxdata = data->messageboxdata; + + XFontStruct *fontinfo = XQueryFont( data->display, data->hfont ); + if ( !fontinfo ) { + SDL_SetError("Couldn't get font info"); + return -1; + } + + /* Go over text and break linefeeds into separate lines. */ + if ( messageboxdata->message && messageboxdata->message[ 0 ] ) + { + const char *text = messageboxdata->message; + TextLineData *plinedata = data->linedata; + + for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) + { + int height; + char *lf = SDL_strchr( ( char * )text, '\n' ); + + data->numlines++; + + /* Only grab length up to lf if it exists and isn't the last line. */ + plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text ); + plinedata->text = text; + + GetTextWidthHeight( fontinfo, text, plinedata->length, &plinedata->width, &height ); + + /* Text and widths are the largest we've ever seen. */ + data->text_height = IntMax( data->text_height, height ); + text_width_max = IntMax( text_width_max, plinedata->width ); + + text += plinedata->length + 1; + + /* Break if there are no more linefeeds. */ + if ( !lf ) + break; + } + + /* Bump up the text height slightly. */ + data->text_height += 2; + } + + /* Loop through all buttons and calculate the button widths and height. */ + for ( i = 0; i < data->numbuttons; i++ ) + { + int height; + + data->buttonpos[ i ].buttondata = &data->buttondata[ i ]; + data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text ); + + GetTextWidthHeight( fontinfo, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ), + &data->buttonpos[ i ].text_width, &height ); + + button_width = IntMax( button_width, data->buttonpos[ i ].text_width ); + button_text_height = IntMax( button_text_height, height ); + } + + if ( data->numlines ) + { + /* x,y for this line of text. */ + data->xtext = data->text_height; + data->ytext = data->text_height + data->text_height; + + /* Bump button y down to bottom of text. */ + ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height; + + /* Bump the dialog box width and height up if needed. */ + data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max ); + data->dialog_height = IntMax( data->dialog_height, ybuttons ); + } + else + { + /* Button y starts at height of button text. */ + ybuttons = button_text_height; + } + + if ( data->numbuttons ) + { + int x, y; + int width_of_buttons; + int button_spacing = button_text_height; + int button_height = 2 * button_text_height; + + /* Bump button width up a bit. */ + button_width += button_text_height; + + /* Get width of all buttons lined up. */ + width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing; + + /* Bump up dialog width and height if buttons are wider than text. */ + data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing ); + data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height ); + + /* Location for first button. */ + x = ( data->dialog_width - width_of_buttons ) / 2; + y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2; + + for ( i = 0; i < data->numbuttons; i++ ) + { + /* Button coordinates. */ + data->buttonpos[ i ].rect.x = x; + data->buttonpos[ i ].rect.y = y; + data->buttonpos[ i ].rect.w = button_width; + data->buttonpos[ i ].rect.h = button_height; + + /* Button text coordinates. */ + data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2; + data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height; + + /* Scoot over for next button. */ + x += button_width + button_text_height; + } + } + + XFreeFontInfo( NULL, fontinfo, 1 ); + return 0; +} + +/* Free SDL_MessageBoxData data. */ +static void +X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data ) +{ + if ( data->hfont != None ) + { + XUnloadFont( data->display, data->hfont ); + data->hfont = None; + } + + if ( data->display ) + { + if ( data->window != None ) + { + XUnmapWindow( data->display, data->window ); + XDestroyWindow( data->display, data->window ); + data->window = None; + } + + XCloseDisplay( data->display ); + data->display = NULL; + } +} + +/* Create and set up our X11 dialog box indow. */ +static int +X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data ) +{ + int x, y; + XSizeHints *sizehints; + XSetWindowAttributes wnd_attr; + Display *display = data->display; + SDL_WindowData *windowdata = NULL; + const SDL_MessageBoxData *messageboxdata = data->messageboxdata; + + if ( messageboxdata->window ) { + windowdata = (SDL_WindowData *)messageboxdata->window->driverdata; + } + + data->event_mask = ExposureMask | + ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask | FocusChangeMask | PointerMotionMask; + wnd_attr.event_mask = data->event_mask; + + data->window = XCreateWindow( + display, DefaultRootWindow( display ), + 0, 0, + data->dialog_width, data->dialog_height, + 0, CopyFromParent, InputOutput, CopyFromParent, + CWEventMask, &wnd_attr ); + if ( data->window == None ) { + SDL_SetError("Couldn't create X window"); + return -1; + } + + if ( windowdata ) { + /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */ + XSetTransientForHint( display, data->window, windowdata->xwindow ); + } + + XStoreName( display, data->window, messageboxdata->title ); + + /* Allow the window to be deleted by the window manager */ + data->wm_protocols = XInternAtom( display, "WM_PROTOCOLS", False ); + data->wm_delete_message = XInternAtom( display, "WM_DELETE_WINDOW", False ); + XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 ); + + if ( windowdata ) { + XWindowAttributes attrib; + Window dummy; + + XGetWindowAttributes(display, windowdata->xwindow, &attrib); + x = attrib.x + ( attrib.width - data->dialog_width ) / 2; + y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ; + XTranslateCoordinates(display, windowdata->xwindow, DefaultRootWindow( display ), x, y, &x, &y, &dummy); + } else { + int screen = DefaultScreen( display ); + x = ( DisplayWidth( display, screen ) - data->dialog_width ) / 2; + y = ( DisplayHeight( display, screen ) - data->dialog_height ) / 3 ; + } + XMoveWindow( display, data->window, x, y ); + + sizehints = XAllocSizeHints(); + if ( sizehints ) { + sizehints->flags = USPosition | USSize | PMaxSize | PMinSize; + sizehints->x = x; + sizehints->y = y; + sizehints->width = data->dialog_width; + sizehints->height = data->dialog_height; + + sizehints->min_width = sizehints->max_width = data->dialog_width; + sizehints->min_height = sizehints->max_height = data->dialog_height; + + XSetWMNormalHints( display, data->window, sizehints ); + + XFree( sizehints ); + } + + XMapRaised( display, data->window ); + return 0; +} + +/* Draw our message box. */ +static void +X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx ) +{ + int i; + Window window = data->window; + Display *display = data->display; + + XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] ); + XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height ); + + XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] ); + for ( i = 0; i < data->numlines; i++ ) + { + TextLineData *plinedata = &data->linedata[ i ]; + + XDrawString( display, window, ctx, + data->xtext, data->ytext + i * data->text_height, + plinedata->text, plinedata->length ); + } + + for ( i = 0; i < data->numbuttons; i++ ) + { + SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ]; + const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata; + int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0; + int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0; + + XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] ); + XFillRectangle( display, window, ctx, + buttondatax11->rect.x - border, buttondatax11->rect.y - border, + buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border ); + + XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] ); + XDrawRectangle( display, window, ctx, + buttondatax11->rect.x, buttondatax11->rect.y, + buttondatax11->rect.w, buttondatax11->rect.h ); + + XSetForeground( display, ctx, ( data->mouse_over_index == i ) ? + data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] : + data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] ); + XDrawString( display, window, ctx, + buttondatax11->x + offset, buttondatax11->y + offset, + buttondata->text, buttondatax11->length ); + } +} + +/* Loop and handle message box event messages until something kills it. */ +static int +X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data ) +{ + GC ctx; + XGCValues ctx_vals; + SDL_bool close_dialog = SDL_FALSE; + SDL_bool has_focus = SDL_TRUE; + KeySym last_key_pressed = XK_VoidSymbol; + + ctx_vals.font = data->hfont; + ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ]; + ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ]; + + ctx = XCreateGC( data->display, data->window, GCForeground | GCBackground | GCFont, &ctx_vals ); + if ( ctx == None ) { + SDL_SetError("Couldn't create graphics context"); + return -1; + } + + data->button_press_index = -1; /* Reset what button is currently depressed. */ + data->mouse_over_index = -1; /* Reset what button the mouse is over. */ + + while( !close_dialog ) { + XEvent e; + SDL_bool draw = SDL_TRUE; + + XWindowEvent( data->display, data->window, data->event_mask, &e ); + + /* If XFilterEvent returns True, then some input method has filtered the + event, and the client should discard the event. */ + if ( ( e.type != Expose ) && XFilterEvent( &e, None ) ) + continue; + + switch( e.type ) { + case Expose: + if ( e.xexpose.count > 0 ) { + draw = SDL_FALSE; + } + break; + + case FocusIn: + /* Got focus. */ + has_focus = SDL_TRUE; + break; + + case FocusOut: + /* lost focus. Reset button and mouse info. */ + has_focus = SDL_FALSE; + data->button_press_index = -1; + data->mouse_over_index = -1; + break; + + case MotionNotify: + if ( has_focus ) { + /* Mouse moved... */ + data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y ); + } + break; + + case ClientMessage: + if ( e.xclient.message_type == data->wm_protocols && + e.xclient.format == 32 && + e.xclient.data.l[ 0 ] == data->wm_delete_message ) { + close_dialog = SDL_TRUE; + } + break; + + case KeyPress: + /* Store key press - we make sure in key release that we got both. */ + last_key_pressed = XLookupKeysym( &e.xkey, 0 ); + break; + + case KeyRelease: + { + Uint32 mask = 0; + KeySym key = XLookupKeysym( &e.xkey, 0 ); + + /* If this is a key release for something we didn't get the key down for, then bail. */ + if ( key != last_key_pressed ) + break; + + if ( key == XK_Escape ) + mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; + else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) ) + mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; + + if ( mask ) { + int i; + + /* Look for first button with this mask set, and return it if found. */ + for ( i = 0; i < data->numbuttons; i++ ) { + SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ]; + + if ( buttondatax11->buttondata->flags & mask ) { + *data->pbuttonid = buttondatax11->buttondata->buttonid; + close_dialog = SDL_TRUE; + break; + } + } + } + break; + } + + case ButtonPress: + data->button_press_index = -1; + if ( e.xbutton.button == Button1 ) { + /* Find index of button they clicked on. */ + data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y ); + } + break; + + case ButtonRelease: + /* If button is released over the same button that was clicked down on, then return it. */ + if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) { + int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y ); + + if ( data->button_press_index == button ) { + SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ]; + + *data->pbuttonid = buttondatax11->buttondata->buttonid; + close_dialog = SDL_TRUE; + } + } + data->button_press_index = -1; + break; + } + + if ( draw ) { + /* Draw our dialog box. */ + X11_MessageBoxDraw( data, ctx ); + } + } + + XFreeGC( data->display, ctx ); + return 0; +} + +/* Display an x11 message box. */ +int +X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid) +{ + int ret; + SDL_MessageBoxDataX11 data; + + SDL_memset( &data, 0, sizeof( data ) ); + + if ( !SDL_X11_LoadSymbols() ) + return -1; + + /* This code could get called from multiple threads maybe? */ + XInitThreads(); + + /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */ + *buttonid = -1; + + /* Init and display the message box. */ + ret = X11_MessageBoxInit( &data, messageboxdata, buttonid ); + if ( ret != -1 ) { + ret = X11_MessageBoxInitPositions( &data ); + if ( ret != -1 ) { + ret = X11_MessageBoxCreateWindow( &data ); + if ( ret != -1 ) { + ret = X11_MessageBoxLoop( &data ); + } + } + } + + X11_MessageBoxShutdown( &data ); + return ret; +} + +#endif /* SDL_VIDEO_DRIVER_X11 */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 9449849bd..c7cc2a966 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -43,13 +43,17 @@ SDL_X11_SYM(int,XDefineCursor,(Display* a,Window b,Cursor c),(a,b,c),return) SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c),(a,b,c),return) SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b),(a,b),return) SDL_X11_SYM(int,XDisplayKeycodes,(Display* a,int* b,int* c),(a,b,c),return) +SDL_X11_SYM(int,XDrawRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return) SDL_X11_SYM(char*,XDisplayName,(_Xconst char* a),(a),return) +SDL_X11_SYM(int,XDrawString,(Display* a,Drawable b,GC c,int d,int e,_Xconst char* f,int g),(a,b,c,d,e,f,g),return) SDL_X11_SYM(int,XEventsQueued,(Display* a,int b),(a,b),return) +SDL_X11_SYM(int,XFillRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return) SDL_X11_SYM(Bool,XFilterEvent,(XEvent *event,Window w),(event,w),return) SDL_X11_SYM(int,XFlush,(Display* a),(a),return) SDL_X11_SYM(int,XFree,(void*a),(a),return) SDL_X11_SYM(int,XFreeCursor,(Display* a,Cursor b),(a,b),return) SDL_X11_SYM(int,XFreeGC,(Display* a,GC b),(a,b),return) +SDL_X11_SYM(int,XFreeFontInfo,(char** a,XFontStruct* b,int c),(a,b,c),return) SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a),(a),return) SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b),(a,b),return) SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b),(a,b),return) @@ -73,6 +77,7 @@ SDL_X11_SYM(char*,XKeysymToString,(KeySym a),(a),return) SDL_X11_SYM(int,XInstallColormap,(Display* a,Colormap b),(a,b),return) SDL_X11_SYM(Atom,XInternAtom,(Display* a,_Xconst char* b,Bool c),(a,b,c),return) SDL_X11_SYM(XPixmapFormatValues*,XListPixmapFormats,(Display* a,int* b),(a,b),return) +SDL_X11_SYM(Font,XLoadFont,(Display* a,_Xconst char* b),(a,b),return) SDL_X11_SYM(KeySym,XLookupKeysym,(XKeyEvent* a,int b),(a,b),return) SDL_X11_SYM(int,XLookupString,(XKeyEvent* a,char* b,int c,KeySym* d,XComposeStatus* e),(a,b,c,d,e),return) SDL_X11_SYM(int,XMapRaised,(Display* a,Window b),(a,b),return) @@ -85,6 +90,7 @@ SDL_X11_SYM(Status,XInitThreads,(void),(),return) SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b),(a,b),return) SDL_X11_SYM(int,XPending,(Display* a),(a),return) SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j),(a,b,c,d,e,f,g,h,i,j),return) +SDL_X11_SYM(XFontStruct*,XQueryFont,(Display* a,XID b),(a,b),return) SDL_X11_SYM(int,XQueryKeymap,(Display* a,char *b),(a,b),return) SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i),(a,b,c,d,e,f,g,h,i),return) SDL_X11_SYM(int,XRaiseWindow,(Display* a,Window b),(a,b),return) @@ -94,6 +100,7 @@ SDL_X11_SYM(int,XResizeWindow,(Display* a,Window b,unsigned int c,unsigned int d SDL_X11_SYM(int,XSelectInput,(Display* a,Window b,long c),(a,b,c),return) SDL_X11_SYM(Status,XSendEvent,(Display* a,Window b,Bool c,long d,XEvent* e),(a,b,c,d,e),return) SDL_X11_SYM(XErrorHandler,XSetErrorHandler,(XErrorHandler a),(a),return) +SDL_X11_SYM(int,XSetForeground,(Display* a,GC b,unsigned long c),(a,b,c),return) SDL_X11_SYM(XIOErrorHandler,XSetIOErrorHandler,(XIOErrorHandler a),(a),return) SDL_X11_SYM(int,XSetInputFocus,(Display *a,Window b,int c,Time d),(a,b,c,d),return) SDL_X11_SYM(int,XSetSelectionOwner,(Display* a,Atom b,Window c,Time d),(a,b,c,d),return) @@ -104,15 +111,20 @@ SDL_X11_SYM(void,XSetWMProperties,(Display* a,Window b,XTextProperty* c,XTextPro SDL_X11_SYM(void,XSetWMNormalHints,(Display* a,Window b,XSizeHints* c),(a,b,c),) SDL_X11_SYM(Status,XSetWMProtocols,(Display* a,Window b,Atom* c,int d),(a,b,c,d),return) SDL_X11_SYM(int,XStoreColors,(Display* a,Colormap b,XColor* c,int d),(a,b,c,d),return) +SDL_X11_SYM(int,XStoreName,(Display* a,Window b,_Xconst char* c),(a,b,c),return) SDL_X11_SYM(Status,XStringListToTextProperty,(char** a,int b,XTextProperty* c),(a,b,c),return) SDL_X11_SYM(int,XSync,(Display* a,Bool b),(a,b),return) +SDL_X11_SYM(int,XTextExtents,(XFontStruct* a,_Xconst char* b,int c,int* d,int* e,int* f,XCharStruct* g),(a,b,c,d,e,f,g),return) +SDL_X11_SYM(Bool,XTranslateCoordinates,(Display *a,Window b,Window c,int d,int e,int* f,int* g,Window* h),(a,b,c,d,e,f,g,h),return) SDL_X11_SYM(int,XUndefineCursor,(Display* a,Window b),(a,b),return) SDL_X11_SYM(int,XUngrabKeyboard,(Display* a,Time b),(a,b),return) SDL_X11_SYM(int,XUngrabPointer,(Display* a,Time b),(a,b),return) SDL_X11_SYM(int,XUngrabServer,(Display* a),(a),return) SDL_X11_SYM(int,XUninstallColormap,(Display* a,Colormap b),(a,b),return) +SDL_X11_SYM(int,XUnloadFont,(Display* a,Font b),(a,b),return) SDL_X11_SYM(int,XUnmapWindow,(Display* a,Window b),(a,b),return) SDL_X11_SYM(int,XWarpPointer,(Display* a,Window b,Window c,int d,int e,unsigned int f,unsigned int g,int h,int i),(a,b,c,d,e,f,g,h,i),return) +SDL_X11_SYM(int,XWindowEvent,(Display* a,Window b,long c,XEvent* d),(a,b,c,d),return) SDL_X11_SYM(VisualID,XVisualIDFromVisual,(Visual* a),(a),return) #if SDL_VIDEO_DRIVER_X11_CONST_PARAM_XEXTADDDISPLAY SDL_X11_SYM(XExtDisplayInfo*,XextAddDisplay,(XExtensionInfo* a,Display* b,_Xconst char* c,XExtensionHooks* d,int e,XPointer f),(a,b,c,d,e,f),return) diff --git a/test/common.c b/test/common.c index 8c4c1ee29..7af002caf 100644 --- a/test/common.c +++ b/test/common.c @@ -1204,6 +1204,12 @@ CommonEvent(CommonState * state, SDL_Event * event, int *done) } } break; + case SDLK_1: + if (event->key.keysym.mod & KMOD_CTRL) { + SDL_Window *window = SDL_GetWindowFromID(event->key.windowID); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Test Message", "You're awesome!", window); + } + break; case SDLK_ESCAPE: *done = 1; break;