src/video/x11/SDL_x11messagebox.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 07 Dec 2012 20:28:52 -0800
changeset 6725 a7a99a9d419a
parent 6724 6c5ed0c4cc6d
child 6726 f2898757eaae
permissions -rw-r--r--
astyle to the rescue!
slouken@6602
     1
/*
slouken@6602
     2
  Simple DirectMedia Layer
slouken@6602
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@6602
     4
slouken@6602
     5
  This software is provided 'as-is', without any express or implied
slouken@6602
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@6602
     7
  arising from the use of this software.
slouken@6602
     8
slouken@6602
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@6602
    10
  including commercial applications, and to alter it and redistribute it
slouken@6602
    11
  freely, subject to the following restrictions:
slouken@6602
    12
slouken@6602
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@6602
    14
     claim that you wrote the original software. If you use this software
slouken@6602
    15
     in a product, an acknowledgment in the product documentation would be
slouken@6602
    16
     appreciated but is not required.
slouken@6602
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@6602
    18
     misrepresented as being the original software.
slouken@6602
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@6602
    20
*/
icculus@6723
    21
slouken@6602
    22
#include "SDL_config.h"
slouken@6602
    23
slouken@6602
    24
#if SDL_VIDEO_DRIVER_X11
slouken@6602
    25
slouken@6602
    26
#include "SDL.h"
slouken@6602
    27
#include "SDL_x11video.h"
slouken@6602
    28
#include "SDL_x11dyn.h"
slouken@6602
    29
icculus@6723
    30
#include <locale.h>
icculus@6723
    31
slouken@6602
    32
#define MAX_BUTTONS             8       /* Maximum number of buttons supported */
slouken@6602
    33
#define MAX_TEXT_LINES          32      /* Maximum number of text lines supported */
slouken@6602
    34
#define MIN_BUTTON_WIDTH        64      /* Minimum button width */
slouken@6602
    35
#define MIN_DIALOG_WIDTH        200     /* Minimum dialog width */
slouken@6602
    36
#define MIN_DIALOG_HEIGHT       100     /* Minimum dialog height */
slouken@6602
    37
icculus@6723
    38
static const char g_MessageBoxFontLatin1[] = "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
icculus@6723
    39
static const char g_MessageBoxFont[] = "-*-*-*-*-*-*-*-*-*-*-*-*-*-*";
slouken@6602
    40
slouken@6725
    41
static const SDL_MessageBoxColor g_default_colors[ SDL_MESSAGEBOX_COLOR_MAX ] = {
slouken@6725
    42
    { 56,  54,  53  }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
slouken@6725
    43
    { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT,
slouken@6725
    44
    { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER,
slouken@6603
    45
    { 105, 102, 99  }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND,
slouken@6725
    46
    { 205, 202, 53  }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED,
slouken@6602
    47
};
slouken@6602
    48
slouken@6602
    49
#define SDL_MAKE_RGB( _r, _g, _b )  ( ( ( Uint32 )( _r ) << 16 ) | \
slouken@6602
    50
                                      ( ( Uint32 )( _g ) << 8 ) |  \
slouken@6602
    51
                                      ( ( Uint32 )( _b ) ) )
slouken@6602
    52
slouken@6725
    53
typedef struct SDL_MessageBoxButtonDataX11 {
slouken@6602
    54
    int x, y;                           /* Text position */
slouken@6602
    55
    int length;                         /* Text length */
slouken@6602
    56
    int text_width;                     /* Text width */
slouken@6602
    57
slouken@6602
    58
    SDL_Rect rect;                      /* Rectangle for entire button */
slouken@6602
    59
slouken@6602
    60
    const SDL_MessageBoxButtonData *buttondata;   /* Button data from caller */
slouken@6602
    61
} SDL_MessageBoxButtonDataX11;
slouken@6602
    62
slouken@6725
    63
typedef struct TextLineData {
slouken@6602
    64
    int width;                          /* Width of this text line */
slouken@6602
    65
    int length;                         /* String length of this text line */
slouken@6602
    66
    const char *text;                   /* Text for this line */
slouken@6602
    67
} TextLineData;
slouken@6602
    68
slouken@6725
    69
typedef struct SDL_MessageBoxDataX11 {
icculus@6723
    70
    XFontSet font_set;                  /* for UTF-8 systems */
icculus@6723
    71
    XFontStruct *font_struct;            /* Latin1 (ASCII) fallback. */
slouken@6602
    72
    Window window;
slouken@6602
    73
    Display *display;
slouken@6603
    74
    long event_mask;
slouken@6602
    75
    Atom wm_protocols;
slouken@6602
    76
    Atom wm_delete_message;
slouken@6602
    77
slouken@6602
    78
    int dialog_width;                   /* Dialog box width. */
slouken@6602
    79
    int dialog_height;                  /* Dialog box height. */
slouken@6602
    80
slouken@6602
    81
    int xtext, ytext;                   /* Text position to start drawing at. */
slouken@6602
    82
    int numlines;                       /* Count of Text lines. */
slouken@6602
    83
    int text_height;                    /* Height for text lines. */
slouken@6602
    84
    TextLineData linedata[ MAX_TEXT_LINES ];
slouken@6602
    85
slouken@6602
    86
    int *pbuttonid;                     /* Pointer to user return buttonid value. */
slouken@6602
    87
slouken@6602
    88
    int button_press_index;             /* Index into buttondata/buttonpos for button which is pressed (or -1). */
slouken@6602
    89
    int mouse_over_index;               /* Index into buttondata/buttonpos for button mouse is over (or -1). */
slouken@6602
    90
slouken@6602
    91
    int numbuttons;                     /* Count of buttons. */
slouken@6602
    92
    const SDL_MessageBoxButtonData *buttondata;
slouken@6602
    93
    SDL_MessageBoxButtonDataX11 buttonpos[ MAX_BUTTONS ];
slouken@6602
    94
slouken@6603
    95
    Uint32 color[ SDL_MESSAGEBOX_COLOR_MAX ];
slouken@6602
    96
slouken@6602
    97
    const SDL_MessageBoxData *messageboxdata;
slouken@6602
    98
} SDL_MessageBoxDataX11;
slouken@6602
    99
slouken@6602
   100
/* Maximum helper for ints. */
slouken@6602
   101
static __inline__ int
slouken@6602
   102
IntMax( int a, int b )
slouken@6602
   103
{
slouken@6602
   104
    return ( a > b  ) ? a : b;
slouken@6602
   105
}
slouken@6602
   106
slouken@6602
   107
/* Return width and height for a string. */
slouken@6602
   108
static void
icculus@6723
   109
GetTextWidthHeight( SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight )
slouken@6602
   110
{
icculus@6723
   111
    if (SDL_X11_HAVE_UTF8) {
icculus@6723
   112
        XRectangle overall_ink, overall_logical;
icculus@6723
   113
        Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
icculus@6723
   114
        *pwidth = overall_logical.width;
icculus@6723
   115
        *pheight = overall_logical.height;
icculus@6723
   116
    } else {
icculus@6723
   117
        XCharStruct text_structure;
icculus@6723
   118
        int font_direction, font_ascent, font_descent;
icculus@6723
   119
        XTextExtents( data->font_struct, str, nbytes,
icculus@6723
   120
                      &font_direction, &font_ascent, &font_descent,
icculus@6723
   121
                      &text_structure );
icculus@6723
   122
        *pwidth = text_structure.width;
icculus@6723
   123
        *pheight = text_structure.ascent + text_structure.descent;
icculus@6723
   124
    }
slouken@6602
   125
}
slouken@6602
   126
slouken@6602
   127
/* Return index of button if position x,y is contained therein. */
slouken@6602
   128
static int
slouken@6602
   129
GetHitButtonIndex( SDL_MessageBoxDataX11 *data, int x, int y )
slouken@6602
   130
{
slouken@6602
   131
    int i;
slouken@6602
   132
    int numbuttons = data->numbuttons;
slouken@6602
   133
    SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
slouken@6602
   134
slouken@6725
   135
    for ( i = 0; i < numbuttons; i++ ) {
slouken@6602
   136
        SDL_Rect *rect = &buttonpos[ i ].rect;
slouken@6602
   137
slouken@6602
   138
        if ( ( x >= rect->x ) &&
slouken@6725
   139
             ( x <= ( rect->x + rect->w ) ) &&
slouken@6725
   140
             ( y >= rect->y ) &&
slouken@6725
   141
             ( y <= ( rect->y + rect->h ) ) ) {
slouken@6602
   142
            return i;
slouken@6602
   143
        }
slouken@6602
   144
    }
slouken@6602
   145
slouken@6602
   146
    return -1;
slouken@6602
   147
}
slouken@6602
   148
slouken@6602
   149
/* Initialize SDL_MessageBoxData structure and Display, etc. */
slouken@6602
   150
static int
slouken@6602
   151
X11_MessageBoxInit( SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData * messageboxdata, int * pbuttonid )
slouken@6602
   152
{
slouken@6603
   153
    int i;
slouken@6602
   154
    int numbuttons = messageboxdata->numbuttons;
slouken@6602
   155
    const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
slouken@6603
   156
    const SDL_MessageBoxColor *colorhints;
slouken@6602
   157
slouken@6602
   158
    if ( numbuttons > MAX_BUTTONS ) {
slouken@6602
   159
        SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
slouken@6602
   160
        return -1;
slouken@6602
   161
    }
slouken@6602
   162
slouken@6602
   163
    data->dialog_width = MIN_DIALOG_WIDTH;
slouken@6602
   164
    data->dialog_height = MIN_DIALOG_HEIGHT;
slouken@6602
   165
    data->messageboxdata = messageboxdata;
slouken@6602
   166
    data->buttondata = buttondata;
slouken@6602
   167
    data->numbuttons = numbuttons;
slouken@6602
   168
    data->pbuttonid = pbuttonid;
slouken@6602
   169
slouken@6602
   170
    data->display = XOpenDisplay( NULL );
slouken@6602
   171
    if ( !data->display ) {
slouken@6602
   172
        SDL_SetError("Couldn't open X11 display");
slouken@6602
   173
        return -1;
slouken@6602
   174
    }
slouken@6602
   175
icculus@6723
   176
    if (SDL_X11_HAVE_UTF8) {
icculus@6723
   177
        char **missing = NULL;
icculus@6723
   178
        int num_missing = 0;
icculus@6723
   179
        data->font_set = XCreateFontSet(data->display, g_MessageBoxFont,
icculus@6723
   180
                                        &missing, &num_missing, NULL);
icculus@6723
   181
        if ( missing != NULL ) {
icculus@6723
   182
            XFreeStringList(missing);
icculus@6723
   183
        }
icculus@6723
   184
        if ( data->font_set == NULL ) {
icculus@6723
   185
            SDL_SetError("Couldn't load font %s", g_MessageBoxFont);
icculus@6723
   186
            return -1;
icculus@6723
   187
        }
icculus@6723
   188
    } else {
icculus@6723
   189
        data->font_struct = XLoadQueryFont( data->display, g_MessageBoxFontLatin1 );
icculus@6723
   190
        if ( data->font_struct == NULL ) {
icculus@6723
   191
            SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
icculus@6723
   192
            return -1;
icculus@6723
   193
        }
slouken@6602
   194
    }
slouken@6602
   195
slouken@6603
   196
    if ( messageboxdata->colorScheme ) {
slouken@6603
   197
        colorhints = messageboxdata->colorScheme->colors;
slouken@6603
   198
    } else {
slouken@6603
   199
        colorhints = g_default_colors;
slouken@6603
   200
    }
slouken@6602
   201
slouken@6603
   202
    /* Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. */
slouken@6603
   203
    for ( i = 0; i < SDL_MESSAGEBOX_COLOR_MAX; i++ ) {
slouken@6603
   204
        data->color[ i ] = SDL_MAKE_RGB( colorhints[ i ].r, colorhints[ i ].g, colorhints[ i ].b );
slouken@6603
   205
    }
slouken@6602
   206
slouken@6602
   207
    return 0;
slouken@6602
   208
}
slouken@6602
   209
slouken@6602
   210
/* Calculate and initialize text and button locations. */
slouken@6602
   211
static int
slouken@6602
   212
X11_MessageBoxInitPositions( SDL_MessageBoxDataX11 *data )
slouken@6602
   213
{
slouken@6602
   214
    int i;
slouken@6602
   215
    int ybuttons;
slouken@6602
   216
    int text_width_max = 0;
slouken@6602
   217
    int button_text_height = 0;
slouken@6602
   218
    int button_width = MIN_BUTTON_WIDTH;
slouken@6602
   219
    const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
slouken@6602
   220
slouken@6602
   221
    /* Go over text and break linefeeds into separate lines. */
slouken@6725
   222
    if ( messageboxdata->message && messageboxdata->message[ 0 ] ) {
slouken@6602
   223
        const char *text = messageboxdata->message;
slouken@6602
   224
        TextLineData *plinedata = data->linedata;
slouken@6602
   225
slouken@6725
   226
        for ( i = 0; i < MAX_TEXT_LINES; i++, plinedata++ ) {
slouken@6602
   227
            int height;
slouken@6602
   228
            char *lf = SDL_strchr( ( char * )text, '\n' );
slouken@6602
   229
slouken@6602
   230
            data->numlines++;
slouken@6602
   231
slouken@6602
   232
            /* Only grab length up to lf if it exists and isn't the last line. */
slouken@6602
   233
            plinedata->length = ( lf && ( i < MAX_TEXT_LINES - 1 ) ) ? ( lf - text ) : SDL_strlen( text );
slouken@6602
   234
            plinedata->text = text;
slouken@6602
   235
icculus@6723
   236
            GetTextWidthHeight( data, text, plinedata->length, &plinedata->width, &height );
slouken@6602
   237
slouken@6602
   238
            /* Text and widths are the largest we've ever seen. */
slouken@6602
   239
            data->text_height = IntMax( data->text_height, height );
slouken@6602
   240
            text_width_max = IntMax( text_width_max, plinedata->width );
slouken@6602
   241
slouken@6602
   242
            text += plinedata->length + 1;
slouken@6602
   243
slouken@6602
   244
            /* Break if there are no more linefeeds. */
slouken@6602
   245
            if ( !lf )
slouken@6602
   246
                break;
slouken@6602
   247
        }
slouken@6602
   248
slouken@6602
   249
        /* Bump up the text height slightly. */
slouken@6602
   250
        data->text_height += 2;
slouken@6602
   251
    }
slouken@6602
   252
slouken@6602
   253
    /* Loop through all buttons and calculate the button widths and height. */
slouken@6725
   254
    for ( i = 0; i < data->numbuttons; i++ ) {
slouken@6602
   255
        int height;
slouken@6602
   256
slouken@6602
   257
        data->buttonpos[ i ].buttondata = &data->buttondata[ i ];
slouken@6602
   258
        data->buttonpos[ i ].length = SDL_strlen( data->buttondata[ i ].text );
slouken@6602
   259
icculus@6723
   260
        GetTextWidthHeight( data, data->buttondata[ i ].text, SDL_strlen( data->buttondata[ i ].text ),
slouken@6602
   261
                            &data->buttonpos[ i ].text_width, &height );
slouken@6602
   262
slouken@6602
   263
        button_width = IntMax( button_width, data->buttonpos[ i ].text_width );
slouken@6602
   264
        button_text_height = IntMax( button_text_height, height );
slouken@6602
   265
    }
slouken@6602
   266
slouken@6725
   267
    if ( data->numlines ) {
slouken@6602
   268
        /* x,y for this line of text. */
slouken@6602
   269
        data->xtext = data->text_height;
slouken@6602
   270
        data->ytext = data->text_height + data->text_height;
slouken@6602
   271
slouken@6602
   272
        /* Bump button y down to bottom of text. */
slouken@6602
   273
        ybuttons = 3 * data->ytext / 2 + ( data->numlines - 1 ) * data->text_height;
slouken@6602
   274
slouken@6602
   275
        /* Bump the dialog box width and height up if needed. */
slouken@6602
   276
        data->dialog_width = IntMax( data->dialog_width, 2 * data->xtext + text_width_max );
slouken@6602
   277
        data->dialog_height = IntMax( data->dialog_height, ybuttons );
slouken@6725
   278
    } else {
slouken@6602
   279
        /* Button y starts at height of button text. */
slouken@6602
   280
        ybuttons = button_text_height;
slouken@6602
   281
    }
slouken@6602
   282
slouken@6725
   283
    if ( data->numbuttons ) {
slouken@6602
   284
        int x, y;
slouken@6602
   285
        int width_of_buttons;
slouken@6602
   286
        int button_spacing = button_text_height;
slouken@6602
   287
        int button_height = 2 * button_text_height;
slouken@6602
   288
slouken@6602
   289
        /* Bump button width up a bit. */
slouken@6602
   290
        button_width += button_text_height;
slouken@6602
   291
slouken@6602
   292
        /* Get width of all buttons lined up. */
slouken@6602
   293
        width_of_buttons = data->numbuttons * button_width + ( data->numbuttons - 1 ) * button_spacing;
slouken@6602
   294
slouken@6602
   295
        /* Bump up dialog width and height if buttons are wider than text. */
slouken@6602
   296
        data->dialog_width = IntMax( data->dialog_width, width_of_buttons + 2 * button_spacing );
slouken@6602
   297
        data->dialog_height = IntMax( data->dialog_height, ybuttons + 2 * button_height );
slouken@6602
   298
slouken@6602
   299
        /* Location for first button. */
slouken@6602
   300
        x = ( data->dialog_width - width_of_buttons ) / 2;
slouken@6602
   301
        y = ybuttons + ( data->dialog_height - ybuttons - button_height ) / 2;
slouken@6602
   302
slouken@6725
   303
        for ( i = 0; i < data->numbuttons; i++ ) {
slouken@6602
   304
            /* Button coordinates. */
slouken@6602
   305
            data->buttonpos[ i ].rect.x = x;
slouken@6602
   306
            data->buttonpos[ i ].rect.y = y;
slouken@6602
   307
            data->buttonpos[ i ].rect.w = button_width;
slouken@6602
   308
            data->buttonpos[ i ].rect.h = button_height;
slouken@6602
   309
slouken@6602
   310
            /* Button text coordinates. */
slouken@6602
   311
            data->buttonpos[ i ].x = x + ( button_width - data->buttonpos[ i ].text_width ) / 2;
slouken@6602
   312
            data->buttonpos[ i ].y = y + ( button_height - button_text_height - 1 ) / 2 + button_text_height;
slouken@6602
   313
slouken@6602
   314
            /* Scoot over for next button. */
slouken@6615
   315
            x += button_width + button_spacing;
slouken@6602
   316
        }
slouken@6602
   317
    }
slouken@6602
   318
slouken@6602
   319
    return 0;
slouken@6602
   320
}
slouken@6602
   321
slouken@6602
   322
/* Free SDL_MessageBoxData data. */
slouken@6602
   323
static void
slouken@6602
   324
X11_MessageBoxShutdown( SDL_MessageBoxDataX11 *data )
slouken@6602
   325
{
slouken@6725
   326
    if ( data->font_set != NULL ) {
icculus@6723
   327
        XFreeFontSet( data->display, data->font_set );
icculus@6723
   328
        data->font_set = NULL;
icculus@6723
   329
    }
icculus@6723
   330
slouken@6725
   331
    if ( data->font_struct != NULL ) {
icculus@6723
   332
        XFreeFont( data->display, data->font_struct );
icculus@6723
   333
        data->font_struct = NULL;
slouken@6602
   334
    }
slouken@6602
   335
slouken@6725
   336
    if ( data->display ) {
slouken@6725
   337
        if ( data->window != None ) {
slouken@6602
   338
            XUnmapWindow( data->display, data->window );
slouken@6602
   339
            XDestroyWindow( data->display, data->window );
slouken@6602
   340
            data->window = None;
slouken@6602
   341
        }
slouken@6602
   342
slouken@6602
   343
        XCloseDisplay( data->display );
slouken@6602
   344
        data->display = NULL;
slouken@6602
   345
    }
slouken@6602
   346
}
slouken@6602
   347
slouken@6602
   348
/* Create and set up our X11 dialog box indow. */
slouken@6602
   349
static int
slouken@6602
   350
X11_MessageBoxCreateWindow( SDL_MessageBoxDataX11 *data )
slouken@6602
   351
{
slouken@6602
   352
    int x, y;
slouken@6602
   353
    XSizeHints *sizehints;
slouken@6602
   354
    XSetWindowAttributes wnd_attr;
slouken@6602
   355
    Display *display = data->display;
slouken@6602
   356
    SDL_WindowData *windowdata = NULL;
slouken@6602
   357
    const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
slouken@6602
   358
slouken@6602
   359
    if ( messageboxdata->window ) {
slouken@6602
   360
        windowdata = (SDL_WindowData *)messageboxdata->window->driverdata;
slouken@6602
   361
    }
slouken@6602
   362
slouken@6602
   363
    data->event_mask = ExposureMask |
slouken@6725
   364
                       ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
slouken@6725
   365
                       StructureNotifyMask | FocusChangeMask | PointerMotionMask;
slouken@6603
   366
    wnd_attr.event_mask = data->event_mask;
slouken@6602
   367
slouken@6602
   368
    data->window = XCreateWindow(
slouken@6725
   369
                       display, DefaultRootWindow( display ),
slouken@6725
   370
                       0, 0,
slouken@6725
   371
                       data->dialog_width, data->dialog_height,
slouken@6725
   372
                       0, CopyFromParent, InputOutput, CopyFromParent,
slouken@6725
   373
                       CWEventMask, &wnd_attr );
slouken@6602
   374
    if ( data->window == None ) {
slouken@6602
   375
        SDL_SetError("Couldn't create X window");
slouken@6602
   376
        return -1;
slouken@6602
   377
    }
slouken@6602
   378
slouken@6602
   379
    if ( windowdata ) {
slouken@6602
   380
        /* http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR */
slouken@6602
   381
        XSetTransientForHint( display, data->window, windowdata->xwindow );
slouken@6602
   382
    }
slouken@6602
   383
slouken@6602
   384
    XStoreName( display, data->window, messageboxdata->title );
slouken@6602
   385
slouken@6602
   386
    /* Allow the window to be deleted by the window manager */
slouken@6602
   387
    data->wm_protocols = XInternAtom( display, "WM_PROTOCOLS", False );
slouken@6602
   388
    data->wm_delete_message = XInternAtom( display, "WM_DELETE_WINDOW", False );
slouken@6602
   389
    XSetWMProtocols( display, data->window, &data->wm_delete_message, 1 );
slouken@6602
   390
slouken@6602
   391
    if ( windowdata ) {
slouken@6602
   392
        XWindowAttributes attrib;
slouken@6602
   393
        Window dummy;
slouken@6602
   394
slouken@6602
   395
        XGetWindowAttributes(display, windowdata->xwindow, &attrib);
slouken@6602
   396
        x = attrib.x + ( attrib.width - data->dialog_width ) / 2;
slouken@6602
   397
        y = attrib.y + ( attrib.height - data->dialog_height ) / 3 ;
slouken@6602
   398
        XTranslateCoordinates(display, windowdata->xwindow, DefaultRootWindow( display ), x, y, &x, &y, &dummy);
slouken@6602
   399
    } else {
slouken@6602
   400
        int screen = DefaultScreen( display );
slouken@6602
   401
        x = ( DisplayWidth( display, screen ) - data->dialog_width ) / 2;
slouken@6602
   402
        y = ( DisplayHeight( display, screen ) - data->dialog_height ) / 3 ;
slouken@6602
   403
    }
slouken@6602
   404
    XMoveWindow( display, data->window, x, y );
slouken@6602
   405
slouken@6602
   406
    sizehints = XAllocSizeHints();
slouken@6602
   407
    if ( sizehints ) {
slouken@6602
   408
        sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
slouken@6602
   409
        sizehints->x = x;
slouken@6602
   410
        sizehints->y = y;
slouken@6602
   411
        sizehints->width = data->dialog_width;
slouken@6602
   412
        sizehints->height = data->dialog_height;
slouken@6602
   413
slouken@6602
   414
        sizehints->min_width = sizehints->max_width = data->dialog_width;
slouken@6602
   415
        sizehints->min_height = sizehints->max_height = data->dialog_height;
slouken@6602
   416
slouken@6602
   417
        XSetWMNormalHints( display, data->window, sizehints );
slouken@6602
   418
slouken@6602
   419
        XFree( sizehints );
slouken@6602
   420
    }
slouken@6602
   421
slouken@6602
   422
    XMapRaised( display, data->window );
slouken@6602
   423
    return 0;
slouken@6602
   424
}
slouken@6602
   425
slouken@6602
   426
/* Draw our message box. */
slouken@6602
   427
static void
slouken@6602
   428
X11_MessageBoxDraw( SDL_MessageBoxDataX11 *data, GC ctx )
slouken@6602
   429
{
slouken@6602
   430
    int i;
slouken@6602
   431
    Window window = data->window;
slouken@6602
   432
    Display *display = data->display;
slouken@6602
   433
slouken@6602
   434
    XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ] );
slouken@6602
   435
    XFillRectangle( display, window, ctx, 0, 0, data->dialog_width, data->dialog_height );
slouken@6602
   436
slouken@6602
   437
    XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
slouken@6725
   438
    for ( i = 0; i < data->numlines; i++ ) {
slouken@6602
   439
        TextLineData *plinedata = &data->linedata[ i ];
slouken@6602
   440
icculus@6723
   441
        if (SDL_X11_HAVE_UTF8) {
icculus@6723
   442
            Xutf8DrawString( display, window, data->font_set, ctx,
icculus@6723
   443
                             data->xtext, data->ytext + i * data->text_height,
icculus@6723
   444
                             plinedata->text, plinedata->length );
icculus@6723
   445
        } else {
icculus@6723
   446
            XDrawString( display, window, ctx,
icculus@6723
   447
                         data->xtext, data->ytext + i * data->text_height,
icculus@6723
   448
                         plinedata->text, plinedata->length );
icculus@6723
   449
        }
slouken@6602
   450
    }
slouken@6602
   451
slouken@6725
   452
    for ( i = 0; i < data->numbuttons; i++ ) {
slouken@6602
   453
        SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
slouken@6602
   454
        const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
slouken@6602
   455
        int border = ( buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT ) ? 2 : 0;
slouken@6602
   456
        int offset = ( ( data->mouse_over_index == i ) && ( data->button_press_index == data->mouse_over_index ) ) ? 1 : 0;
slouken@6602
   457
slouken@6602
   458
        XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND ] );
slouken@6602
   459
        XFillRectangle( display, window, ctx,
slouken@6602
   460
                        buttondatax11->rect.x - border, buttondatax11->rect.y - border,
slouken@6602
   461
                        buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border );
slouken@6602
   462
slouken@6602
   463
        XSetForeground( display, ctx, data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_BORDER ] );
slouken@6602
   464
        XDrawRectangle( display, window, ctx,
slouken@6602
   465
                        buttondatax11->rect.x, buttondatax11->rect.y,
slouken@6602
   466
                        buttondatax11->rect.w, buttondatax11->rect.h );
slouken@6602
   467
slouken@6602
   468
        XSetForeground( display, ctx, ( data->mouse_over_index == i ) ?
slouken@6602
   469
                        data->color[ SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED ] :
slouken@6603
   470
                        data->color[ SDL_MESSAGEBOX_COLOR_TEXT ] );
icculus@6723
   471
icculus@6723
   472
        if (SDL_X11_HAVE_UTF8) {
icculus@6723
   473
            Xutf8DrawString( display, window, data->font_set, ctx,
icculus@6723
   474
                             buttondatax11->x + offset,
icculus@6723
   475
                             buttondatax11->y + offset,
icculus@6723
   476
                             buttondata->text, buttondatax11->length );
icculus@6723
   477
        } else {
icculus@6723
   478
            XDrawString( display, window, ctx,
icculus@6723
   479
                         buttondatax11->x + offset, buttondatax11->y + offset,
icculus@6723
   480
                         buttondata->text, buttondatax11->length );
icculus@6723
   481
        }
slouken@6602
   482
    }
slouken@6602
   483
}
slouken@6602
   484
slouken@6602
   485
/* Loop and handle message box event messages until something kills it. */
slouken@6602
   486
static int
slouken@6602
   487
X11_MessageBoxLoop( SDL_MessageBoxDataX11 *data )
slouken@6602
   488
{
slouken@6602
   489
    GC ctx;
slouken@6602
   490
    XGCValues ctx_vals;
slouken@6602
   491
    SDL_bool close_dialog = SDL_FALSE;
slouken@6602
   492
    SDL_bool has_focus = SDL_TRUE;
slouken@6602
   493
    KeySym last_key_pressed = XK_VoidSymbol;
icculus@6723
   494
    unsigned long gcflags = GCForeground | GCBackground;
slouken@6602
   495
icculus@6723
   496
    SDL_zero(ctx_vals);
slouken@6602
   497
    ctx_vals.foreground = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
slouken@6602
   498
    ctx_vals.background = data->color[ SDL_MESSAGEBOX_COLOR_BACKGROUND ];
slouken@6602
   499
icculus@6723
   500
    if (!SDL_X11_HAVE_UTF8) {
icculus@6723
   501
        gcflags |= GCFont;
icculus@6723
   502
        ctx_vals.font = data->font_struct->fid;
icculus@6723
   503
    }
icculus@6723
   504
icculus@6723
   505
    ctx = XCreateGC( data->display, data->window, gcflags, &ctx_vals );
slouken@6602
   506
    if ( ctx == None ) {
slouken@6602
   507
        SDL_SetError("Couldn't create graphics context");
slouken@6602
   508
        return -1;
slouken@6602
   509
    }
slouken@6602
   510
slouken@6602
   511
    data->button_press_index = -1;  /* Reset what button is currently depressed. */
slouken@6602
   512
    data->mouse_over_index = -1;    /* Reset what button the mouse is over. */
slouken@6602
   513
slouken@6602
   514
    while( !close_dialog ) {
slouken@6602
   515
        XEvent e;
slouken@6602
   516
        SDL_bool draw = SDL_TRUE;
slouken@6602
   517
slouken@6603
   518
        XWindowEvent( data->display, data->window, data->event_mask, &e );
slouken@6602
   519
slouken@6602
   520
        /* If XFilterEvent returns True, then some input method has filtered the
slouken@6602
   521
           event, and the client should discard the event. */
slouken@6602
   522
        if ( ( e.type != Expose ) && XFilterEvent( &e, None ) )
slouken@6602
   523
            continue;
slouken@6602
   524
slouken@6602
   525
        switch( e.type ) {
slouken@6602
   526
        case Expose:
slouken@6602
   527
            if ( e.xexpose.count > 0 ) {
slouken@6602
   528
                draw = SDL_FALSE;
slouken@6602
   529
            }
slouken@6602
   530
            break;
slouken@6602
   531
slouken@6602
   532
        case FocusIn:
slouken@6602
   533
            /* Got focus. */
slouken@6602
   534
            has_focus = SDL_TRUE;
slouken@6602
   535
            break;
slouken@6602
   536
slouken@6602
   537
        case FocusOut:
slouken@6602
   538
            /* lost focus. Reset button and mouse info. */
slouken@6602
   539
            has_focus = SDL_FALSE;
slouken@6602
   540
            data->button_press_index = -1;
slouken@6602
   541
            data->mouse_over_index = -1;
slouken@6602
   542
            break;
slouken@6602
   543
slouken@6602
   544
        case MotionNotify:
slouken@6602
   545
            if ( has_focus ) {
slouken@6602
   546
                /* Mouse moved... */
slouken@6602
   547
                data->mouse_over_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
slouken@6602
   548
            }
slouken@6602
   549
            break;
slouken@6602
   550
slouken@6602
   551
        case ClientMessage:
slouken@6602
   552
            if ( e.xclient.message_type == data->wm_protocols &&
slouken@6602
   553
                 e.xclient.format == 32 &&
slouken@6602
   554
                 e.xclient.data.l[ 0 ] == data->wm_delete_message ) {
slouken@6602
   555
                close_dialog = SDL_TRUE;
slouken@6602
   556
            }
slouken@6602
   557
            break;
slouken@6602
   558
slouken@6602
   559
        case KeyPress:
slouken@6602
   560
            /* Store key press - we make sure in key release that we got both. */
slouken@6602
   561
            last_key_pressed = XLookupKeysym( &e.xkey, 0 );
slouken@6602
   562
            break;
slouken@6602
   563
slouken@6725
   564
        case KeyRelease: {
slouken@6602
   565
            Uint32 mask = 0;
slouken@6602
   566
            KeySym key = XLookupKeysym( &e.xkey, 0 );
slouken@6602
   567
slouken@6602
   568
            /* If this is a key release for something we didn't get the key down for, then bail. */
slouken@6602
   569
            if ( key != last_key_pressed )
slouken@6602
   570
                break;
slouken@6602
   571
slouken@6602
   572
            if ( key == XK_Escape )
slouken@6602
   573
                mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
slouken@6602
   574
            else if ( ( key == XK_Return ) || ( key == XK_KP_Enter ) )
slouken@6602
   575
                mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
slouken@6602
   576
slouken@6602
   577
            if ( mask ) {
slouken@6602
   578
                int i;
slouken@6602
   579
slouken@6602
   580
                /* Look for first button with this mask set, and return it if found. */
slouken@6602
   581
                for ( i = 0; i < data->numbuttons; i++ ) {
slouken@6602
   582
                    SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ i ];
slouken@6602
   583
slouken@6602
   584
                    if ( buttondatax11->buttondata->flags & mask ) {
slouken@6602
   585
                        *data->pbuttonid = buttondatax11->buttondata->buttonid;
slouken@6602
   586
                        close_dialog = SDL_TRUE;
slouken@6602
   587
                        break;
slouken@6602
   588
                    }
slouken@6602
   589
                }
slouken@6602
   590
            }
slouken@6602
   591
            break;
slouken@6602
   592
        }
slouken@6602
   593
slouken@6602
   594
        case ButtonPress:
slouken@6602
   595
            data->button_press_index = -1;
slouken@6602
   596
            if ( e.xbutton.button == Button1 ) {
slouken@6602
   597
                /* Find index of button they clicked on. */
slouken@6602
   598
                data->button_press_index = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
slouken@6602
   599
            }
slouken@6602
   600
            break;
slouken@6602
   601
slouken@6602
   602
        case ButtonRelease:
slouken@6602
   603
            /* If button is released over the same button that was clicked down on, then return it. */
slouken@6602
   604
            if ( ( e.xbutton.button == Button1 ) && ( data->button_press_index >= 0 ) ) {
slouken@6602
   605
                int button = GetHitButtonIndex( data, e.xbutton.x, e.xbutton.y );
slouken@6602
   606
slouken@6602
   607
                if ( data->button_press_index == button ) {
slouken@6602
   608
                    SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[ button ];
slouken@6602
   609
slouken@6602
   610
                    *data->pbuttonid = buttondatax11->buttondata->buttonid;
slouken@6602
   611
                    close_dialog = SDL_TRUE;
slouken@6602
   612
                }
slouken@6602
   613
            }
slouken@6602
   614
            data->button_press_index = -1;
slouken@6602
   615
            break;
slouken@6602
   616
        }
slouken@6602
   617
slouken@6602
   618
        if ( draw ) {
slouken@6602
   619
            /* Draw our dialog box. */
slouken@6602
   620
            X11_MessageBoxDraw( data, ctx );
slouken@6602
   621
        }
slouken@6602
   622
    }
slouken@6602
   623
slouken@6602
   624
    XFreeGC( data->display, ctx );
slouken@6602
   625
    return 0;
slouken@6602
   626
}
slouken@6602
   627
slouken@6602
   628
/* Display an x11 message box. */
slouken@6602
   629
int
slouken@6602
   630
X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
slouken@6602
   631
{
slouken@6602
   632
    int ret;
slouken@6602
   633
    SDL_MessageBoxDataX11 data;
icculus@6724
   634
    char *origlocale;
slouken@6602
   635
icculus@6723
   636
    SDL_zero(data);
icculus@6723
   637
slouken@6602
   638
    if ( !SDL_X11_LoadSymbols() )
slouken@6602
   639
        return -1;
slouken@6602
   640
icculus@6724
   641
    origlocale = setlocale(LC_ALL, NULL);
icculus@6724
   642
    if (origlocale != NULL) {
icculus@6724
   643
        origlocale = SDL_strdup(origlocale);
icculus@6724
   644
        if (origlocale == NULL) {
icculus@6724
   645
            SDL_OutOfMemory();
icculus@6724
   646
            return -1;
icculus@6724
   647
        }
icculus@6724
   648
        setlocale(LC_ALL, "");
icculus@6724
   649
    }
icculus@6724
   650
slouken@6602
   651
    /* This code could get called from multiple threads maybe? */
slouken@6602
   652
    XInitThreads();
slouken@6602
   653
slouken@6602
   654
    /* Initialize the return buttonid value to -1 (for error or dialogbox closed). */
slouken@6602
   655
    *buttonid = -1;
slouken@6602
   656
slouken@6602
   657
    /* Init and display the message box. */
slouken@6602
   658
    ret = X11_MessageBoxInit( &data, messageboxdata, buttonid );
slouken@6602
   659
    if ( ret != -1 ) {
slouken@6602
   660
        ret = X11_MessageBoxInitPositions( &data );
slouken@6602
   661
        if ( ret != -1 ) {
slouken@6602
   662
            ret = X11_MessageBoxCreateWindow( &data );
slouken@6602
   663
            if ( ret != -1 ) {
slouken@6602
   664
                ret = X11_MessageBoxLoop( &data );
slouken@6602
   665
            }
slouken@6602
   666
        }
slouken@6602
   667
    }
slouken@6602
   668
slouken@6602
   669
    X11_MessageBoxShutdown( &data );
icculus@6724
   670
icculus@6724
   671
    if (origlocale) {
icculus@6724
   672
        setlocale(LC_ALL, origlocale);
icculus@6724
   673
        SDL_free(origlocale);
icculus@6724
   674
    }
icculus@6724
   675
slouken@6602
   676
    return ret;
slouken@6602
   677
}
slouken@6602
   678
slouken@6602
   679
#endif /* SDL_VIDEO_DRIVER_X11 */
slouken@6602
   680
slouken@6602
   681
/* vi: set ts=4 sw=4 expandtab: */