diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c index dd4a1bf6913f6..c762a7c7057ca 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,1183 +37,195 @@ #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 +typedef struct SDL_MessageBoxControlsX11 { - 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; - - 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; - + SDL_ToolkitWindowX11 *window; + SDL_ToolkitControlX11 *icon; + SDL_ToolkitControlX11 fake_icon; + SDL_ToolkitControlX11 *message; + SDL_ToolkitControlX11 **buttons; 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; -} - -// 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; -} +} SDL_MessageBoxControlsX11; -// Initialize SDL_MessageBoxData structure and Display, etc. -static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid) +static void X11_MessageBoxButtonCallback(SDL_ToolkitControlX11 *control, void *data) { - int i; - int numbuttons = messageboxdata->numbuttons; - const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons; - const SDL_MessageBoxColor *colorhints; - - if (numbuttons > MAX_BUTTONS) { - return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS); - } - - data->dialog_width = MIN_DIALOG_WIDTH; - data->dialog_height = MIN_DIALOG_HEIGHT; - data->messageboxdata = messageboxdata; - data->buttondata = buttondata; - data->numbuttons = numbuttons; - data->pbuttonid = pbuttonid; - - // 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 - -#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'; - } - } - } - - 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; - } + SDL_MessageBoxCallbackDataX11 *cbdata; - /* 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); - } - - return true; + cbdata = (SDL_MessageBoxCallbackDataX11 *)data; + *cbdata->buttonID = X11Toolkit_GetButtonControlData(control)->buttonID; + X11Toolkit_SignalWindowClose(cbdata->window); } -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; -} - -// 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; +static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp, int *hp) { + 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; - 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; - } - - // 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)); - } else { - data->dialog_width = IntMax(data->dialog_width, button_width_total); + /* Init vars */ + max_button_w = 50; + max_button_h = 0; + w = h = 2; + i = t = total_button_w = total_text_and_icon_w = 0; + max_button_w *= controls->window->iscale; + + + /* Positioning and sizing */ + for (i = 0; i < controls->messageboxdata->numbuttons; i++) { + max_button_w = SDL_max(max_button_w, controls->buttons[i]->rect.w); + max_button_h = SDL_max(max_button_h, controls->buttons[i]->rect.h); + controls->buttons[i]->rect.x = 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; + if (controls->icon) { + controls->icon->rect.x = controls->icon->rect.y = 0; } - /* 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; + if (controls->icon) { + controls->message->rect.x = (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) + controls->icon->rect.x + controls->icon->rect.w; + controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon); + } else { + controls->message->rect.x = 0; + controls->message->rect.y = -2; + controls->icon = &controls->fake_icon; + controls->icon->rect.w = 0; + controls->icon->rect.h = 0; + controls->icon->rect.x = 0; + controls->icon->rect.y = 0; + } + if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { + for (i = controls->messageboxdata->numbuttons; i != -1; i--) { + controls->buttons[i]->rect.w = max_button_w; + controls->buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + + if (controls->icon->rect.h > controls->message->rect.h) { + controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 *controls-> window->iscale); } 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; + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + + if (i) { + controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); } - } + } } 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 < controls->messageboxdata->numbuttons; i++) { + controls->buttons[i]->rect.w = max_button_w; + controls->buttons[i]->rect.h = max_button_h; + X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]); + + if (controls->icon->rect.h > controls->message->rect.h) { + controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); } 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; + controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + + if (i) { + controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale); } } } - - /* 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 = controls->buttons[controls->messageboxdata->numbuttons-1]->rect.x + controls->buttons[controls->messageboxdata->numbuttons-1]->rect.w; + total_text_and_icon_w = controls->message->rect.x + controls->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]); + w = total_text_and_icon_w; } - 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"); - } - - 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 * controls->window->iscale) * 2; + if (controls->message->rect.h > controls->icon->rect.h) { + h = controls->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; - } - } -#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); - } - } + h = controls->icon->rect.h; + } + h += max_button_h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) * 3; + t = (w - total_text_and_icon_w) / 2; + controls->icon->rect.x += t; + controls->message->rect.x += t; + controls->icon->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + controls->message->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + t = (w - total_button_w) / 2; + for (i = 0; i < controls->messageboxdata->numbuttons; i++) { + controls->buttons[i]->rect.x += t; + controls->buttons[i]->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale); + } + if (!controls->messageboxdata->message) { + controls->icon->rect.x = (w - controls->icon->rect.w)/2; + } - 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 + *wp = w; + *hp = h; } -// 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 void X11_OnMessageBoxScaleChange(SDL_ToolkitWindowX11 *window, void *data) { + SDL_MessageBoxControlsX11 *controls; + int w; + int h; + + controls = data; + X11_PositionMessageBox(controls, &w, &h); + X11Toolkit_ResizeWindow(window, w, h); } 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; + SDL_MessageBoxControlsX11 controls; + SDL_MessageBoxCallbackDataX11 data; + const SDL_MessageBoxColor *colorhints; + int i; + int w; + int h; + + controls.messageboxdata = messageboxdata; + + /* Color scheme */ + if (messageboxdata->colorScheme) { + colorhints = messageboxdata->colorScheme->colors; + } else { + colorhints = NULL; } - if (!X11_MessageBoxInitPositions(&data)) { - goto done; + /* Create window */ + controls.window = X11Toolkit_CreateWindowStruct(messageboxdata->window, colorhints); + controls.window->cb_data = &controls; + controls.window->cb_on_scale_change = X11_OnMessageBoxScaleChange; + if (!controls.window) { + return false; } - if (!X11_MessageBoxCreateWindow(&data)) { - goto done; - } + /* Create controls */ + controls.buttons = SDL_calloc(messageboxdata->numbuttons, sizeof(SDL_ToolkitControlX11 *)); + controls.icon = X11Toolkit_CreateIconControl(controls.window, messageboxdata->flags); + controls.message = X11Toolkit_CreateLabelControl(controls.window, (char *)messageboxdata->message); + data.buttonID = buttonID; + data.window = controls.window; + for (i = 0; i < messageboxdata->numbuttons; i++) { + controls.buttons[i] = X11Toolkit_CreateButtonControl(controls.window, &messageboxdata->buttons[i]); + X11Toolkit_RegisterCallbackForButtonControl(controls.buttons[i], &data, X11_MessageBoxButtonCallback); + } + + /* Positioning */ + X11_PositionMessageBox(&controls, &w, &h); - result = X11_MessageBoxLoop(&data); - -done: - X11_MessageBoxShutdown(&data); -#if SDL_SET_LOCALE - if (origlocale) { - (void)setlocale(LC_ALL, origlocale); - SDL_free(origlocale); + /* Actually create window, do event loop, cleanup */ + X11Toolkit_CreateWindowRes(controls.window, w, h, (char *)messageboxdata->title); + X11Toolkit_DoWindowEventLoop(controls.window); + X11Toolkit_DestroyWindow(controls.window); + if (controls.buttons) { + SDL_free(controls.buttons); } -#endif - - return result; + return true; } // Display an x11 message box. diff --git a/src/video/x11/SDL_x11settings.c b/src/video/x11/SDL_x11settings.c index 50bf773f3a3dc..a608919770c32 100644 --- a/src/video/x11/SDL_x11settings.c +++ b/src/video/x11/SDL_x11settings.c @@ -26,10 +26,6 @@ #include "SDL_x11video.h" #include "SDL_x11settings.h" -#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" -#define SDL_XSETTINGS_GDK_UNSCALED_DPI "Gdk/UnscaledDPI" -#define SDL_XSETTINGS_XFT_DPI "Xft/DPI" - static void UpdateContentScale(SDL_VideoDevice *_this) { if (_this) { diff --git a/src/video/x11/SDL_x11settings.h b/src/video/x11/SDL_x11settings.h index f91ed70ac7e1d..2dab71e05898e 100644 --- a/src/video/x11/SDL_x11settings.h +++ b/src/video/x11/SDL_x11settings.h @@ -27,6 +27,10 @@ #include #include "xsettings-client.h" +#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" +#define SDL_XSETTINGS_GDK_UNSCALED_DPI "Gdk/UnscaledDPI" +#define SDL_XSETTINGS_XFT_DPI "Xft/DPI" + typedef struct X11_SettingsData { XSettingsClient *xsettings; } SDLX11_SettingsData; diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index 959aabce8cd37..b53a6f11d18db 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -42,6 +42,8 @@ SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height)) SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d)) SDL_X11_SYM(Cursor,XCreatePixmapCursor,(Display* a,Pixmap b,Pixmap c,XColor* d,XColor* e,unsigned int f,unsigned int g)) +SDL_X11_SYM(Cursor,XCreatePixmap,(Display* a,Drawable b,unsigned int d,unsigned int e,unsigned int f)) + SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b)) SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e)) SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d)) @@ -68,6 +70,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)) @@ -102,6 +105,8 @@ SDL_X11_SYM(Display*,XOpenDisplay,(_Xconst char* a)) SDL_X11_SYM(Status,XInitThreads,(void)) SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b)) SDL_X11_SYM(int,XPending,(Display* a)) +SDL_X11_SYM(XImage*,XGetImage,(Display* a,Drawable b,int c, int d,unsigned int e,unsigned int f,unsigned long g,int h)) +SDL_X11_SYM(void,XDestroyImage,(XImage *a)) SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j)) SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32])) SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i)) diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c new file mode 100644 index 0000000000000..1eccfbe8ff419 --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.c @@ -0,0 +1,1732 @@ +/* + 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 "SDL_x11settings.h" +#include "SDL_x11modes.h" +#include "xsettings-client.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-*-%d-*-*-*-*-*-iso8859-1[33 88 105]"; +#define G_ICONFONT_SIZE 18 + +static const char g_ToolkitFontLatin1[] = + "-*-*-medium-r-normal--0-%d-*-*-p-0-iso8859-1"; + +static const char *g_ToolkitFont[] = { + "-*-*-medium-r-normal--*-%d-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) + "-*-*-medium-r-*--*-%d-*-*-*-*-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--*-v-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. + "-*-*-medium-r-*--*-%d-*-*-*-*-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 +}; +#define G_TOOLKITFONT_SIZE 120 + +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, +}; + +int SettingsGetInt(XSettingsClient *client, const char *key, int fallback_value) { + XSettingsSetting *setting = NULL; + int res = fallback_value; + + + if (client) { + if (xsettings_client_get_setting(client, key, &setting) != XSETTINGS_SUCCESS) { + goto no_key; + } + + if (setting->type != XSETTINGS_TYPE_INT) { + goto no_key; + } + + res = setting->data.v_int; + } + +no_key: + if (setting) { + xsettings_setting_free(setting); + } + + return res; +} + +static float GetUIScale(XSettingsClient *client, Display *display) +{ + double scale_factor = 0.0; + + // First use the forced scaling factor specified by the app/user + const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR); + if (hint && *hint) { + double value = SDL_atof(hint); + if (value >= 1.0f && value <= 10.0f) { + scale_factor = value; + } + } + + // If that failed, try "Xft.dpi" from the XResourcesDatabase... + // We attempt to read this directly to get the live value, XResourceManagerString + // is cached per display connection. + if (scale_factor <= 0.0) + { + int status, real_format; + Atom real_type; + Atom res_mgr; + unsigned long items_read, items_left; + char *resource_manager; + bool owns_resource_manager = false; + + X11_XrmInitialize(); + res_mgr = X11_XInternAtom(display, "RESOURCE_MANAGER", False); + status = X11_XGetWindowProperty(display, RootWindow(display, DefaultScreen(display)), + res_mgr, 0L, 8192L, False, XA_STRING, + &real_type, &real_format, &items_read, &items_left, + (unsigned char **)&resource_manager); + + if (status == Success && resource_manager) { + owns_resource_manager = true; + } else { + // Fall back to XResourceManagerString. This will not be updated if the + // dpi value is later changed but should allow getting the initial value. + resource_manager = X11_XResourceManagerString(display); + } + + if (resource_manager) { + XrmDatabase db; + XrmValue value; + char *type; + + db = X11_XrmGetStringDatabase(resource_manager); + + // Get the value of Xft.dpi from the Database + if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { + if (value.addr && type && SDL_strcmp(type, "String") == 0) { + int dpi = SDL_atoi(value.addr); + scale_factor = dpi / 96.0; + } + } + X11_XrmDestroyDatabase(db); + + if (owns_resource_manager) { + X11_XFree(resource_manager); + } + } + } + + // If that failed, try the XSETTINGS keys... + if (scale_factor <= 0.0) { + scale_factor = SettingsGetInt(client, "Gdk/WindowScalingFactor", -1); + + // The Xft/DPI key is stored in increments of 1024th + if (scale_factor <= 0.0) { + int dpi = SettingsGetInt(client, "Xft/DPI", -1); + if (dpi > 0) { + scale_factor = (double) dpi / 1024.0; + scale_factor /= 96.0; + } + } + } + + // If that failed, try the GDK_SCALE envvar... + if (scale_factor <= 0.0) { + const char *scale_str = SDL_getenv("GDK_SCALE"); + if (scale_str) { + scale_factor = SDL_atoi(scale_str); + } + } + + // Nothing or a bad value, just fall back to 1.0 + if (scale_factor <= 0.0) { + scale_factor = 1.0; + } + + return (float)scale_factor; +} + +static void SettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) +{ + SDL_ToolkitWindowX11 *window; + int i; + + window = data; + + if (window->xsettings_first_time) { + return; + } + + if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_GDK_UNSCALED_DPI) == 0 || + SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { + bool dbe_already_setup; + bool pixmap_already_setup; + + if (window->pixmap) { + pixmap_already_setup = true; + } else { + dbe_already_setup = true; + } + + /* set scale vars */ + window->scale = GetUIScale(window->xsettings, window->display); + window->iscale = (int)SDL_ceilf(window->scale); + if (roundf(window->scale) == window->scale) { + window->scale = 0; + } + + /* set up window */ + if (window->scale != 0) { + window->window_width = SDL_lroundf((window->window_width/window->iscale) * window->scale); + window->window_height = SDL_lroundf((window->window_height/window->iscale) * window->scale); + window->pixmap_width = window->window_width; + window->pixmap_height = window->window_height; + window->pixmap = true; + } else { + window->pixmap = false; + } + + if (window->pixmap) { + if (!pixmap_already_setup) { +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && window->xdbe) { + X11_XdbeDeallocateBackBufferName(window->display, window->buf); + } +#endif + } + + X11_XFreePixmap(window->display, window->drawable); + window->drawable = X11_XCreatePixmap(window->display, window->window, window->pixmap_width, window->pixmap_height, window->depth); + } else { + if (!dbe_already_setup) { + X11_XFreePixmap(window->display, window->drawable); +#ifdef SDL_VIDEO_DRIVER_X11_XDBE + if (SDL_X11_HAVE_XDBE && window->xdbe) { + window->buf = X11_XdbeAllocateBackBufferName(window->display, window->window, XdbeUndefined); + window->drawable = window->buf; + } +#endif + } + } + + /* setup fonts */ +#ifdef X_HAVE_UTF8_STRING + if (window->font_set) { + X11_XFreeFontSet(window->display, window->font_set); + } +#endif + if (window->font_struct) { + X11_XFreeFont(window->display, window->font_struct); + } + +#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) { + char *font; + + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); + window->font_set = X11_XCreateFontSet(window->display, font, + &missing, &num_missing, NULL); + SDL_free(font); + if (missing) { + X11_XFreeStringList(missing); + } + if (window->font_set) { + break; + } + } + if (!window->font_set) { + goto load_font_traditional; + } + } else +#endif + { + char *font; + load_font_traditional: + + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + window->font_struct = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + window->utf8 = false; + } + + /* notify controls */ + for (i = 0; i < window->controls_sz; i++) { + if (window->controls[i]->func_on_scale_change) { + window->controls[i]->func_on_scale_change(window->controls[i]); + } + + if (window->controls[i]->func_calc_size) { + window->controls[i]->func_calc_size(window->controls[i]); + } + } + + /* notify */ + if (window->cb_on_scale_change) { + window->cb_on_scale_change(window, window->cb_data); + } + } +} + + +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 + + /* Scale/Xsettings */ + window->pixmap = false; + window->xsettings_first_time = true; + window->xsettings = xsettings_client_new(window->display, DefaultScreen(window->display), SettingsNotify, NULL, window); + window->xsettings_first_time = false; + window->scale = GetUIScale(window->xsettings, window->display); + window->iscale = (int)SDL_ceilf(window->scale); + if (roundf(window->scale) == window->scale) { + window->scale = 0; + } + +#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) { + char *font; + + SDL_asprintf(&font, g_ToolkitFont[i_font], G_TOOLKITFONT_SIZE * window->iscale); + window->font_set = X11_XCreateFontSet(window->display, font, + &missing, &num_missing, NULL); + SDL_free(font); + if (missing) { + X11_XFreeStringList(missing); + } + if (window->font_set) { + break; + } + } + if (!window->font_set) { + goto load_font_traditional; + } + } else +#endif + { + char *font; + load_font_traditional: + + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + window->font_struct = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + 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); + window->depth = DefaultDepth(window->display, window->screen); + X11_GetVisualInfoFromVisual(window->display, window->visual, &window->vi); + + 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->controls = NULL; + window->controls_sz = 0; + window->dyn_controls_sz = 0; + window->fiddled_control = NULL; + + 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; + + if (control->dynamic) { + window->dyn_controls_sz++; + + if (window->dyn_controls_sz == 1) { + window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *)); + } else { + window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->dyn_controls, sizeof(struct SDL_ToolkitControlX11 *) * window->dyn_controls_sz); + } + + window->dyn_controls[window->dyn_controls_sz - 1] = control; + } + + if (control->selected) { + window->focused_control = 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 + + if (data->scale == 0) { + data->window_width = w; + data->window_height = h; + } else { + data->window_width = SDL_lroundf((w/data->iscale) * data->scale); + data->window_height = SDL_lroundf((h/data->iscale) * data->scale); + data->pixmap_width = w; + data->pixmap_height = h; + data->pixmap = true; + } + + 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, data->depth, 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 && !data->pixmap) { + 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 + + if (data->pixmap) { + data->drawable = X11_XCreatePixmap(display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } + + 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 && !data->pixmap) { + X11_XdbeBeginIdiom(data->display); + } +#endif + + X11_XSetForeground(data->display, data->ctx, data->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].pixel); + if (data->pixmap) { + X11_XFillRectangle(data->display, data->drawable, data->ctx, 0, 0, data->pixmap_width, data->pixmap_height); + } else { + 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 && !data->pixmap) { + 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 + + if (data->pixmap) { + XImage *pixmap_image; + XImage *put_image; + SDL_Surface *pixmap_surface; + SDL_Surface *put_surface; + + /* FIXME: Implement SHM transport? */ + pixmap_image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); + pixmap_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), pixmap_image->data, pixmap_image->bytes_per_line); + put_surface = SDL_ScaleSurface(pixmap_surface, data->window_width, data->window_height, SDL_SCALEMODE_LINEAR); + put_image = X11_XCreateImage(data->display, data->visual, data->vi.depth, ZPixmap, 0, put_surface->pixels, data->window_width, data->window_height, 32, put_surface->pitch); + X11_XPutImage(data->display, data->window, data->ctx, put_image, 0, 0, 0, 0, data->window_width, data->window_height); + + X11_XDestroyImage(pixmap_image); + /* Needed because XDestroyImage results in a double-free otherwise */ + put_image->data = NULL; + X11_XDestroyImage(put_image); + SDL_DestroySurface(pixmap_surface); + SDL_DestroySurface(put_surface); + } + + 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; + float scale; + float iscale; + + data->close = false; + key_control_esc = key_control_enter = NULL; + if (!data->pixmap) { + scale = iscale = 1; + } else { + scale = data->scale; + iscale = data->iscale; + } + while (!data->close) { + XEvent e; + bool draw; + + if (data->xsettings) { + xsettings_client_process_event(data->xsettings, &e); + } + + // 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->fiddled_control) { + data->fiddled_control->selected = false; + } + data->fiddled_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->fiddled_control; + data->fiddled_control = GetControlMouseIsOn(data, SDL_lroundf((e.xbutton.x/scale)*iscale), SDL_lroundf((e.xbutton.y/scale)*iscale)); + if (previous_control) { + previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + draw = true; + } + if (data->fiddled_control) { + if (data->fiddled_control->dynamic) { + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_HOVER; + draw = true; + } else { + data->fiddled_control = NULL; + } + } + } + break; + case ButtonPress: + previous_control = data->fiddled_control; + if (previous_control) { + previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; + draw = true; + } + if (e.xbutton.button == Button1) { + data->fiddled_control = GetControlMouseIsOn(data, SDL_lroundf((e.xbutton.x/scale)*iscale), SDL_lroundf((e.xbutton.y/scale)*iscale)); + if (data->fiddled_control) { + data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; + draw = true; + } + } + break; + case ButtonRelease: + if ((e.xbutton.button == Button1) && (data->fiddled_control)) { + SDL_ToolkitControlX11 *control; + + control = GetControlMouseIsOn(data, SDL_lroundf((e.xbutton.x/scale)*iscale), SDL_lroundf((e.xbutton.y/scale)*iscale)); + if (data->fiddled_control == control) { + if (data->fiddled_control->func_on_state_change) { + data->fiddled_control->func_on_state_change(data->fiddled_control); + } + data->fiddled_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]->selected) { + 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); + } + } + } else if (key == XK_Tab || key == XK_Left || key == XK_Right) { + data->focused_control->selected = false; + draw = true; + for (i = 0; i < data->dyn_controls_sz; i++) { + if (data->dyn_controls[i] == data->focused_control) { + int next_index; + + if (key == XK_Left) { + next_index = i - 1; + } else { + next_index = i + 1; + } + if ((next_index >= data->dyn_controls_sz) || (next_index < 0)) { + if (key == XK_Right || key == XK_Left) { + next_index = i; + } else { + next_index = 0; + } + } + data->focused_control = data->dyn_controls[next_index]; + data->focused_control->selected = true; + break; + } + } + } + break; + } + + } + + if (draw) { + X11Toolkit_DrawWindow(data); + } + } + +} + +void X11Toolkit_ResizeWindow(SDL_ToolkitWindowX11 *data, int w, int h) { + if (!data->pixmap) { + data->window_width = w; + data->window_height = h; + } else { + data->window_width = SDL_lroundf((w/data->iscale) * data->scale); + data->window_height = SDL_lroundf((h/data->iscale) * data->scale); + data->pixmap_width = w; + data->pixmap_height = h; + X11_XFreePixmap(data->display, data->drawable); + data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); + } + + 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->window->iscale; + control->rect.h -= 2 * control->window->iscale; + 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->window->iscale), control->rect.y + (2* control->window->iscale), 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->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 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->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 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->window->iscale), control->rect.y+(1* control->window->iscale), control->rect.w-(2* control->window->iscale), control->rect.h-(2* control->window->iscale), 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->window->iscale; + control->rect.h += 2 * control->window->iscale; +} + +static void X11Toolkit_CalculateIconControl(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitIconControlX11 *control; + int icon_char_w; + int icon_char_h; + int icon_char_max; + + control = (SDL_ToolkitIconControlX11 *)base_control; + 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->window->iscale; + base_control->rect.h = icon_char_h + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; + 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->window->iscale; + base_control->rect.h += 2 * base_control->window->iscale; +} + +static void X11Toolkit_OnScaleChangeIconControl(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitIconControlX11 *control; + char *font; + + control = (SDL_ToolkitIconControlX11 *)base_control; + X11_XFreeFont(base_control->window->display, control->icon_char_font); + SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * base_control->window->iscale); + control->icon_char_font = X11_XLoadQueryFont(base_control->window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * base_control->window->iscale); + control->icon_char_font = X11_XLoadQueryFont(base_control->window->display, font); + SDL_free(font); + } +} + +SDL_ToolkitControlX11 *X11Toolkit_CreateIconControl(SDL_ToolkitWindowX11 *window, SDL_MessageBoxFlags flags) { + SDL_ToolkitIconControlX11 *control; + SDL_ToolkitControlX11 *base_control; + char *font; + + + /* Create control struct */ + 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; + } + + /* Fill out struct */ + 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_calc_size = X11Toolkit_CalculateIconControl; + base_control->func_on_scale_change = X11Toolkit_OnScaleChangeIconControl; + 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; + + /* Load font */ + SDL_asprintf(&font, g_IconFont, G_ICONFONT_SIZE * window->iscale); + control->icon_char_font = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); + control->icon_char_font = X11_XLoadQueryFont(window->display, font); + SDL_free(font); + if (!control->icon_char_font) { + SDL_free(control); + return NULL; + } + } + + /* Set colors */ + 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); + + /* Sizing and positioning */ + X11Toolkit_CalculateIconControl(base_control); + + X11Toolkit_AddControlToWindow(window, base_control); + return base_control; +} + +bool X11Toolkit_NotifyControlOfSizeChange(SDL_ToolkitControlX11 *control) { + if (control->func_calc_size) { + control->func_calc_size(control); + return true; + } else { + return false; + } +} + +static void X11Toolkit_CalculateButtonControl(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 * control->window->iscale; + } else { + button_control->text_rect.y -= 4 * control->window->iscale; + } +} + + +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->window->iscale), control->rect.h - (1* control->window->iscale)); + + + 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->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 2 * control->window->iscale); + + 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->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + 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->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + } else { + if (control->selected) { + 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->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + 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->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + + 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->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 5 * control->window->iscale, control->rect.h - 5 * control->window->iscale); + + 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->window->iscale, control->rect.y + 3 * control->window->iscale, + control->rect.w - 6 * control->window->iscale, control->rect.h - 6 * control->window->iscale); + } 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->window->iscale, control->rect.h - 1 * control->window->iscale); + + 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->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 2 * control->window->iscale, control->rect.h - 2 * control->window->iscale); + + 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->window->iscale, control->rect.y + 1 * control->window->iscale, + control->rect.w - 3 * control->window->iscale, control->rect.h - 3 * control->window->iscale); + + 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->window->iscale, control->rect.y + 2 * control->window->iscale, + control->rect.w - 4 * control->window->iscale, control->rect.h - 4 * control->window->iscale); + } + } + + 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_DestroyGenericControl(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_calc_size = X11Toolkit_CalculateButtonControl; + base_control->func_draw = X11Toolkit_DrawButtonControl; + base_control->func_on_state_change = X11Toolkit_OnButtonControlStateChange; + base_control->func_free = X11Toolkit_DestroyGenericControl; + base_control->func_on_scale_change = NULL; + 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; + base_control->selected = 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 * window->iscale + control->text_rect.w; + base_control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * window->iscale + control->text_rect.h; + control->text_rect.x = control->text_rect.y = 0; + X11Toolkit_CalculateButtonControl(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]); + } + } + SDL_free(data->controls); + SDL_free(data->dyn_controls); + + if (data->pixmap) { + X11_XFreePixmap(data->display, data->drawable); + } + +#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 && !data->pixmap) { + X11_XdbeDeallocateBackBufferName(data->display, data->buf); + } +#endif + + if (data->xsettings) { + xsettings_client_destroy(data->xsettings); + } + + 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); +} + + +static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control) { + SDL_ToolkitLabelControlX11 *control; + int ascent; + int descent; + int w; + int h; + int i; + + control = (SDL_ToolkitLabelControlX11 *)base_control; + for (i = 0; i < control->sz; i++) { + GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent); + + 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; + } + } +} + +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_calc_size = X11Toolkit_CalculateLabelControl; + base_control->func_free = X11Toolkit_DestroyLabelControl; + base_control->func_on_scale_change = NULL; + 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..50eaaf067a97f --- /dev/null +++ b/src/video/x11/SDL_x11toolkit.h @@ -0,0 +1,147 @@ +/* + 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_x11settings.h" +#include "SDL_x11toolkit.h" +#include "xsettings-client.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; + XVisualInfo vi; + int depth; + Colormap cmap; + GC ctx; + bool utf8; + bool pixmap; +#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. + int pixmap_width; + int pixmap_height; + + XSettingsClient *xsettings; + bool xsettings_first_time; + int iscale; + float scale; + + 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 *fiddled_control; + struct SDL_ToolkitControlX11 **controls; + size_t controls_sz; + struct SDL_ToolkitControlX11 **dyn_controls; + size_t dyn_controls_sz; + + void *cb_data; + void (*cb_on_scale_change)(struct SDL_ToolkitWindowX11 *, void *); +} 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_calc_size)(struct SDL_ToolkitControlX11 *); + void (*func_on_scale_change)(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_NotifyControlOfSizeChange(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_