diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c index dd4a1bf6913f6..36783d044fec4 100644 --- a/src/video/x11/SDL_x11messagebox.c +++ b/src/video/x11/SDL_x11messagebox.c @@ -23,22 +23,13 @@ #ifdef SDL_VIDEO_DRIVER_X11 -#include "SDL_x11video.h" -#include "SDL_x11dyn.h" #include "SDL_x11messagebox.h" - -#include -#include +#include "SDL_x11toolkit.h" #ifndef SDL_FORK_MESSAGEBOX #define SDL_FORK_MESSAGEBOX 1 #endif -#define SDL_SET_LOCALE 1 -#define SDL_DIALOG_ELEMENT_PADDING 4 -#define SDL_DIALOG_ELEMENT_PADDING_2 12 -#define SDL_DIALOG_ELEMENT_PADDING_3 8 - #if SDL_FORK_MESSAGEBOX #include #include @@ -46,1185 +37,159 @@ #include #endif -#define MAX_BUTTONS 8 // Maximum number of buttons supported -#define MIN_BUTTON_WIDTH 32 // Minimum button width -#define MIN_DIALOG_WIDTH 10 // Minimum dialog width -#define MIN_DIALOG_HEIGHT 10 // Minimum dialog height - -static const char g_MessageBoxFontLatin1[] = - "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1"; - -static const char *g_MessageBoxFont[] = { - "-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) - "-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) - "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems) - "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode. - "-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. - "-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. - "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems) - "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1. - NULL -}; - -static const char *g_IconFont = "-*-*-bold-r-normal-*-18-*-*-*-*-*-iso8859-1[33 88 105]"; - -static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { - { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, - { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_TEXT, - { 127, 120, 127 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, - { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, - { 235, 235, 235 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, -}; - -typedef struct SDL_MessageBoxButtonDataX11 +typedef struct SDL_MessageBoxCallbackDataX11 { - int length; // Text length - int text_a; - int text_d; - - SDL_Rect text_rect; - SDL_Rect rect; // Rectangle for entire button + int *buttonID; + SDL_ToolkitWindowX11 *window; +} SDL_MessageBoxCallbackDataX11; - const SDL_MessageBoxButtonData *buttondata; // Button data from caller -} SDL_MessageBoxButtonDataX11; - -typedef struct TextLineData +static void X11_MessageBoxButtonCallback(SDL_ToolkitControlX11 *control, void *data) { - int length; // String length of this text line - const char *text; // Text for this line - SDL_Rect rect; -} TextLineData; - -typedef struct SDL_MessageBoxDataX11 -{ - Display *display; - int screen; - Window window; - Visual *visual; - Colormap cmap; -#ifdef SDL_VIDEO_DRIVER_X11_XDBE - XdbeBackBuffer buf; - bool xdbe; // Whether Xdbe is present or not -#endif - long event_mask; - Atom wm_protocols; - Atom wm_delete_message; -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - bool xrandr; // Whether Xrandr is present or not -#endif - - 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 numlines; // Count of Text lines. - int text_height; // Height for text lines. - TextLineData *linedata; - - char icon_char; // Icon, '\0' indicates that the messsage box has no icon. - XFontStruct *icon_char_font; - SDL_Rect icon_box_rect; - int icon_char_x; - int icon_char_y; + SDL_MessageBoxCallbackDataX11 *cbdata; - 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]; - - /* Colors for rendering widgets */ - XColor xcolor[SDL_MESSAGEBOX_COLOR_COUNT]; - XColor xcolor_bevel_l1; - XColor xcolor_bevel_l2; - XColor xcolor_bevel_d; - XColor xcolor_pressed; - - /* Colors for rendering icons */ - XColor xcolor_black; - XColor xcolor_red; - XColor xcolor_red_darker; - XColor xcolor_white; - XColor xcolor_yellow; - XColor xcolor_blue; - XColor xcolor_bg_shadow; - - const SDL_MessageBoxData *messageboxdata; -} SDL_MessageBoxDataX11; - -// Int helpers -static SDL_INLINE int IntMax(int a, int b) -{ - return (a > b) ? a : b; -} - -static SDL_INLINE int IntClamp(int a, int b, int c) -{ - if (a < b) return b; - if (a > c) return c; - return a; -} - -static void GetTextWidthHeightForFont(SDL_MessageBoxDataX11 *data, XFontStruct *font, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent) -{ - XCharStruct text_structure; - int font_direction, font_descent; - X11_XTextExtents(font, str, nbytes, - &font_direction, font_ascent, &font_descent, - &text_structure); - *pwidth = text_structure.width; - *pheight = text_structure.ascent + text_structure.descent; + cbdata = (SDL_MessageBoxCallbackDataX11 *)data; + *cbdata->buttonID = X11Toolkit_GetButtonControlData(control)->buttonID; + X11Toolkit_SignalWindowClose(cbdata->window); } -// Return width and height for a string. -static void GetTextWidthHeight(SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent, int *font_descent) -{ -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - XFontSetExtents *extents; - XRectangle overall_ink, overall_logical; - extents = X11_XExtentsOfFontSet(data->font_set); - X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); - *pwidth = overall_logical.width; - *pheight = overall_logical.height; - *font_ascent = -extents->max_logical_extent.y; - *font_descent = extents->max_logical_extent.height - *font_ascent; - } else -#endif - { - XCharStruct text_structure; - int font_direction; - 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 bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid) +static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID) { - int i; - int numbuttons = messageboxdata->numbuttons; - const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons; + SDL_ToolkitWindowX11 *window; + SDL_ToolkitControlX11 *icon; + SDL_ToolkitControlX11 fake_icon; + SDL_ToolkitControlX11 *message; + SDL_ToolkitControlX11 **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; - - // Convert flags to icon character - switch (data->messageboxdata->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { - case SDL_MESSAGEBOX_ERROR: - data->icon_char = 'X'; - break; - case SDL_MESSAGEBOX_WARNING: - data->icon_char = '!'; - break; - case SDL_MESSAGEBOX_INFORMATION: - data->icon_char = 'i'; - break; - default: - data->icon_char = '\0'; - } - - data->display = X11_XOpenDisplay(NULL); - if (!data->display) { - return SDL_SetError("Couldn't open X11 display"); - } - -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - int xrandr_event_base, xrandr_error_base; - data->xrandr = X11_XRRQueryExtension(data->display, &xrandr_event_base, &xrandr_error_base); -#endif + SDL_MessageBoxCallbackDataX11 data; + int max_button_w; + int max_button_h; + int total_button_w; + int total_text_and_icon_w; + int w; + int h; + int i; + int t; -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - char **missing = NULL; - int num_missing = 0; - int i_font; - for (i_font = 0; g_MessageBoxFont[i_font]; ++i_font) { - data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont[i_font], - &missing, &num_missing, NULL); - if (missing) { - X11_XFreeStringList(missing); - } - if (data->font_set) { - break; - } - } - if (!data->font_set) { - return SDL_SetError("Couldn't load x11 message box font"); - } - } else -#endif - { - data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1); - if (!data->font_struct) { - return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1); - } - } - - if (data->icon_char != '\0') { - data->icon_char_font = X11_XLoadQueryFont(data->display, g_IconFont); - if (!data->icon_char_font) { - data->icon_char_font = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1); - if (!data->icon_char_font) { - data->icon_char = '\0'; - } - } - } + /* Init vars */ + max_button_w = 50; + max_button_h = 0; + w = h = 2; + i = t = total_button_w = total_text_and_icon_w = 0; + /* Color scheme */ if (messageboxdata->colorScheme) { colorhints = messageboxdata->colorScheme->colors; } else { - colorhints = g_default_colors; - } - - // Convert colors to 16 bpc XColor format - for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { - data->xcolor[i].flags = DoRed|DoGreen|DoBlue; - data->xcolor[i].red = colorhints[i].r * 257; - data->xcolor[i].green = colorhints[i].g * 257; - data->xcolor[i].blue = colorhints[i].b * 257; + colorhints = NULL; } - /* Generate bevel and pressed colors */ - data->xcolor_bevel_l1.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_l1.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 12500, 0, 65535); - data->xcolor_bevel_l1.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 12500, 0, 65535); - data->xcolor_bevel_l1.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 12500, 0, 65535); - - data->xcolor_bevel_l2.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_l2.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 32500, 0, 65535); - data->xcolor_bevel_l2.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 32500, 0, 65535); - data->xcolor_bevel_l2.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 32500, 0, 65535); - - data->xcolor_bevel_d.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bevel_d.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red - 22500, 0, 65535); - data->xcolor_bevel_d.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green - 22500, 0, 65535); - data->xcolor_bevel_d.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue - 22500, 0, 65535); - - data->xcolor_pressed.flags = DoRed|DoGreen|DoBlue; - data->xcolor_pressed.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].red - 12500, 0, 65535); - data->xcolor_pressed.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].green - 12500, 0, 65535); - data->xcolor_pressed.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].blue - 12500, 0, 65535); - - /* Icon colors */ - if (data->icon_char != '\0') { - data->xcolor_black.flags = DoRed|DoGreen|DoBlue; - data->xcolor_black.red = 0; - data->xcolor_black.green = 0; - data->xcolor_black.blue = 0; - - data->xcolor_black.flags = DoRed|DoGreen|DoBlue; - data->xcolor_white.red = 65535; - data->xcolor_white.green = 65535; - data->xcolor_white.blue = 65535; - - data->xcolor_red.flags = DoRed|DoGreen|DoBlue; - data->xcolor_red.red = 65535; - data->xcolor_red.green = 0; - data->xcolor_red.blue = 0; - - data->xcolor_red_darker.flags = DoRed|DoGreen|DoBlue; - data->xcolor_red_darker.red = 40535; - data->xcolor_red_darker.green = 0; - data->xcolor_red_darker.blue = 0; - - data->xcolor_yellow.flags = DoRed|DoGreen|DoBlue; - data->xcolor_yellow.red = 65535; - data->xcolor_yellow.green = 65535; - data->xcolor_yellow.blue = 0; - - data->xcolor_blue.flags = DoRed|DoGreen|DoBlue; - data->xcolor_blue.red = 0; - data->xcolor_blue.green = 0; - data->xcolor_blue.blue = 65535; - - data->xcolor_bg_shadow.flags = DoRed|DoGreen|DoBlue; - data->xcolor_bg_shadow.red = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); - data->xcolor_bg_shadow.green = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); - data->xcolor_bg_shadow.blue = IntClamp(data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); + /* Create window */ + window = X11Toolkit_CreateWindowStruct(messageboxdata->window, colorhints); + if (!window) { + return false; } - return true; -} - -static int CountLinesOfText(const char *text) -{ - int result = 0; - while (text && *text) { - const char *lf = SDL_strchr(text, '\n'); - result++; // even without an endline, this counts as a line. - text = lf ? lf + 1 : NULL; + /* Create controls */ + buttons = SDL_calloc(messageboxdata->numbuttons, sizeof(SDL_ToolkitControlX11 *)); + icon = X11Toolkit_CreateIconControl(window, messageboxdata->flags); + message = X11Toolkit_CreateLabelControl(window, (char *)messageboxdata->message); + data.buttonID = buttonID; + data.window = window; + for (i = 0; i < messageboxdata->numbuttons; i++) { + buttons[i] = X11Toolkit_CreateButtonControl(window, &messageboxdata->buttons[i]); + X11Toolkit_RegisterCallbackForButtonControl(buttons[i], &data, X11_MessageBoxButtonCallback); + max_button_w = SDL_max(max_button_w, buttons[i]->rect.w); + max_button_h = SDL_max(max_button_h, buttons[i]->rect.h); + buttons[i]->rect.x = 0; } - return result; -} - -// Calculate and initialize text and button locations. -static bool X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data) -{ - int paddingx2; - int padding2x2; - int text_width_max; - int text_height_total; - int button_height_max; - int button_width_max; - int button_width_total; - int elems_total_height; - int text_ix; - int i; - int t; - const SDL_MessageBoxData *messageboxdata = data->messageboxdata; - - /* Optimization and initialization */ - text_height_total = 0; - text_width_max = 0; - paddingx2 = SDL_DIALOG_ELEMENT_PADDING * 2; - padding2x2 = SDL_DIALOG_ELEMENT_PADDING_2 * 2; - data->icon_char_y = 0; - data->icon_box_rect.w = 0; - data->icon_box_rect.h = 0; - text_ix = 0; - t = 0; - button_width_max = MIN_BUTTON_WIDTH; - button_height_max = 0; - - /* Calculate icon sizing */ - if (data->icon_char != '\0') { - int icon_char_w; - int icon_char_h; - int icon_char_a; - int icon_char_max; - - GetTextWidthHeightForFont(data, data->icon_char_font, &data->icon_char, 1, &icon_char_w, &icon_char_h, &icon_char_a); - data->icon_box_rect.w = icon_char_w + paddingx2; - data->icon_box_rect.h = icon_char_h + paddingx2; - icon_char_max = IntMax(data->icon_box_rect.w, data->icon_box_rect.h) + 2; - data->icon_box_rect.w = icon_char_max; - data->icon_box_rect.h = icon_char_max; - data->icon_box_rect.y = 0; - data->icon_box_rect.x = 0; - data->icon_char_y = icon_char_a + data->icon_box_rect.y + (data->icon_box_rect.h - icon_char_h)/2 + 1; - data->icon_char_x = data->icon_box_rect.x + (data->icon_box_rect.w - icon_char_w)/2 + 1; - data->icon_box_rect.w += 2; - data->icon_box_rect.h += 2; - } - - // Go over text and break linefeeds into separate lines. - if (messageboxdata && messageboxdata->message[0]) { - int iascent; - const char *text = messageboxdata->message; - const int linecount = CountLinesOfText(text); - TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount); - - if (!plinedata) { - return false; - } - - data->linedata = plinedata; - data->numlines = linecount; - iascent = 0; - - for (i = 0; i < linecount; i++) { - const char *lf = SDL_strchr(text, '\n'); - const int length = lf ? (lf - text) : SDL_strlen(text); - int ascent; - int descent; - - plinedata[i].text = text; - - GetTextWidthHeight(data, text, length, &plinedata[i].rect.w, &plinedata[i].rect.h, &ascent, &descent); - - // Text widths are the largest we've ever seen. - text_width_max = IntMax(text_width_max, plinedata[i].rect.w); - - plinedata[i].length = length; - if (lf && (lf > text) && (lf[-1] == '\r')) { - plinedata[i].length--; - } - - if (i > 0) { - plinedata[i].rect.y = ascent + descent + plinedata[i-1].rect.y; - } else { - plinedata[i].rect.y = data->icon_box_rect.y + SDL_DIALOG_ELEMENT_PADDING + ascent; - iascent = ascent; - } - plinedata[i].rect.x = text_ix = data->icon_box_rect.x + data->icon_box_rect.w + SDL_DIALOG_ELEMENT_PADDING_2; - text += length + 1; - - // Break if there are no more linefeeds. - if (!lf) { - break; - } - } - - text_height_total = plinedata[linecount-1].rect.y + plinedata[linecount-1].rect.h - iascent - data->icon_box_rect.y - SDL_DIALOG_ELEMENT_PADDING; + if (icon) { + icon->rect.x = icon->rect.y = 0; } - - // Loop through all buttons and calculate the button widths and height. - for (i = 0; i < data->numbuttons; i++) { - 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_rect.w, &data->buttonpos[i].text_rect.h, &data->buttonpos[i].text_a, &data->buttonpos[i].text_d); - - button_height_max = IntMax(button_height_max, (data->buttonpos[i].text_rect.h + SDL_DIALOG_ELEMENT_PADDING_3 * 2)); - button_width_max = IntMax(button_width_max, (data->buttonpos[i].text_rect.w + padding2x2)); - - } - button_width_total = button_width_max * data->numbuttons + SDL_DIALOG_ELEMENT_PADDING * (data->numbuttons - 1); - button_width_total += padding2x2; - - /* Dialog width */ - if (button_width_total < (text_ix + text_width_max + padding2x2)) { - data->dialog_width = IntMax(data->dialog_width, (text_ix + text_width_max + padding2x2)); + + /* Positioning and sizing */ + if (icon) { + message->rect.x = SDL_TOOLKIT_X11_ELEMENT_PADDING_2 + icon->rect.x + icon->rect.w; + message->rect.y = X11Toolkit_GetIconControlCharY(icon); } else { - data->dialog_width = IntMax(data->dialog_width, button_width_total); + message->rect.x = 0; + message->rect.y = -2; + icon = &fake_icon; + icon->rect.w = 0; + icon->rect.h = 0; + icon->rect.x = 0; + icon->rect.y = 0; } - - /* X position of text and icon */ - t = (data->dialog_width - (text_ix + text_width_max))/2; - if (data->icon_char != '\0') { - data->icon_box_rect.y = 0; - if (data->numlines) { - data->icon_box_rect.x += t; - data->icon_char_x += t; - } else { - int tt; - - tt = t; - t = (data->dialog_width - data->icon_box_rect.w)/2; - data->icon_box_rect.x += t; - data->icon_char_x += t; - t = tt; - } - } - for (i = 0; i < data->numlines; i++) { - data->linedata[i].rect.x += t; - } - - /* Button poistioning */ - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].rect.w = button_width_max; - data->buttonpos[i].text_rect.x = (data->buttonpos[i].rect.w - data->buttonpos[i].text_rect.w)/2; - data->buttonpos[i].rect.h = button_height_max; - data->buttonpos[i].text_rect.y = data->buttonpos[i].text_a + (data->buttonpos[i].rect.h - data->buttonpos[i].text_rect.h)/2; - if (i > 0) { - data->buttonpos[i].rect.x += data->buttonpos[i-1].rect.x + data->buttonpos[i-1].rect.w + SDL_DIALOG_ELEMENT_PADDING_3; - data->buttonpos[i].text_rect.x += data->buttonpos[i].rect.x; - } - } - button_width_total = data->buttonpos[data->numbuttons-1].rect.x + data->buttonpos[data->numbuttons-1].rect.w; - data->dialog_width = IntMax(data->dialog_width, (button_width_total + padding2x2)); if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].rect.x += (data->dialog_width - button_width_total)/2; - data->buttonpos[i].text_rect.x += (data->dialog_width - button_width_total)/2; - if (data->icon_box_rect.h > text_height_total) { - data->buttonpos[i].text_rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2; + for (i = messageboxdata->numbuttons; i != -1; i--) { + buttons[i]->rect.w = max_button_w; + buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyUpsize(buttons[i]); + + if (icon->rect.h > message->rect.h) { + buttons[i]->rect.y = icon->rect.h + SDL_TOOLKIT_X11_ELEMENT_PADDING_2; } else { - int a; - - a = 0; - if (data->numlines) { - a = data->linedata[data->numlines - 1].rect.y + data->linedata[data->numlines - 1].rect.h; - } - data->buttonpos[i].text_rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2; + buttons[i]->rect.y = message->rect.h + SDL_TOOLKIT_X11_ELEMENT_PADDING_2; + } + + if (i) { + buttons[i]->rect.x = buttons[i-1]->rect.x + buttons[i-1]->rect.w + SDL_TOOLKIT_X11_ELEMENT_PADDING_3; } - } + } } else { - for (i = data->numbuttons; i != -1; i--) { - data->buttonpos[i].rect.x += (data->dialog_width - button_width_total)/2; - data->buttonpos[i].text_rect.x += (data->dialog_width - button_width_total)/2; - if (data->icon_box_rect.h > text_height_total) { - data->buttonpos[i].text_rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += data->icon_box_rect.h + SDL_DIALOG_ELEMENT_PADDING_2; + for (i = 0; i < messageboxdata->numbuttons; i++) { + buttons[i]->rect.w = max_button_w; + buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyUpsize(buttons[i]); + + if (icon->rect.h > message->rect.h) { + buttons[i]->rect.y = icon->rect.h + SDL_TOOLKIT_X11_ELEMENT_PADDING_2; } else { - int a; - - a = 0; - if (data->numlines) { - a = data->linedata[data->numlines - 1].rect.y + data->linedata[data->numlines - 1].rect.h; - } - data->buttonpos[i].text_rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2 - 2; - data->buttonpos[i].rect.y += a + SDL_DIALOG_ELEMENT_PADDING_2; + buttons[i]->rect.y = message->rect.h + SDL_TOOLKIT_X11_ELEMENT_PADDING_2; + } + + if (i) { + buttons[i]->rect.x = buttons[i-1]->rect.x + buttons[i-1]->rect.w + SDL_TOOLKIT_X11_ELEMENT_PADDING_3; } } } - - /* Dialog height */ - elems_total_height = data->buttonpos[data->numbuttons-1].rect.y + data->buttonpos[data->numbuttons-1].rect.h; - data->dialog_height = IntMax(data->dialog_height, (elems_total_height + padding2x2)); - t = (data->dialog_height - elems_total_height)/2; - if (data->icon_char != '\0') { - data->icon_box_rect.y += t; - data->icon_char_y += t; - data->icon_box_rect.w -= 2; - data->icon_box_rect.h -= 2; - } - for (i = 0; i < data->numbuttons; i++) { - data->buttonpos[i].text_rect.y += t; - data->buttonpos[i].rect.y += t; - } - for (i = 0; i < data->numlines; i++) { - data->linedata[i].rect.y += t; - } - return true; -} - -// Free SDL_MessageBoxData data. -static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data) -{ - if (data->font_set) { - X11_XFreeFontSet(data->display, data->font_set); - data->font_set = NULL; - } - - if (data->font_struct) { - X11_XFreeFont(data->display, data->font_struct); - data->font_struct = NULL; - } - - if (data->icon_char != '\0') { - X11_XFreeFont(data->display, data->icon_char_font); - data->icon_char_font = NULL; - } - -#ifdef 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; - } - - SDL_free(data->linedata); -} - -// Create and set up our X11 dialog box indow. -static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data) -{ - int x, y, i; - XSizeHints *sizehints; - XSetWindowAttributes wnd_attr; - Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG; - Display *display = data->display; - SDL_WindowData *windowdata = NULL; - const SDL_MessageBoxData *messageboxdata = data->messageboxdata; -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR -#ifdef XRANDR_DISABLED_BY_DEFAULT - const bool use_xrandr_by_default = false; -#else - const bool use_xrandr_by_default = true; -#endif -#endif - - if (messageboxdata->window) { - SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window); - windowdata = messageboxdata->window->internal; - data->screen = displaydata->screen; + total_button_w = buttons[messageboxdata->numbuttons-1]->rect.x + buttons[messageboxdata->numbuttons-1]->rect.w; + total_text_and_icon_w = message->rect.x + message->rect.w; + if (total_button_w > total_text_and_icon_w) { + w = total_button_w; } else { - data->screen = DefaultScreen(display); - } - - data->visual = DefaultVisual(display, data->screen); - data->cmap = DefaultColormap(display, data->screen); - for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { - X11_XAllocColor(display, data->cmap, &data->xcolor[i]); - } - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_l1); - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_l2); - X11_XAllocColor(display, data->cmap, &data->xcolor_bevel_d); - X11_XAllocColor(display, data->cmap, &data->xcolor_pressed); - if (data->icon_char != '\0') { - X11_XAllocColor(display, data->cmap, &data->xcolor_black); - X11_XAllocColor(display, data->cmap, &data->xcolor_white); - X11_XAllocColor(display, data->cmap, &data->xcolor_red); - X11_XAllocColor(display, data->cmap, &data->xcolor_red_darker); - X11_XAllocColor(display, data->cmap, &data->xcolor_yellow); - X11_XAllocColor(display, data->cmap, &data->xcolor_blue); - X11_XAllocColor(display, data->cmap, &data->xcolor_bg_shadow); - } - - data->event_mask = ExposureMask | - ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | - StructureNotifyMask | FocusChangeMask | PointerMotionMask; - wnd_attr.event_mask = data->event_mask; - wnd_attr.colormap = data->cmap; - - data->window = X11_XCreateWindow( - display, RootWindow(display, data->screen), - 0, 0, - data->dialog_width, data->dialog_height, - 0, DefaultDepth(display, data->screen), InputOutput, data->visual, - CWEventMask | CWColormap, &wnd_attr); - if (data->window == None) { - return SDL_SetError("Couldn't create X window"); + w = total_text_and_icon_w; } - - if (windowdata) { - Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False); - Atom stateatoms[16]; - size_t statecount = 0; - // Set some message-boxy window states when attached to a parent window... - // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False); - stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False); - SDL_assert(statecount <= SDL_arraysize(stateatoms)); - X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32, - PropModeReplace, (unsigned char *)stateatoms, statecount); - - // http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR - X11_XSetTransientForHint(display, data->window, windowdata->xwindow); - } - - SDL_X11_SetWindowTitle(display, data->window, (char *)messageboxdata->title); - - // 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_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); - X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); - - data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); - - 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); + w += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * 2; + if (message->rect.h > icon->rect.h) { + h = message->rect.h; } else { - 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 = dpy->internal; - x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2); - y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3); - } -#ifdef SDL_VIDEO_DRIVER_X11_XRANDR - else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default) && data->xrandr) { - XRRScreenResources *screen = X11_XRRGetScreenResourcesCurrent(display, DefaultRootWindow(display)); - if (!screen) { - goto XRANDRBAIL; - } - if (!screen->ncrtc) { - goto XRANDRBAIL; - } - - XRRCrtcInfo *crtc_info = X11_XRRGetCrtcInfo(display, screen, screen->crtcs[0]); - if (crtc_info) { - x = (crtc_info->width - data->dialog_width) / 2; - y = (crtc_info->height - data->dialog_height) / 3; - } else { - goto XRANDRBAIL; - } - } -#endif - else { - // oh well. This will misposition on a multi-head setup. Init first next time. - XRANDRBAIL: - x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2; - y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3; - } - } - 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); - -#ifdef 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 = true; - data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined); - } else { - data->xdbe = false; - } + h = icon->rect.h; + } + h += max_button_h + SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * 3; + t = (w - total_text_and_icon_w) / 2; + icon->rect.x += t; + message->rect.x += t; + icon->rect.y += SDL_TOOLKIT_X11_ELEMENT_PADDING_2; + message->rect.y += SDL_TOOLKIT_X11_ELEMENT_PADDING_2; + t = (w - total_button_w) / 2; + for (i = 0; i < messageboxdata->numbuttons; i++) { + buttons[i]->rect.x += t; + buttons[i]->rect.y += SDL_TOOLKIT_X11_ELEMENT_PADDING_2; + } + if (!messageboxdata->message) { + icon->rect.x = (w - icon->rect.w)/2; + } + + /* Actually create window, do event loop, cleanup */ + X11Toolkit_CreateWindowRes(window, w, h, (char *)messageboxdata->title); + X11Toolkit_DoWindowEventLoop(window); + X11Toolkit_DestroyWindow(window); + if (buttons) { + SDL_free(buttons); } -#endif - return true; } -// Draw our message box. -static void X11_MessageBoxDraw(SDL_MessageBoxDataX11 *data, GC ctx, bool utf8) -{ - int i; - Drawable window = data->window; - Display *display = data->display; - -#ifdef 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->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, 0, 0, data->dialog_width, data->dialog_height); - - if(data->icon_char != '\0') { - X11_XSetForeground(display, ctx, data->xcolor_bg_shadow.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x + 2, data->icon_box_rect.y + 2, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - switch (data->messageboxdata->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { - case SDL_MESSAGEBOX_ERROR: - X11_XSetForeground(display, ctx, data->xcolor_red_darker.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_red.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - break; - case SDL_MESSAGEBOX_WARNING: - X11_XSetForeground(display, ctx, data->xcolor_black.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_yellow.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_black.pixel); - break; - case SDL_MESSAGEBOX_INFORMATION: - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_blue.pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x+1, data->icon_box_rect.y+1, data->icon_box_rect.w-2, data->icon_box_rect.h-2, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor_white.pixel); - break; - default: - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - X11_XFillArc(display, window, ctx, data->icon_box_rect.x, data->icon_box_rect.y, data->icon_box_rect.w, data->icon_box_rect.h, 0, 360 * 64); - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); - } - X11_XSetFont(display, ctx, data->icon_char_font->fid); - X11_XDrawString(display, window, ctx, data->icon_char_x, data->icon_char_y, &data->icon_char, 1); - if (!utf8) { - X11_XSetFont(display, ctx, data->font_struct->fid); - } - } - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - for (i = 0; i < data->numlines; i++) { - TextLineData *plinedata = &data->linedata[i]; - -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - X11_Xutf8DrawString(display, window, data->font_set, ctx, - plinedata->rect.x, plinedata->rect.y, - plinedata->text, plinedata->length); - } else -#endif - { - X11_XDrawString(display, window, ctx, - plinedata->rect.x, plinedata->rect.y, - plinedata->text, plinedata->length); - } - } - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); - for (i = 0; i < data->numbuttons; i++) { - SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i]; - const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata; - - /* Draw bevel */ - if (data->button_press_index == i) { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w - 1, buttondatax11->rect.h - 1); - - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 2); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, data->xcolor_pressed.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - } else { - if (buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 5, buttondatax11->rect.h - 5); - - X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 3, buttondatax11->rect.y + 3, - buttondatax11->rect.w - 6, buttondatax11->rect.h - 6); - } else { - X11_XSetForeground(display, ctx, data->xcolor_bevel_d.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w, buttondatax11->rect.h); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l2.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x, buttondatax11->rect.y, - buttondatax11->rect.w - 1, buttondatax11->rect.h - 1); - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 2, buttondatax11->rect.h - 2); - - X11_XSetForeground(display, ctx, data->xcolor_bevel_l1.pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 1, buttondatax11->rect.y + 1, - buttondatax11->rect.w - 3, buttondatax11->rect.h - 3); - - X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : data->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); - X11_XFillRectangle(display, window, ctx, - buttondatax11->rect.x + 2, buttondatax11->rect.y + 2, - buttondatax11->rect.w - 4, buttondatax11->rect.h - 4); - } - } - - X11_XSetForeground(display, ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); -#ifdef X_HAVE_UTF8_STRING - if (SDL_X11_HAVE_UTF8) { - X11_Xutf8DrawString(display, window, data->font_set, ctx, - buttondatax11->text_rect.x, - buttondatax11->text_rect.y, - buttondata->text, buttondatax11->length); - } else -#endif - { - X11_XDrawString(display, window, ctx, - buttondatax11->text_rect.x, buttondatax11->text_rect.y, - buttondata->text, buttondatax11->length); - } - } - -#ifdef 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 -} - -// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef -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 bool X11_MessageBoxLoop(SDL_MessageBoxDataX11 *data) -{ - GC ctx; - XGCValues ctx_vals; - bool close_dialog = false; - bool has_focus = true; - KeySym last_key_pressed = XK_VoidSymbol; - unsigned long gcflags = GCForeground | GCBackground; -#ifdef X_HAVE_UTF8_STRING - const int have_utf8 = SDL_X11_HAVE_UTF8; -#else - const int have_utf8 = 0; -#endif - - SDL_zero(ctx_vals); - ctx_vals.foreground = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; - ctx_vals.background = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; - - if (!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; - bool draw = 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 = false; - } - break; - - case FocusIn: - // Got focus. - has_focus = true; - break; - - case FocusOut: - // lost focus. Reset button and mouse info. - has_focus = 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 = 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 = 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 = 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 = true; - } - } - data->button_press_index = -1; - break; - } - - if (draw) { - // Draw our dialog box. - X11_MessageBoxDraw(data, ctx, have_utf8); - } - } - - X11_XFreeGC(data->display, ctx); - return true; -} - -static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID) -{ - bool result = false; - SDL_MessageBoxDataX11 data; -#if SDL_SET_LOCALE - char *origlocale; -#endif - - SDL_zero(data); - - if (!SDL_X11_LoadSymbols()) { - return false; - } - -#if SDL_SET_LOCALE - origlocale = setlocale(LC_ALL, NULL); - if (origlocale) { - origlocale = SDL_strdup(origlocale); - if (!origlocale) { - return false; - } - (void)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. - if (!X11_MessageBoxInit(&data, messageboxdata, buttonID)) { - goto done; - } - - if (!X11_MessageBoxInitPositions(&data)) { - goto done; - } - - if (!X11_MessageBoxCreateWindow(&data)) { - goto done; - } - - result = X11_MessageBoxLoop(&data); - -done: - X11_MessageBoxShutdown(&data); -#if SDL_SET_LOCALE - if (origlocale) { - (void)setlocale(LC_ALL, origlocale); - SDL_free(origlocale); - } -#endif - - return result; -} - // Display an x11 message box. bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) { diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 959aabce8cd37..c0d254724de23 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -68,6 +68,7 @@ SDL_X11_SYM(int,XFreeGC,(Display* a,GC b)) SDL_X11_SYM(int,XFreeFont,(Display* a, XFontStruct* b)) SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a)) SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b)) +SDL_X11_SYM(int,XFreeColormap,(Display* a,Colormap b)) SDL_X11_SYM(void,XFreeStringList,(char** a)) SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b)) SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c)) diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c new file mode 100644 index 0000000000000..94a04e91a2edc --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.c @@ -0,0 +1,1282 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 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_internal.h" + +#ifdef SDL_VIDEO_DRIVER_X11 + +#include "SDL_x11video.h" +#include "SDL_x11dyn.h" +#include "SDL_x11toolkit.h" +#include +#include + +#define SDL_SET_LOCALE 1 + +typedef struct SDL_ToolkitIconControlX11 +{ + SDL_ToolkitControlX11 parent; + + SDL_MessageBoxFlags flags; + char icon_char; + XFontStruct *icon_char_font; + int icon_char_x; + int icon_char_y; + int icon_char_a; + XColor xcolor_black; + XColor xcolor_red; + XColor xcolor_red_darker; + XColor xcolor_white; + XColor xcolor_yellow; + XColor xcolor_blue; + XColor xcolor_bg_shadow; +} SDL_ToolkitIconControlX11; + +typedef struct SDL_ToolkitButtonControlX11 +{ + SDL_ToolkitControlX11 parent; + const SDL_MessageBoxButtonData *data; + SDL_Rect text_rect; + int text_a; + int text_d; + int str_sz; + void (*cb)(struct SDL_ToolkitControlX11 *, void *); + void *cb_data; +} SDL_ToolkitButtonControlX11; + +typedef struct SDL_ToolkitLabelControlX11 +{ + SDL_ToolkitControlX11 parent; + char **lines; + int *y; + size_t *szs; + size_t sz; +} SDL_ToolkitLabelControlX11; + +static const char *g_IconFont = "-*-*-bold-r-normal-*-18-*-*-*-*-*-iso8859-1[33 88 105]"; + +static const char g_ToolkitFontLatin1[] = + "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1"; + +static const char *g_ToolkitFont[] = { + "-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) + "-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) + "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems) + "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode. + "-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. + "-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. + "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems) + "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1. + NULL +}; + +static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { + { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, + { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_TEXT, + { 127, 120, 127 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, + { 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, + { 235, 235, 235 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, +}; + +static void GetTextWidthHeightForFont(XFontStruct *font, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent) +{ + XCharStruct text_structure; + int font_direction, font_descent; + X11_XTextExtents(font, str, nbytes, + &font_direction, font_ascent, &font_descent, + &text_structure); + *pwidth = text_structure.width; + *pheight = text_structure.ascent + text_structure.descent; +} + +static void GetTextWidthHeight(SDL_ToolkitWindowX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *font_ascent, int *font_descent) +{ +#ifdef X_HAVE_UTF8_STRING + if (data->utf8) { + XFontSetExtents *extents; + XRectangle overall_ink, overall_logical; + extents = X11_XExtentsOfFontSet(data->font_set); + X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); + *pwidth = overall_logical.width; + *pheight = overall_logical.height; + *font_ascent = -extents->max_logical_extent.y; + *font_descent = extents->max_logical_extent.height - *font_ascent; + } else +#endif + { + XCharStruct text_structure; + int font_direction; + 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; + } +} + +SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, const SDL_MessageBoxColor *colorhints) +{ + SDL_ToolkitWindowX11 *window; + int i; + #define ErrorFreeRetNull(x, y) SDL_SetError(x); SDL_free(y); return NULL; + #define ErrorCloseFreeRetNull(x, y, z) X11_XCloseDisplay(z->display); SDL_SetError(x, y); SDL_free(z); return NULL; + + if (!SDL_X11_LoadSymbols()) { + return NULL; + } + + // This code could get called from multiple threads maybe? + X11_XInitThreads(); + + window = (SDL_ToolkitWindowX11 *)SDL_malloc(sizeof(SDL_ToolkitWindowX11)); + if (!window) { + SDL_SetError("Unable to allocate toolkit window structure"); + return NULL; + } + +#if SDL_SET_LOCALE + window->origlocale = setlocale(LC_ALL, NULL); + if (window->origlocale) { + window->origlocale = SDL_strdup(window->origlocale); + if (!window->origlocale) { + return NULL; + } + (void)setlocale(LC_ALL, ""); + } +#endif + + window->display = X11_XOpenDisplay(NULL); + if (!window->display) { + ErrorFreeRetNull("Couldn't open X11 display", window); + } + +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR + int xrandr_event_base, xrandr_error_base; + window->xrandr = X11_XRRQueryExtension(window->display, &xrandr_event_base, &xrandr_error_base); +#endif + +#ifdef X_HAVE_UTF8_STRING + window->utf8 = true; + window->font_set = NULL; + if (SDL_X11_HAVE_UTF8) { + char **missing = NULL; + int num_missing = 0; + int i_font; + window->font_struct = NULL; + for (i_font = 0; g_ToolkitFont[i_font]; ++i_font) { + window->font_set = X11_XCreateFontSet(window->display, g_ToolkitFont[i_font], + &missing, &num_missing, NULL); + if (missing) { + X11_XFreeStringList(missing); + } + if (window->font_set) { + break; + } + } + if (!window->font_set) { + goto load_font_traditional; + } + } else +#endif + { + load_font_traditional: + window->font_struct = X11_XLoadQueryFont(window->display, g_ToolkitFontLatin1); + window->utf8 = false; + if (!window->font_struct) { + ErrorCloseFreeRetNull("Couldn't load font %s", g_ToolkitFontLatin1, window); + } + } + + if (!colorhints) { + colorhints = g_default_colors; + } + + // Convert colors to 16 bpc XColor format + for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { + window->xcolor[i].flags = DoRed|DoGreen|DoBlue; + window->xcolor[i].red = colorhints[i].r * 257; + window->xcolor[i].green = colorhints[i].g * 257; + window->xcolor[i].blue = colorhints[i].b * 257; + } + + /* Generate bevel and pressed colors */ + window->xcolor_bevel_l1.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_l1.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 12500, 0, 65535); + window->xcolor_bevel_l1.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 12500, 0, 65535); + window->xcolor_bevel_l1.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 12500, 0, 65535); + + window->xcolor_bevel_l2.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_l2.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red + 32500, 0, 65535); + window->xcolor_bevel_l2.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green + 32500, 0, 65535); + window->xcolor_bevel_l2.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue + 32500, 0, 65535); + + window->xcolor_bevel_d.flags = DoRed|DoGreen|DoBlue; + window->xcolor_bevel_d.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].red - 22500, 0, 65535); + window->xcolor_bevel_d.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].green - 22500, 0, 65535); + window->xcolor_bevel_d.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].blue - 22500, 0, 65535); + + window->xcolor_pressed.flags = DoRed|DoGreen|DoBlue; + window->xcolor_pressed.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].red - 12500, 0, 65535); + window->xcolor_pressed.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].green - 12500, 0, 65535); + window->xcolor_pressed.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].blue - 12500, 0, 65535); + + window->parent = parent; + if (parent) { + SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(parent); + window->screen = displaydata->screen; + } else { + window->screen = DefaultScreen(window->display); + } + + window->visual = DefaultVisual(window->display, window->screen); + window->cmap = DefaultColormap(window->display, window->screen); + for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { + X11_XAllocColor(window->display, window->cmap, &window->xcolor[i]); + } + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_l1); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_l2); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_bevel_d); + X11_XAllocColor(window->display, window->cmap, &window->xcolor_pressed); + + /* Control list */ + window->has_focus = false; + window->focused_control = NULL; + window->controls = NULL; + window->controls_sz = 0; + + return window; +} + +static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_ToolkitControlX11 *control) { + window->controls_sz++; + + if (window->controls_sz == 1) { + window->controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *)); + } else { + window->controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->controls, sizeof(struct SDL_ToolkitControlX11 *) * window->controls_sz); + } + + window->controls[window->controls_sz - 1] = control; +} + +static void X11Toolkit_FreeWindowStruct(SDL_ToolkitWindowX11 *data) { +#ifdef X_HAVE_UTF8_STRING + if (data->font_set) { + X11_XFreeFontSet(data->display, data->font_set); + data->font_set = NULL; + } +#endif + if (data->font_struct) { + X11_XFreeFont(data->display, data->font_struct); + data->font_struct = NULL; + } + + if (data->display) { + X11_XCloseDisplay(data->display); + } + +#if SDL_SET_LOCALE + if (data->origlocale) { + (void)setlocale(LC_ALL, data->origlocale); + SDL_free(data->origlocale); + } +#endif + + SDL_free(data); +} + +bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, char *title) +{ + int x, y; + XSizeHints *sizehints; + XSetWindowAttributes wnd_attr; + Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG; + SDL_WindowData *windowdata = NULL; + Display *display = data->display; + XGCValues ctx_vals; + Window root_win; + unsigned long gcflags = GCForeground | GCBackground; +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR +#ifdef XRANDR_DISABLED_BY_DEFAULT + const bool use_xrandr_by_default = false; +#else + const bool use_xrandr_by_default = true; +#endif +#endif + + data->window_width = w; + data->window_height = h; + if (data->parent) { + windowdata = data->parent->internal; + } + data->event_mask = ExposureMask | + ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | + StructureNotifyMask | FocusChangeMask | PointerMotionMask; + wnd_attr.event_mask = data->event_mask; + wnd_attr.colormap = data->cmap; + + root_win = RootWindow(display, data->screen); + data->window = X11_XCreateWindow( + display, root_win, + 0, 0, + data->window_width, data->window_height, + 0, DefaultDepth(display, data->screen), InputOutput, data->visual, + CWEventMask | CWColormap, &wnd_attr); + if (data->window == None) { + return SDL_SetError("Couldn't create X window"); + } + + if (windowdata) { + Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False); + Atom stateatoms[16]; + size_t statecount = 0; + // Set some message-boxy window states when attached to a parent window... + // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False); + stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False); + SDL_assert(statecount <= SDL_arraysize(stateatoms)); + X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char *)stateatoms, statecount); + + // http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR + X11_XSetTransientForHint(display, data->window, windowdata->xwindow); + } + + SDL_X11_SetWindowTitle(display, data->window, title); + + // 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_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); + X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); + + data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); + + if (windowdata) { + XWindowAttributes attrib; + Window dummy; + + X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib); + x = attrib.x + (attrib.width - data->window_width) / 2; + y = attrib.y + (attrib.height - data->window_height) / 3; + X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy); + } else { + 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 = dpy->internal; + x = dpydata->x + ((dpy->current_mode->w - data->window_width) / 2); + y = dpydata->y + ((dpy->current_mode->h - data->window_height) / 3); + } +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR + else if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, use_xrandr_by_default) && data->xrandr) { + XRRScreenResources *screen_res; + XRRCrtcInfo *crtc_info; + RROutput default_out; + + screen_res = X11_XRRGetScreenResourcesCurrent(display, root_win); + if (!screen_res) { + goto NOXRANDR; + } + + default_out = X11_XRRGetOutputPrimary(display, root_win); + if (default_out != None) { + XRROutputInfo *out_info; + + out_info = X11_XRRGetOutputInfo(display, screen_res, default_out); + if (out_info->connection != RR_Connected) { + X11_XRRFreeOutputInfo(out_info); + goto FIRSTOUTPUTXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); + if (crtc_info) { + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeOutputInfo(out_info); + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + } else { + X11_XRRFreeOutputInfo(out_info); + goto NOXRANDR; + } + } else { + FIRSTOUTPUTXRANDR: + if (screen_res->noutput > 0) { + XRROutputInfo *out_info; + + out_info = X11_XRRGetOutputInfo(display, screen_res, screen_res->outputs[0]); + if (!out_info) { + goto FIRSTCRTCXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc); + if (!crtc_info) { + X11_XRRFreeOutputInfo(out_info); + goto FIRSTCRTCXRANDR; + } + + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeOutputInfo(out_info); + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + goto MOVEWINDOW; + } + + FIRSTCRTCXRANDR: + if (!screen_res->ncrtc) { + X11_XRRFreeScreenResources(screen_res); + goto NOXRANDR; + } + + crtc_info = X11_XRRGetCrtcInfo(display, screen_res, screen_res->crtcs[0]); + if (crtc_info) { + x = (crtc_info->width - data->window_width) / 2; + y = (crtc_info->height - data->window_height) / 3; + X11_XRRFreeCrtcInfo(crtc_info); + X11_XRRFreeScreenResources(screen_res); + } else { + X11_XRRFreeScreenResources(screen_res); + goto NOXRANDR; + } + } + } +#endif + else { + // oh well. This will misposition on a multi-head setup. Init first next time. + NOXRANDR: + x = (DisplayWidth(display, data->screen) - data->window_width) / 2; + y = (DisplayHeight(display, data->screen) - data->window_height) / 3; + } + } + MOVEWINDOW: + 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->window_width; + sizehints->height = data->window_height; + + sizehints->min_width = sizehints->max_width = data->window_width; + sizehints->min_height = sizehints->max_height = data->window_height; + + X11_XSetWMNormalHints(display, data->window, sizehints); + + X11_XFree(sizehints); + } + + X11_XMapRaised(display, data->window); + + data->drawable = data->window; +#ifdef 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 = true; + data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined); + data->drawable = data->buf; + } else { + data->xdbe = false; + } + } +#endif + + SDL_zero(ctx_vals); + ctx_vals.foreground = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; + ctx_vals.background = data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel; + if (!data->utf8) { + gcflags |= GCFont; + ctx_vals.font = data->font_struct->fid; + } + data->ctx = X11_XCreateGC(data->display, data->drawable, gcflags, &ctx_vals); + if (data->ctx == None) { + return SDL_SetError("Couldn't create graphics context"); + } + + return true; +} + +SDL_ToolkitWindowX11 *X11Toolkit_CreateWindow(SDL_Window *parent, const SDL_MessageBoxColor *colorhints, int w, int h, char *title) { + SDL_ToolkitWindowX11 *window; + + window = X11Toolkit_CreateWindowStruct(parent, colorhints); + if (!window) { + return NULL; + } + + if (!X11Toolkit_CreateWindowRes(window, w, h, title)) { + X11Toolkit_FreeWindowStruct(window); + return NULL; + } + + return window; +} + +// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef +static Bool X11Toolkit_EventTest(Display *display, XEvent *event, XPointer arg) +{ + const SDL_ToolkitWindowX11 *data = (const SDL_ToolkitWindowX11 *)arg; + return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False; +} + +static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { + int i; + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && data->xdbe) { + X11_XdbeBeginIdiom(data->display); + } +#endif + + X11_XSetForeground(data->display, data->ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); + X11_XFillRectangle(data->display, data->drawable, data->ctx, 0, 0, data->window_width, data->window_height); + + for (i = 0; i < data->controls_sz; i++) { + SDL_ToolkitControlX11 *control; + + control = data->controls[i]; + if (control) { + if (control->func_draw) { + control->func_draw(control); + } + } + } + +#ifdef 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 + + X11_XFlush(data->display); +} + +static SDL_ToolkitControlX11 *GetControlMouseIsOn(SDL_ToolkitWindowX11 *data, int x, int y) +{ + int i; + + for (i = 0; i < data->controls_sz; i++) { + SDL_Rect *rect = &data->controls[i]->rect; + if ((x >= rect->x) && + (x <= (rect->x + rect->w)) && + (y >= rect->y) && + (y <= (rect->y + rect->h))) { + return data->controls[i]; + } + } + + return NULL; +} + +void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data) { + SDL_ToolkitControlX11 *previous_control; + SDL_ToolkitControlX11 *key_control_esc; + SDL_ToolkitControlX11 *key_control_enter; + KeySym last_key_pressed = XK_VoidSymbol; + int i; + + data->close = false; + key_control_esc = key_control_enter = NULL; + while (!data->close) { + XEvent e; + bool draw; + + // can't use XWindowEvent() because it can't handle ClientMessage events. + // can't use XNextEvent() because we only want events for this window. + draw = false; + X11_XIfEvent(data->display, &e, X11Toolkit_EventTest, (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: + draw = true; + break; + case ClientMessage: + if (e.xclient.message_type == data->wm_protocols && + e.xclient.format == 32 && + e.xclient.data.l[0] == data->wm_delete_message) { + data->close = true; + } + break; + case FocusIn: + data->has_focus = true; + break; + case FocusOut: + data->has_focus = false; + if (data->focused_control) { + data->focused_control->selected = false; + } + data->focused_control = NULL; + for (i = 0; i < data->controls_sz; i++) { + data->controls[i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + } + break; + case MotionNotify: + if (data->has_focus) { + previous_control = data->focused_control; + data->focused_control = GetControlMouseIsOn(data, e.xbutton.x, e.xbutton.y); + if (previous_control) { + previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + draw = true; + } + if (data->focused_control) { + if (data->focused_control->dynamic) { + data->focused_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_HOVER; + draw = true; + } else { + data->focused_control = NULL; + } + } + } + break; + case ButtonPress: + previous_control = data->focused_control; + if (previous_control) { + previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + draw = true; + } + if (e.xbutton.button == Button1) { + data->focused_control = GetControlMouseIsOn(data, e.xbutton.x, e.xbutton.y); + if (data->focused_control) { + data->focused_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + draw = true; + } + } + break; + case ButtonRelease: + if ((e.xbutton.button == Button1) && (data->focused_control)) { + SDL_ToolkitControlX11 *control; + + control = GetControlMouseIsOn(data, e.xbutton.x, e.xbutton.y); + if (data->focused_control == control) { + if (data->focused_control->func_on_state_change) { + data->focused_control->func_on_state_change(data->focused_control); + } + data->focused_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + draw = true; + } + } + break; + case KeyPress: + last_key_pressed = X11_XLookupKeysym(&e.xkey, 0); + + if (last_key_pressed == XK_Escape) { + for (i = 0; i < data->controls_sz; i++) { + if(data->controls[i]->is_default_esc) { + data->controls[i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + draw = true; + key_control_esc = data->controls[i]; + } + } + } else if ((last_key_pressed == XK_Return) || (last_key_pressed == XK_KP_Enter)) { + for (i = 0; i < data->controls_sz; i++) { + if(data->controls[i]->is_default_enter) { + data->controls[i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + draw = true; + key_control_enter = data->controls[i]; + } + } + } + break; + case KeyRelease: + { + 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) { + if (key_control_esc) { + if (key_control_esc->func_on_state_change) { + key_control_esc->func_on_state_change(key_control_esc); + } + } + } else if ((key == XK_Return) || (key == XK_KP_Enter)) { + if (key_control_enter) { + if (key_control_enter->func_on_state_change) { + key_control_enter->func_on_state_change(key_control_enter); + } + } + } + break; + } + + } + + if (draw) { + X11Toolkit_DrawWindow(data); + } + } + +} + +void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { + data->window_width = w; + data->window_height = h; + X11_XResizeWindow(data->display, data->window, data->window_width, data->window_height); +} + +static void X11Toolkit_DestroyIconControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + X11_XFreeFont(control->window->display, icon_control->icon_char_font); + SDL_free(control); +} + +static void X11Toolkit_DrawIconControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + control->rect.w -= 2; + control->rect.h -= 2; + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_bg_shadow.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + 2, control->rect.y + 2, control->rect.w, control->rect.h, 0, 360 * 64); + + switch (icon_control->flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { + case SDL_MESSAGEBOX_ERROR: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_red_darker.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_red.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+1, control->rect.y+1, control->rect.w-2, control->rect.h-2, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + break; + case SDL_MESSAGEBOX_WARNING: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_black.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_yellow.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+1, control->rect.y+1, control->rect.w-2, control->rect.h-2, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_black.pixel); + break; + case SDL_MESSAGEBOX_INFORMATION: + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x, control->rect.y, control->rect.w, control->rect.h, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_blue.pixel); + X11_XFillArc(control->window->display, control->window->drawable, control->window->ctx, control->rect.x+1, control->rect.y+1, control->rect.w-2, control->rect.h-2, 0, 360 * 64); + X11_XSetForeground(control->window->display, control->window->ctx, icon_control->xcolor_white.pixel); + break; + } + X11_XSetFont(control->window->display, control->window->ctx, icon_control->icon_char_font->fid); + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, control->rect.x + icon_control->icon_char_x, control->rect.y + icon_control->icon_char_y, &icon_control->icon_char, 1); + if (!control->window->utf8) { + X11_XSetFont(control->window->display, control->window->ctx, control->window->font_struct->fid); + } + + control->rect.w += 2; + control->rect.h += 2; +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags) { + SDL_ToolkitIconControlX11 *control; + SDL_ToolkitControlX11 *base_control; + int icon_char_w; + int icon_char_h; + int icon_char_max; + + control = (SDL_ToolkitIconControlX11 *)SDL_malloc(sizeof(SDL_ToolkitIconControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate icon control structure"); + return NULL; + } + base_control->window = window; + base_control->func_draw = X11Toolkit_DrawIconControl; + base_control->func_free = X11Toolkit_DestroyIconControl; + base_control->func_on_state_change = NULL; + base_control->func_upsize = NULL; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = false; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + control->flags = flags; + control->icon_char_font = X11_XLoadQueryFont(window->display, g_IconFont); + if (!control->icon_char_font) { + control->icon_char_font = X11_XLoadQueryFont(window->display, g_ToolkitFontLatin1); + if (!control->icon_char_font) { + SDL_free(control); + return NULL; + } + } + + switch (flags & (SDL_MESSAGEBOX_ERROR | SDL_MESSAGEBOX_WARNING | SDL_MESSAGEBOX_INFORMATION)) { + case SDL_MESSAGEBOX_ERROR: + control->icon_char = 'X'; + control->xcolor_white.flags = DoRed|DoGreen|DoBlue; + control->xcolor_white.red = 65535; + control->xcolor_white.green = 65535; + control->xcolor_white.blue = 65535; + control->xcolor_red.flags = DoRed|DoGreen|DoBlue; + control->xcolor_red.red = 65535; + control->xcolor_red.green = 0; + control->xcolor_red.blue = 0; + control->xcolor_red_darker.flags = DoRed|DoGreen|DoBlue; + control->xcolor_red_darker.red = 40535; + control->xcolor_red_darker.green = 0; + control->xcolor_red_darker.blue = 0; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_red_darker); + break; + case SDL_MESSAGEBOX_WARNING: + control->icon_char = '!'; + control->xcolor_black.flags = DoRed|DoGreen|DoBlue; + control->xcolor_black.red = 0; + control->xcolor_black.green = 0; + control->xcolor_black.blue = 0; + control->xcolor_yellow.flags = DoRed|DoGreen|DoBlue; + control->xcolor_yellow.red = 65535; + control->xcolor_yellow.green = 65535; + control->xcolor_yellow.blue = 0; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_black); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_yellow); + break; + case SDL_MESSAGEBOX_INFORMATION: + control->icon_char = 'i'; + control->xcolor_white.flags = DoRed|DoGreen|DoBlue; + control->xcolor_white.red = 65535; + control->xcolor_white.green = 65535; + control->xcolor_white.blue = 65535; + control->xcolor_blue.flags = DoRed|DoGreen|DoBlue; + control->xcolor_blue.red = 0; + control->xcolor_blue.green = 0; + control->xcolor_blue.blue = 65535; + X11_XAllocColor(window->display, window->cmap, &control->xcolor_white); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_blue); + break; + default: + X11_XFreeFont(window->display, control->icon_char_font); + SDL_free(control); + return NULL; + } + + control->xcolor_bg_shadow.flags = DoRed|DoGreen|DoBlue; + control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); + control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); + control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); + X11_XAllocColor(window->display, window->cmap, &control->xcolor_bg_shadow); + + GetTextWidthHeightForFont(control->icon_char_font, &control->icon_char, 1, &icon_char_w, &icon_char_h, &control->icon_char_a); + base_control->rect.w = icon_char_w + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2; + base_control->rect.h = icon_char_h + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2; + icon_char_max = SDL_max(base_control->rect.w, base_control->rect.h) + 2; + base_control->rect.w = icon_char_max; + base_control->rect.h = icon_char_max; + base_control->rect.y = 0; + base_control->rect.x = 0; + control->icon_char_y = control->icon_char_a + (base_control->rect.h - icon_char_h)/2 + 1; + control->icon_char_x = (base_control->rect.w - icon_char_w)/2 + 1; + base_control->rect.w += 2; + base_control->rect.h += 2; + + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +bool X11Toolkit_NotifyUpsize(SDL_ToolkitControlX11 *control) { + if (control->func_upsize) { + control->func_upsize(control); + return true; + } else { + return false; + } +} + +static void X11Toolkit_UpsizeButtonControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + button_control->text_rect.x = (control->rect.w - button_control->text_rect.w)/2; + button_control->text_rect.y = button_control->text_a + (control->rect.h - button_control->text_rect.h)/2; + if (control->window->utf8) { + button_control->text_rect.y -= 2; + } else { + button_control->text_rect.y -= 4; + } +} + + +static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); + /* Draw bevel */ + if (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED) { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w - 1, control->rect.h - 1); + + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1, control->rect.y + 1, + control->rect.w - 3, control->rect.h - 2); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1, control->rect.y + 1, + control->rect.w - 3, control->rect.h - 3); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_pressed.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2, control->rect.y + 2, + control->rect.w - 4, control->rect.h - 4); + } else { + if (button_control->data->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1, control->rect.y + 1, + control->rect.w - 3, control->rect.h - 3); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2, control->rect.y + 2, + control->rect.w - 4, control->rect.h - 4); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2, control->rect.y + 2, + control->rect.w - 5, control->rect.h - 5); + + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 3, control->rect.y + 3, + control->rect.w - 6, control->rect.h - 6); + } else { + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_d.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w, control->rect.h); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l2.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y, + control->rect.w - 1, control->rect.h - 1); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1, control->rect.y + 1, + control->rect.w - 2, control->rect.h - 2); + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor_bevel_l1.pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 1, control->rect.y + 1, + control->rect.w - 3, control->rect.h - 3); + + X11_XSetForeground(control->window->display, control->window->ctx, (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_HOVER) ? control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED].pixel : control->window->xcolor[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND].pixel); + X11_XFillRectangle(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + 2, control->rect.y + 2, + control->rect.w - 4, control->rect.h - 4); + } + } + + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); +#ifdef X_HAVE_UTF8_STRING + if (control->window->utf8) { + X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx, + control->rect.x + button_control->text_rect.x, + control->rect.y + button_control->text_rect.y, + button_control->data->text, button_control->str_sz); + } else +#endif + { + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x + button_control->text_rect.x, control->rect.y + button_control->text_rect.y, + button_control->data->text, button_control->str_sz); + } +} + +static void X11Toolkit_OnButtonControlStateChange(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + if (button_control->cb) { + button_control->cb(control, button_control->cb_data); + } +} + +static void X11Toolkit_DestroyButtonControl(SDL_ToolkitControlX11 *control) { + SDL_free(control); +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *window, const SDL_MessageBoxButtonData *data) { + SDL_ToolkitButtonControlX11 *control; + SDL_ToolkitControlX11 *base_control; + int text_d; + + control = (SDL_ToolkitButtonControlX11 *)SDL_malloc(sizeof(SDL_ToolkitButtonControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate button control structure"); + return NULL; + } + base_control->window = window; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->func_upsize = X11Toolkit_UpsizeButtonControl; + base_control->func_draw = X11Toolkit_DrawButtonControl; + base_control->func_on_state_change = X11Toolkit_OnButtonControlStateChange; + base_control->func_free = X11Toolkit_DestroyButtonControl; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = true; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + if (data->flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) { + base_control->is_default_esc = true; + } + if (data->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) { + base_control->is_default_enter = true; + } + control->data = data; + control->str_sz = SDL_strlen(control->data->text); + control->cb = NULL; + GetTextWidthHeight(base_control->window, control->data->text, control->str_sz, &control->text_rect.w, &control->text_rect.h, &control->text_a, &text_d); + base_control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 + control->text_rect.w; + base_control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 + control->text_rect.h; + control->text_rect.x = control->text_rect.y = 0; + X11Toolkit_UpsizeButtonControl(base_control); + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, void *data, void (*cb)(struct SDL_ToolkitControlX11 *, void *)) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + button_control->cb_data = data; + button_control->cb = cb; +} + +const SDL_MessageBoxButtonData *X11Toolkit_GetButtonControlData(SDL_ToolkitControlX11 *control) { + SDL_ToolkitButtonControlX11 *button_control; + + button_control = (SDL_ToolkitButtonControlX11 *)control; + return button_control->data; +} + +void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) { + int i; + + if (!data) { + return; + } + + for (i = 0; i < data->controls_sz; i++) { + if (data->controls[i]->func_free) { + data->controls[i]->func_free(data->controls[i]); + } + } + +#ifdef X_HAVE_UTF8_STRING + if (data->font_set) { + X11_XFreeFontSet(data->display, data->font_set); + data->font_set = NULL; + } +#endif + + if (data->font_struct) { + X11_XFreeFont(data->display, data->font_struct); + data->font_struct = NULL; + } + +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && data->xdbe) { + X11_XdbeDeallocateBackBufferName(data->display, data->buf); + } +#endif + + X11_XFreeGC(data->display, data->ctx); + + 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; + } + +#if SDL_SET_LOCALE + if (data->origlocale) { + (void)setlocale(LC_ALL, data->origlocale); + SDL_free(data->origlocale); + } +#endif + + SDL_free(data); +} + +static int CountLinesOfText(const char *text) +{ + int result = 0; + while (text && *text) { + const char *lf = SDL_strchr(text, '\n'); + result++; // even without an endline, this counts as a line. + text = lf ? lf + 1 : NULL; + } + return result; +} + +static void X11Toolkit_DrawLabelControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitLabelControlX11 *label_control; + int i; + + label_control = (SDL_ToolkitLabelControlX11 *)control; + X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel); + for (i = 0; i < label_control->sz; i++) { +#ifdef X_HAVE_UTF8_STRING + if (control->window->utf8) { + X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx, + control->rect.x, control->rect.y + label_control->y[i], + label_control->lines[i], label_control->szs[i]); + } else +#endif + { + X11_XDrawString(control->window->display, control->window->drawable, control->window->ctx, + control->rect.x, control->rect.y + label_control->y[i], + label_control->lines[i], label_control->szs[i]); + } + } +} + +static void X11Toolkit_DestroyLabelControl(SDL_ToolkitControlX11 *control) { + SDL_ToolkitLabelControlX11 *label_control; + + label_control = (SDL_ToolkitLabelControlX11 *)control; + SDL_free(label_control->lines); + SDL_free(label_control->szs); + SDL_free(label_control->y); + SDL_free(label_control); +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8) { + SDL_ToolkitLabelControlX11 *control; + SDL_ToolkitControlX11 *base_control; + int ascent; + int descent; + int i; + + if (!utf8) { + return NULL; + } + control = (SDL_ToolkitLabelControlX11 *)SDL_malloc(sizeof(SDL_ToolkitLabelControlX11)); + base_control = (SDL_ToolkitControlX11 *)control; + if (!control) { + SDL_SetError("Unable to allocate label control structure"); + return NULL; + } + base_control->window = window; + base_control->func_draw = X11Toolkit_DrawLabelControl; + base_control->func_on_state_change = NULL; + base_control->func_upsize = NULL; + base_control->func_free = X11Toolkit_DestroyLabelControl; + base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + base_control->selected = false; + base_control->dynamic = false; + base_control->rect.w = 0; + base_control->rect.h = 0; + base_control->is_default_enter = false; + base_control->is_default_esc = false; + control->sz = CountLinesOfText(utf8); + control->lines = (char **)SDL_malloc(sizeof(char *) * control->sz); + control->y = (int *)SDL_calloc(control->sz, sizeof(int)); + control->szs = (size_t *)SDL_calloc(control->sz, sizeof(size_t)); + for (i = 0; i < control->sz; i++) { + const char *lf = SDL_strchr(utf8, '\n'); + const int length = lf ? (lf - utf8) : SDL_strlen(utf8); + int w; + int h; + + control->lines[i] = utf8; + GetTextWidthHeight(window, utf8, length, &w, &h, &ascent, &descent); + base_control->rect.w = SDL_max(base_control->rect.w, w); + + control->szs[i] = length; + if (lf && (lf > utf8) && (lf[-1] == '\r')) { + control->szs[i]--; + } + + if (i > 0) { + control->y[i] = ascent + descent + control->y[i-1]; + base_control->rect.h += ascent + descent + h; + } else { + control->y[i] = ascent; + base_control->rect.h = h; + } + utf8 += length + 1; + + if (!lf) { + break; + } + } + + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +int X11Toolkit_GetIconControlCharY(SDL_ToolkitControlX11 *control) { + SDL_ToolkitIconControlX11 *icon_control; + + icon_control = (SDL_ToolkitIconControlX11 *)control; + return icon_control->icon_char_y - icon_control->icon_char_a; +} + +void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data) { + data->close = true; +} + +#endif // SDL_VIDEO_DRIVER_X11 diff --git a/src/video/x11/SDL_x11toolkit.h b/src/video/x11/SDL_x11toolkit.h new file mode 100644 index 0000000000000..5fce8b2c08a49 --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.h @@ -0,0 +1,129 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 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_internal.h" + +#ifndef SDL_x11toolkit_h_ +#define SDL_x11toolkit_h_ + +#include "SDL_x11video.h" +#include "SDL_x11dyn.h" +#include "SDL_x11toolkit.h" + +#ifdef SDL_VIDEO_DRIVER_X11 + +#define SDL_TOOLKIT_X11_ELEMENT_PADDING 4 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_2 12 +#define SDL_TOOLKIT_X11_ELEMENT_PADDING_3 8 + +typedef struct SDL_ToolkitWindowX11 +{ + char *origlocale; + + SDL_Window *parent; + Display *display; + int screen; + Window window; + Drawable drawable; + Visual *visual; + Colormap cmap; + GC ctx; + bool utf8; +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + XdbeBackBuffer buf; + bool xdbe; // Whether Xdbe is present or not +#endif +#ifdef SDL_VIDEO_DRIVER_X11_XRANDR + bool xrandr; // Whether Xrandr is present or not +#endif + long event_mask; + Atom wm_protocols; + Atom wm_delete_message; + bool close; + + int window_width; // Window width. + int window_height; // Window height. + + XFontSet font_set; // for UTF-8 systems + XFontStruct *font_struct; // Latin1 (ASCII) fallback. + + XColor xcolor[SDL_MESSAGEBOX_COLOR_COUNT]; + XColor xcolor_bevel_l1; + XColor xcolor_bevel_l2; + XColor xcolor_bevel_d; + XColor xcolor_pressed; + + bool has_focus; + struct SDL_ToolkitControlX11 *focused_control; + + struct SDL_ToolkitControlX11 **controls; + size_t controls_sz; +} SDL_ToolkitWindowX11; + +typedef enum SDL_ToolkitControlStateX11 +{ + SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL, + SDL_TOOLKIT_CONTROL_STATE_X11_HOVER, + SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED, +} SDL_ToolkitControlStateX11; + +typedef struct SDL_ToolkitControlX11 +{ + SDL_ToolkitControlStateX11 state; + SDL_ToolkitWindowX11 *window; + SDL_Rect rect; + bool selected; + bool dynamic; + bool is_default_enter; + bool is_default_esc; + void *data; + void (*func_draw)(struct SDL_ToolkitControlX11 *); + void (*func_upsize)(struct SDL_ToolkitControlX11 *); + void (*func_on_state_change)(struct SDL_ToolkitControlX11 *); + void (*func_free)(struct SDL_ToolkitControlX11 *); +} SDL_ToolkitControlX11; + +/* WINDOW FUNCTIONS */ +extern SDL_ToolkitWindowX11 *X11Toolkit_CreateWindow(SDL_Window *parent, const SDL_MessageBoxColor *colorhints, int w, int h, char *title); +extern SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, const SDL_MessageBoxColor *colorhints); +extern bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, char *title); +extern void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data); +extern void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h); +extern void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data); +extern void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data); + +/* GENERIC CONTROL FUNCTIONS */ +extern bool X11Toolkit_NotifyUpsize(SDL_ToolkitControlX11 *control); + +/* ICON CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags); +extern int X11Toolkit_GetIconControlCharY(SDL_ToolkitControlX11 *control); + +/* LABEL CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8); + +/* BUTTON CONTROL FUNCTIONS */ +extern SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *window, const SDL_MessageBoxButtonData *data); +extern void X11Toolkit_RegisterCallbackForButtonControl(SDL_ToolkitControlX11 *control, void *data, void (*cb)(struct SDL_ToolkitControlX11 *, void *)); +extern const SDL_MessageBoxButtonData *X11Toolkit_GetButtonControlData(SDL_ToolkitControlX11 *control); + +#endif // SDL_VIDEO_DRIVER_X11 + +#endif // SDL_x11toolkit_h_