2015-06-21 17:33:46 +02:00
|
|
|
/*
|
|
|
|
Simple DirectMedia Layer
|
2017-01-02 03:33:28 +01:00
|
|
|
Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
|
2015-06-21 17:33:46 +02:00
|
|
|
|
|
|
|
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_internal.h"
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_X11
|
|
|
|
|
|
|
|
#include "SDL.h"
|
|
|
|
#include "SDL_x11video.h"
|
|
|
|
#include "SDL_x11dyn.h"
|
|
|
|
#include "SDL_assert.h"
|
2016-11-14 07:57:41 +01:00
|
|
|
#include "SDL_x11messagebox.h"
|
2015-06-21 17:33:46 +02:00
|
|
|
|
|
|
|
#include <X11/keysym.h>
|
|
|
|
#include <locale.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define SDL_FORK_MESSAGEBOX 1
|
|
|
|
#define SDL_SET_LOCALE 1
|
|
|
|
|
|
|
|
#if SDL_FORK_MESSAGEBOX
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#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_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
|
2017-01-28 10:54:12 +01:00
|
|
|
static const char g_MessageBoxFont[] = "-*-*-medium-r-normal--*-120-*-*-*-*-*-*";
|
2015-06-21 17:33:46 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
Display *display;
|
|
|
|
int screen;
|
|
|
|
Window window;
|
|
|
|
#if SDL_VIDEO_DRIVER_X11_XDBE
|
|
|
|
XdbeBackBuffer buf;
|
|
|
|
SDL_bool xdbe; /* Whether Xdbe is present or not */
|
|
|
|
#endif
|
|
|
|
long event_mask;
|
|
|
|
Atom wm_protocols;
|
|
|
|
Atom wm_delete_message;
|
|
|
|
|
|
|
|
int dialog_width; /* Dialog box width. */
|
|
|
|
int dialog_height; /* Dialog box height. */
|
|
|
|
|
|
|
|
XFontSet font_set; /* for UTF-8 systems */
|
|
|
|
XFontStruct *font_struct; /* Latin1 (ASCII) fallback. */
|
|
|
|
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 SDL_INLINE int
|
|
|
|
IntMax( int a, int b )
|
|
|
|
{
|
|
|
|
return ( a > b ) ? a : b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return width and height for a string. */
|
|
|
|
static void
|
|
|
|
GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
|
|
|
|
{
|
|
|
|
if (SDL_X11_HAVE_UTF8) {
|
|
|
|
XRectangle overall_ink, overall_logical;
|
|
|
|
X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
|
|
|
|
*pwidth = overall_logical.width;
|
|
|
|
*pheight = overall_logical.height;
|
|
|
|
} else {
|
|
|
|
XCharStruct text_structure;
|
|
|
|
int font_direction, font_ascent, font_descent;
|
|
|
|
X11_XTextExtents( data->font_struct, str, nbytes,
|
|
|
|
&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 ) {
|
|
|
|
return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 = X11_XOpenDisplay( NULL );
|
|
|
|
if ( !data->display ) {
|
|
|
|
return SDL_SetError("Couldn't open X11 display");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_X11_HAVE_UTF8) {
|
|
|
|
char **missing = NULL;
|
|
|
|
int num_missing = 0;
|
|
|
|
data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont,
|
|
|
|
&missing, &num_missing, NULL);
|
|
|
|
if ( missing != NULL ) {
|
|
|
|
X11_XFreeStringList(missing);
|
|
|
|
}
|
|
|
|
if ( data->font_set == NULL ) {
|
|
|
|
return SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
data->font_struct = X11_XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
|
|
|
|
if ( data->font_struct == NULL ) {
|
|
|
|
return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* 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( data, 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 );
|
|
|
|
|
|
|
|
if (lf && (lf > text) && (lf[-1] == '\r')) {
|
|
|
|
plinedata->length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
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( data, 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_spacing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free SDL_MessageBoxData data. */
|
|
|
|
static void
|
|
|
|
X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
|
|
|
|
{
|
|
|
|
if ( data->font_set != NULL ) {
|
|
|
|
X11_XFreeFontSet( data->display, data->font_set );
|
|
|
|
data->font_set = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( data->font_struct != NULL ) {
|
|
|
|
X11_XFreeFont( data->display, data->font_struct );
|
|
|
|
data->font_struct = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_X11_XDBE
|
|
|
|
if ( SDL_X11_HAVE_XDBE && data->xdbe ) {
|
|
|
|
X11_XdbeDeallocateBackBufferName(data->display, data->buf);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( data->display ) {
|
|
|
|
if ( data->window != None ) {
|
|
|
|
X11_XWithdrawWindow( data->display, data->window, data->screen );
|
|
|
|
X11_XDestroyWindow( data->display, data->window );
|
|
|
|
data->window = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_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;
|
2017-08-13 06:58:23 +02:00
|
|
|
Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_NAME;
|
2015-06-21 17:33:46 +02:00
|
|
|
Display *display = data->display;
|
|
|
|
SDL_WindowData *windowdata = NULL;
|
|
|
|
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
|
2017-08-13 06:58:23 +02:00
|
|
|
char *title_locale = NULL;
|
2015-06-21 17:33:46 +02:00
|
|
|
|
|
|
|
if ( messageboxdata->window ) {
|
|
|
|
SDL_DisplayData *displaydata =
|
|
|
|
(SDL_DisplayData *) SDL_GetDisplayForWindow(messageboxdata->window)->driverdata;
|
|
|
|
windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
|
|
|
|
data->screen = displaydata->screen;
|
|
|
|
} else {
|
|
|
|
data->screen = DefaultScreen( display );
|
|
|
|
}
|
|
|
|
|
|
|
|
data->event_mask = ExposureMask |
|
|
|
|
ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
|
|
|
|
StructureNotifyMask | FocusChangeMask | PointerMotionMask;
|
|
|
|
wnd_attr.event_mask = data->event_mask;
|
|
|
|
|
|
|
|
data->window = X11_XCreateWindow(
|
|
|
|
display, RootWindow(display, data->screen),
|
|
|
|
0, 0,
|
|
|
|
data->dialog_width, data->dialog_height,
|
|
|
|
0, CopyFromParent, InputOutput, CopyFromParent,
|
|
|
|
CWEventMask, &wnd_attr );
|
|
|
|
if ( data->window == None ) {
|
|
|
|
return SDL_SetError("Couldn't create X window");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( windowdata ) {
|
|
|
|
/* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
|
|
|
|
X11_XSetTransientForHint( display, data->window, windowdata->xwindow );
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_XStoreName( display, data->window, messageboxdata->title );
|
|
|
|
_NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False);
|
2017-08-13 06:58:23 +02:00
|
|
|
|
|
|
|
title_locale = SDL_iconv_utf8_locale(messageboxdata->title);
|
|
|
|
if (title_locale) {
|
|
|
|
XTextProperty titleprop;
|
|
|
|
Status status = X11_XStringListToTextProperty(&title_locale, 1, &titleprop);
|
|
|
|
SDL_free(title_locale);
|
|
|
|
if (status) {
|
2017-08-13 07:00:01 +02:00
|
|
|
X11_XSetTextProperty(display, data->window, &titleprop, XA_WM_NAME);
|
2017-08-13 06:58:23 +02:00
|
|
|
X11_XFree(titleprop.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef X_HAVE_UTF8_STRING
|
|
|
|
if (SDL_X11_HAVE_UTF8) {
|
|
|
|
XTextProperty titleprop;
|
2017-08-13 07:00:01 +02:00
|
|
|
Status status = X11_Xutf8TextListToTextProperty(display, (char **) &messageboxdata->title, 1,
|
2017-08-13 06:58:23 +02:00
|
|
|
XUTF8StringStyle, &titleprop);
|
|
|
|
if (status == Success) {
|
|
|
|
X11_XSetTextProperty(display, data->window, &titleprop,
|
|
|
|
_NET_WM_NAME);
|
|
|
|
X11_XFree(titleprop.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2015-06-21 17:33:46 +02:00
|
|
|
|
|
|
|
/* Let the window manager know this is a dialog box */
|
|
|
|
_NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
|
|
|
|
_NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
|
|
|
|
X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
|
|
|
|
PropModeReplace,
|
|
|
|
(unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
|
|
|
|
|
|
|
|
/* Allow the window to be deleted by the window manager */
|
|
|
|
data->wm_protocols = X11_XInternAtom( display, "WM_PROTOCOLS", False );
|
|
|
|
data->wm_delete_message = X11_XInternAtom( display, "WM_DELETE_WINDOW", False );
|
|
|
|
X11_XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
|
|
|
|
|
|
|
|
if ( windowdata ) {
|
|
|
|
XWindowAttributes attrib;
|
|
|
|
Window dummy;
|
|
|
|
|
|
|
|
X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
|
|
|
|
x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
|
|
|
|
y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
|
|
|
|
X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
|
|
|
|
} else {
|
2015-07-15 05:46:35 +02:00
|
|
|
const SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
|
|
|
if ((dev) && (dev->displays) && (dev->num_displays > 0)) {
|
|
|
|
const SDL_VideoDisplay *dpy = &dev->displays[0];
|
|
|
|
const SDL_DisplayData *dpydata = (SDL_DisplayData *) dpy->driverdata;
|
|
|
|
x = dpydata->x + (( dpy->current_mode.w - data->dialog_width ) / 2);
|
|
|
|
y = dpydata->y + (( dpy->current_mode.h - data->dialog_height ) / 3);
|
|
|
|
} else { /* oh well. This will misposition on a multi-head setup. Init first next time. */
|
|
|
|
x = ( DisplayWidth( display, data->screen ) - data->dialog_width ) / 2;
|
|
|
|
y = ( DisplayHeight( display, data->screen ) - data->dialog_height ) / 3 ;
|
|
|
|
}
|
2015-06-21 17:33:46 +02:00
|
|
|
}
|
|
|
|
X11_XMoveWindow( display, data->window, x, y );
|
|
|
|
|
|
|
|
sizehints = X11_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;
|
|
|
|
|
|
|
|
X11_XSetWMNormalHints( display, data->window, sizehints );
|
|
|
|
|
|
|
|
X11_XFree( sizehints );
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_XMapRaised( display, data->window );
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_X11_XDBE
|
|
|
|
/* Initialise a back buffer for double buffering */
|
|
|
|
if (SDL_X11_HAVE_XDBE) {
|
|
|
|
int xdbe_major, xdbe_minor;
|
|
|
|
if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
|
|
|
|
data->xdbe = SDL_TRUE;
|
|
|
|
data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
|
|
|
|
} else {
|
|
|
|
data->xdbe = SDL_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw our message box. */
|
|
|
|
static void
|
|
|
|
X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Drawable window = data->window;
|
|
|
|
Display *display = data->display;
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_X11_XDBE
|
|
|
|
if (SDL_X11_HAVE_XDBE && data->xdbe) {
|
|
|
|
window = data->buf;
|
|
|
|
X11_XdbeBeginIdiom(data->display);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
|
|
|
|
X11_XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
|
|
|
|
|
|
|
|
X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
|
|
|
|
for ( i = 0; i < data->numlines; i++ ) {
|
|
|
|
TextLineData *plinedata = &data->linedata[ i ];
|
|
|
|
|
|
|
|
if (SDL_X11_HAVE_UTF8) {
|
|
|
|
X11_Xutf8DrawString( display, window, data->font_set, ctx,
|
|
|
|
data->xtext, data->ytext + i * data->text_height,
|
|
|
|
plinedata->text, plinedata->length );
|
|
|
|
} else {
|
|
|
|
X11_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;
|
|
|
|
|
|
|
|
X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
|
|
|
|
X11_XFillRectangle( display, window, ctx,
|
|
|
|
buttondatax11->rect.x - border, buttondatax11->rect.y - border,
|
|
|
|
buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
|
|
|
|
|
|
|
|
X11_XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
|
|
|
|
X11_XDrawRectangle( display, window, ctx,
|
|
|
|
buttondatax11->rect.x, buttondatax11->rect.y,
|
|
|
|
buttondatax11->rect.w, buttondatax11->rect.h );
|
|
|
|
|
|
|
|
X11_XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
|
|
|
|
data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
|
|
|
|
data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
|
|
|
|
|
|
|
|
if (SDL_X11_HAVE_UTF8) {
|
|
|
|
X11_Xutf8DrawString( display, window, data->font_set, ctx,
|
|
|
|
buttondatax11->x + offset,
|
|
|
|
buttondatax11->y + offset,
|
|
|
|
buttondata->text, buttondatax11->length );
|
|
|
|
} else {
|
|
|
|
X11_XDrawString( display, window, ctx,
|
|
|
|
buttondatax11->x + offset, buttondatax11->y + offset,
|
|
|
|
buttondata->text, buttondatax11->length );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SDL_VIDEO_DRIVER_X11_XDBE
|
|
|
|
if (SDL_X11_HAVE_XDBE && data->xdbe) {
|
|
|
|
XdbeSwapInfo swap_info;
|
|
|
|
swap_info.swap_window = data->window;
|
|
|
|
swap_info.swap_action = XdbeUndefined;
|
|
|
|
X11_XdbeSwapBuffers(data->display, &swap_info, 1);
|
|
|
|
X11_XdbeEndIdiom(data->display);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bool
|
|
|
|
X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
|
|
|
|
{
|
|
|
|
const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *) arg;
|
|
|
|
return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
unsigned long gcflags = GCForeground | GCBackground;
|
|
|
|
|
|
|
|
SDL_zero(ctx_vals);
|
|
|
|
ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
|
|
|
|
ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
|
|
|
|
|
|
|
|
if (!SDL_X11_HAVE_UTF8) {
|
|
|
|
gcflags |= GCFont;
|
|
|
|
ctx_vals.font = data->font_struct->fid;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = X11_XCreateGC( data->display, data->window, gcflags, &ctx_vals );
|
|
|
|
if ( ctx == None ) {
|
|
|
|
return SDL_SetError("Couldn't create graphics context");
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* can't use XWindowEvent() because it can't handle ClientMessage events. */
|
|
|
|
/* can't use XNextEvent() because we only want events for this window. */
|
|
|
|
X11_XIfEvent( data->display, &e, X11_MessageBoxEventTest, (XPointer) data );
|
|
|
|
|
|
|
|
/* If X11_XFilterEvent returns True, then some input method has filtered the
|
|
|
|
event, and the client should discard the event. */
|
|
|
|
if ( ( e.type != Expose ) && X11_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... */
|
|
|
|
const int previndex = data->mouse_over_index;
|
|
|
|
data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
|
|
|
|
if (data->mouse_over_index == previndex) {
|
|
|
|
draw = SDL_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 = X11_XLookupKeysym( &e.xkey, 0 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case KeyRelease: {
|
|
|
|
Uint32 mask = 0;
|
|
|
|
KeySym key = X11_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 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
X11_XFreeGC( data->display, ctx );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonid)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
SDL_MessageBoxDataX11 data;
|
|
|
|
#if SDL_SET_LOCALE
|
|
|
|
char *origlocale;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SDL_zero(data);
|
|
|
|
|
|
|
|
if ( !SDL_X11_LoadSymbols() )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
#if SDL_SET_LOCALE
|
|
|
|
origlocale = setlocale(LC_ALL, NULL);
|
|
|
|
if (origlocale != NULL) {
|
|
|
|
origlocale = SDL_strdup(origlocale);
|
|
|
|
if (origlocale == NULL) {
|
|
|
|
return SDL_OutOfMemory();
|
|
|
|
}
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* This code could get called from multiple threads maybe? */
|
|
|
|
X11_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 );
|
|
|
|
|
|
|
|
#if SDL_SET_LOCALE
|
|
|
|
if (origlocale) {
|
|
|
|
setlocale(LC_ALL, origlocale);
|
|
|
|
SDL_free(origlocale);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Display an x11 message box. */
|
|
|
|
int
|
|
|
|
X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
|
|
|
|
{
|
|
|
|
#if SDL_FORK_MESSAGEBOX
|
|
|
|
/* Use a child process to protect against setlocale(). Annoying. */
|
|
|
|
pid_t pid;
|
|
|
|
int fds[2];
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (pipe(fds) == -1) {
|
|
|
|
return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = fork();
|
|
|
|
if (pid == -1) { /* failed */
|
|
|
|
close(fds[0]);
|
|
|
|
close(fds[1]);
|
|
|
|
return X11_ShowMessageBoxImpl(messageboxdata, buttonid); /* oh well. */
|
|
|
|
} else if (pid == 0) { /* we're the child */
|
|
|
|
int exitcode = 0;
|
|
|
|
close(fds[0]);
|
|
|
|
status = X11_ShowMessageBoxImpl(messageboxdata, buttonid);
|
|
|
|
if (write(fds[1], &status, sizeof (int)) != sizeof (int))
|
|
|
|
exitcode = 1;
|
|
|
|
else if (write(fds[1], buttonid, sizeof (int)) != sizeof (int))
|
|
|
|
exitcode = 1;
|
|
|
|
close(fds[1]);
|
|
|
|
_exit(exitcode); /* don't run atexit() stuff, static destructors, etc. */
|
|
|
|
} else { /* we're the parent */
|
|
|
|
pid_t rc;
|
|
|
|
close(fds[1]);
|
|
|
|
do {
|
|
|
|
rc = waitpid(pid, &status, 0);
|
|
|
|
} while ((rc == -1) && (errno == EINTR));
|
|
|
|
|
|
|
|
SDL_assert(rc == pid); /* not sure what to do if this fails. */
|
|
|
|
|
|
|
|
if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
|
|
|
|
return SDL_SetError("msgbox child process failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read(fds[0], &status, sizeof (int)) != sizeof (int))
|
|
|
|
status = -1;
|
|
|
|
else if (read(fds[0], buttonid, sizeof (int)) != sizeof (int))
|
|
|
|
status = -1;
|
|
|
|
close(fds[0]);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return X11_ShowMessageBoxImpl(messageboxdata, buttonid);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif /* SDL_VIDEO_DRIVER_X11 */
|
|
|
|
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: */
|