/* 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_list.h" #include "SDL_x11video.h" #ifdef SDL_USE_LIBDBUS #include "../../core/linux/SDL_system_theme.h" #endif #ifdef HAVE_FRIBIDI_H #include "../../core/unix/SDL_fribidi.h" #endif #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 #define SDL_GRAB 1 typedef struct SDL_ToolkitIconControlX11 { SDL_ToolkitControlX11 parent; /* Icon type */ SDL_MessageBoxFlags flags; char icon_char; /* Font */ XFontStruct *icon_char_font; int icon_char_x; int icon_char_y; int icon_char_a; int icon_char_h; /* Colors */ 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; /* Data */ const SDL_MessageBoxButtonData *data; /* Text */ SDL_Rect text_rect; int text_a; int text_d; int str_sz; #ifdef HAVE_FRIBIDI_H char *text; bool free_text; #endif /* Callback */ void *cb_data; void (*cb)(struct SDL_ToolkitControlX11 *, void *); } SDL_ToolkitButtonControlX11; typedef struct SDL_ToolkitLabelControlX11 { SDL_ToolkitControlX11 parent; char **lines; int *y; size_t *szs; size_t sz; #ifdef HAVE_FRIBIDI_H int *x; int *w; bool *free_lines; FriBidiParType *par_types; #endif } SDL_ToolkitLabelControlX11; typedef struct SDL_ToolkitMenuBarControlX11 { SDL_ToolkitControlX11 parent; SDL_ListNode *menu_items; } SDL_ToolkitMenuBarControlX11; typedef struct SDL_ToolkitMenuControlX11 { SDL_ToolkitControlX11 parent; SDL_ListNode *menu_items; XColor xcolor_check_bg; } SDL_ToolkitMenuControlX11; /* Font for icon control */ static const char *g_IconFont = "-*-*-bold-r-normal-*-%d-*-*-*-*-*-iso8859-1[33 88 105]"; #define G_ICONFONT_SIZE 22 /* General UI font */ static const char g_ToolkitFontLatin1[] = "-*-*-medium-r-normal--0-%d-*-*-p-0-iso8859-1"; static const char g_ToolkitFontLatin1Fallback[] = "-*-*-*-*-*--*-*-*-*-*-*-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--*-%d-*-*-*-*-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 140 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, }; #ifdef SDL_USE_LIBDBUS static const SDL_MessageBoxColor g_default_colors_dark[SDL_MESSAGEBOX_COLOR_COUNT] = { { 20, 20, 20 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, { 192, 192, 192 }, // SDL_MESSAGEBOX_COLOR_TEXT, { 12, 12, 12 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, { 20, 20, 20 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, { 36, 36, 36 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, }; #if 0 static const SDL_MessageBoxColor g_default_colors_dark_high_contrast[SDL_MESSAGEBOX_COLOR_COUNT] = { { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, { 255, 255, 255 }, // SDL_MESSAGEBOX_COLOR_TEXT, { 20, 235, 255 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, { 125, 5, 125 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, }; static const SDL_MessageBoxColor g_default_colors_light_high_contrast[SDL_MESSAGEBOX_COLOR_COUNT] = { { 255, 255, 255 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_TEXT, { 0, 0, 0 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, { 255, 255, 255 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, { 20, 230, 255 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, }; #endif #endif static int g_shm_error; static int (*g_old_error_handler)(Display *, XErrorEvent *) = NULL; static int X11Toolkit_SharedMemoryErrorHandler(Display *d, XErrorEvent *e) { if (e->error_code == BadAccess || e->error_code == BadRequest) { g_shm_error = True; return 0; } return g_old_error_handler(d, e); } int X11Toolkit_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 X11Toolkit_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 = X11Toolkit_SettingsGetInt(client, "Gdk/WindowScalingFactor", -1); // The Xft/DPI key is stored in increments of 1024th if (scale_factor <= 0.0) { int dpi = X11Toolkit_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 X11Toolkit_InitWindowPixmap(SDL_ToolkitWindowX11 *data) { if (data->pixmap) { #ifndef NO_SHARED_MEMORY if (!data->shm_pixmap) { data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); } #else data->drawable = X11_XCreatePixmap(data->display, data->window, data->pixmap_width, data->pixmap_height, data->depth); #endif #ifndef NO_SHARED_MEMORY if (data->shm) { data->image = X11_XShmCreateImage(data->display, data->visual, data->depth, ZPixmap, NULL, &data->shm_info, data->pixmap_width, data->pixmap_height); if (data->image) { data->shm_bytes_per_line = data->image->bytes_per_line; data->shm_info.shmid = shmget(IPC_PRIVATE, data->image->bytes_per_line * data->image->height, IPC_CREAT | 0777); if (data->shm_info.shmid < 0) { XDestroyImage(data->image); data->image = NULL; data->shm = false; } data->shm_info.readOnly = False; data->shm_info.shmaddr = data->image->data = (char *)shmat(data->shm_info.shmid, 0, 0); if (((signed char *)data->shm_info.shmaddr) == (signed char *)-1) { XDestroyImage(data->image); data->shm = false; data->image = NULL; } g_shm_error = False; g_old_error_handler = X11_XSetErrorHandler(X11Toolkit_SharedMemoryErrorHandler); X11_XShmAttach(data->display, &data->shm_info); X11_XSync(data->display, False); X11_XSetErrorHandler(g_old_error_handler); if (g_shm_error) { XDestroyImage(data->image); shmdt(data->shm_info.shmaddr); shmctl(data->shm_info.shmid, IPC_RMID, 0); data->image = NULL; data->shm = false; } if (data->shm_pixmap) { data->drawable = X11_XShmCreatePixmap(data->display, data->window, data->shm_info.shmaddr, &data->shm_info, data->pixmap_width, data->pixmap_height, data->depth); if (data->drawable == None) { data->shm_pixmap = False; } else { XDestroyImage(data->image); data->image = NULL; } } shmctl(data->shm_info.shmid, IPC_RMID, 0); } else { data->shm = false; } } #endif } } static void X11Toolkit_InitWindowFonts(SDL_ToolkitWindowX11 *window) { #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; if (SDL_strstr(g_ToolkitFont[i_font], "%d")) { try_load_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 (!window->font_set) { if (window->scale != 0 && window->iscale > 0) { window->iscale = (int)SDL_ceilf(window->scale); window->scale = 0; } else { window->iscale--; } goto try_load_font; } } else { window->font_set = X11_XCreateFontSet(window->display, g_ToolkitFont[i_font], &missing, &num_missing, NULL); } if (missing) { X11_XFreeStringList(missing); } if (window->font_set) { break; } } if (!window->font_set) { goto load_font_traditional; } else { #ifdef HAVE_FRIBIDI_H window->do_shaping = !X11_XContextDependentDrawing(window->font_set); #endif } } else #endif { char *font; load_font_traditional: window->utf8 = false; SDL_asprintf(&font, g_ToolkitFontLatin1, G_TOOLKITFONT_SIZE * window->iscale); window->font_struct = X11_XLoadQueryFont(window->display, font); SDL_free(font); if (!window->font_struct) { if (window->iscale > 0) { if (window->scale != 0) { window->iscale = (int)SDL_ceilf(window->scale); window->scale = 0; } else { window->iscale--; } goto load_font_traditional; } else { window->font_struct = X11_XLoadQueryFont(window->display, g_ToolkitFontLatin1Fallback); } } } } static void X11Toolkit_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 = false; bool pixmap_already_setup = false; if (window->pixmap) { pixmap_already_setup = true; } else { dbe_already_setup = true; } /* set scale vars */ window->scale = X11Toolkit_GetUIScale(window->xsettings, window->display); window->iscale = (int)SDL_ceilf(window->scale); if (SDL_roundf(window->scale) == window->scale) { window->scale = 0; } /* 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); } X11Toolkit_InitWindowFonts(window); /* 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); X11Toolkit_InitWindowPixmap(window); } else { if (!dbe_already_setup) { X11_XFreePixmap(window->display, window->drawable); #ifndef NO_SHARED_MEMORY if (window->image) { XDestroyImage(window->image); window->image = NULL; } #endif #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 } } /* notify controls */ for (i = 0; i < window->controls_sz; i++) { window->controls[i]->do_size = true; 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]); } window->controls[i]->do_size = false; } /* notify cb */ if (window->cb_on_scale_change) { window->cb_on_scale_change(window, window->cb_data); } /* update ev scales */ if (!window->pixmap) { window->ev_scale = window->ev_iscale = 1; } else { window->ev_scale = window->scale; window->ev_iscale = window->iscale; } } } static void X11Toolkit_GetTextWidthHeightForFont(XFontStruct *font, const char *str, int nbytes, int *pwidth, int *pheight, int *ascent) { XCharStruct text_structure; int font_direction, font_ascent, 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; *ascent = text_structure.ascent; } static void X11Toolkit_GetTextWidthHeight(SDL_ToolkitWindowX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *ascent, int *descent, int *font_height) { #ifdef X_HAVE_UTF8_STRING if (data->utf8) { XRectangle overall_ink, overall_logical; X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); *pwidth = overall_logical.width; *pheight = overall_logical.height; *ascent = -overall_logical.y; *descent = overall_logical.height - *ascent; if (font_height) { XFontSetExtents *extents; extents = X11_XExtentsOfFontSet(data->font_set); *font_height = extents->max_logical_extent.height; } } else #endif { XCharStruct text_structure; int font_direction, font_ascent, font_descent; X11_XTextExtents(data->font_struct, str, nbytes, &font_direction, &font_ascent, &font_descent, &text_structure); *pwidth = text_structure.width; *pheight = text_structure.ascent + text_structure.descent; *ascent = text_structure.ascent; *descent = text_structure.descent; if (font_height) { *font_height = font_ascent + font_descent; } } } SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_ToolkitWindowX11 *tkparent, SDL_ToolkitWindowModeX11 mode, const SDL_MessageBoxColor *colorhints, bool create_new_display) { SDL_ToolkitWindowX11 *window; int i; #ifdef SDL_USE_LIBDBUS SDL_SystemTheme theme; #endif #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; } window->mode = mode; window->tk_parent = tkparent; #if SDL_SET_LOCALE if (mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { 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->parent_device = NULL; if (create_new_display) { window->display = X11_XOpenDisplay(NULL); window->display_close = true; if (!window->display) { ErrorFreeRetNull("Couldn't open X11 display", window); } } else { if (parent) { window->parent_device = SDL_GetVideoDevice(); window->display = window->parent_device->internal->display; window->display_close = false; } else if (tkparent) { window->display = tkparent->display; window->display_close = false; } else { window->display = X11_XOpenDisplay(NULL); window->display_close = true; 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 #ifndef NO_SHARED_MEMORY window->shm_pixmap = False; window->shm = X11_XShmQueryExtension(window->display) ? SDL_X11_HAVE_SHM : false; if (window->shm) { int major; int minor; X11_XShmQueryVersion(window->display, &major, &minor, &window->shm_pixmap); if (window->shm_pixmap) { if (X11_XShmPixmapFormat(window->display) != ZPixmap) { window->shm_pixmap = False; } } } #endif /* Scale/Xsettings */ window->pixmap = false; window->xsettings_first_time = true; window->xsettings = xsettings_client_new(window->display, DefaultScreen(window->display), X11Toolkit_SettingsNotify, NULL, window); window->xsettings_first_time = false; window->scale = X11Toolkit_GetUIScale(window->xsettings, window->display); window->iscale = (int)SDL_ceilf(window->scale); if (SDL_roundf(window->scale) == window->scale) { window->scale = 0; } /* Fonts */ X11Toolkit_InitWindowFonts(window); /* Color hints */ #ifdef SDL_USE_LIBDBUS theme = SDL_SYSTEM_THEME_LIGHT; if (SDL_SystemTheme_Init()) { theme = SDL_SystemTheme_Get(); } #endif if (!colorhints) { #ifdef SDL_USE_LIBDBUS switch (theme) { case SDL_SYSTEM_THEME_DARK: colorhints = g_default_colors_dark; break; #if 0 case SDL_SYSTEM_THEME_LIGHT_HIGH_CONTRAST: colorhints = g_default_colors_light_high_contrast; break; case SDL_SYSTEM_THEME_DARK_HIGH_CONTRAST: colorhints = g_default_colors_dark_high_contrast; break; #endif default: colorhints = g_default_colors; } #else colorhints = g_default_colors; #endif } window->color_hints = colorhints; /* 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->xcolor_disabled_text.flags = DoRed|DoGreen|DoBlue; window->xcolor_disabled_text.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].red + 19500, 0, 65535); window->xcolor_disabled_text.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].green + 19500, 0, 65535); window->xcolor_disabled_text.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].blue + 19500, 0, 65535); /* Screen */ window->parent = parent; if (parent) { SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(parent); window->screen = displaydata->screen; } else { window->screen = DefaultScreen(window->display); } /* Visuals */ if (mode == SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { window->visual = parent->internal->visual; window->cmap = parent->internal->colormap; X11_GetVisualInfoFromVisual(window->display, window->visual, &window->vi); window->depth = window->vi.depth; } else { 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); } /* Allocate colors */ 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); X11_XAllocColor(window->display, window->cmap, &window->xcolor_disabled_text); /* Control list */ window->has_focus = false; window->controls = NULL; window->controls_sz = 0; window->dyn_controls_sz = 0; window->fiddled_control = NULL; window->dyn_controls = NULL; /* Menu windows */ window->popup_windows = NULL; #ifdef HAVE_FRIBIDI_H window->fribidi = SDL_FriBidi_Create(); #endif return window; } static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_ToolkitControlX11 *control) { /* Add to controls list */ 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 dynamic, add it to the dynamic controls list too */ 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 selected, set currently focused control to it */ if (control->selected) { window->focused_control = control; } } bool X11Toolkit_CreateWindowRes(SDL_ToolkitWindowX11 *data, int w, int h, int cx, int cy, char *title) { int x, y; XSizeHints *sizehints; XSetWindowAttributes wnd_attr; Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG, _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, _NET_WM_WINDOW_TYPE_TOOLTIP; SDL_WindowData *windowdata = NULL; Display *display = data->display; XGCValues ctx_vals; Window root_win; Window parent_win; unsigned long gcflags = GCForeground | GCBackground; unsigned long valuemask; #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; } valuemask = CWEventMask | CWColormap; data->event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | StructureNotifyMask | FocusChangeMask | PointerMotionMask; wnd_attr.event_mask = data->event_mask; wnd_attr.colormap = data->cmap; if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { valuemask |= CWOverrideRedirect | CWSaveUnder; wnd_attr.save_under = True; wnd_attr.override_redirect = True; } root_win = RootWindow(display, data->screen); if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { parent_win = windowdata->xwindow; } else { parent_win = root_win; } data->window = X11_XCreateWindow( display, parent_win, 0, 0, data->window_width, data->window_height, 0, data->depth, InputOutput, data->visual, valuemask, &wnd_attr); if (data->window == None) { return SDL_SetError("Couldn't create X window"); } if (windowdata && data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { 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); } if (windowdata && data->mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) { X11_XSetTransientForHint(display, data->window, windowdata->xwindow); } if (data->tk_parent) { X11_XSetTransientForHint(display, data->window, data->tk_parent->window); } SDL_X11_SetWindowTitle(display, data->window, title); // Let the window manager the type of the window if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG) { _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); } else if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU) { _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); _NET_WM_WINDOW_TYPE_DROPDOWN_MENU = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, 1); } else if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); _NET_WM_WINDOW_TYPE_TOOLTIP = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_TOOLTIP", False); X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&_NET_WM_WINDOW_TYPE_TOOLTIP, 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 (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { x = cx; y = cy; goto MOVEWINDOW; } 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); data->window_x = x; data->window_y = 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 X11Toolkit_InitWindowPixmap(data); 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"); } data->close = false; data->key_control_esc = data->key_control_enter = NULL; if (!data->pixmap) { data->ev_scale = data->ev_iscale = 1; } else { data->ev_scale = data->scale; data->ev_iscale = data->iscale; } #if SDL_GRAB if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { X11_XGrabPointer(display, data->window, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); X11_XGrabKeyboard(display, data->window, False, GrabModeAsync, GrabModeAsync, CurrentTime); } #endif return true; } static void X11Toolkit_DrawWindow(SDL_ToolkitWindowX11 *data) { SDL_Rect rect; 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) { SDL_Surface *scale_surface; rect.x = rect.y = 0; rect.w = data->window_width; rect.h = data->window_height; #ifndef NO_SHARED_MEMORY if (data->shm) { if (data->shm_pixmap) { X11_XFlush(data->display); X11_XSync(data->display, false); scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), data->shm_info.shmaddr, data->shm_bytes_per_line); SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); SDL_DestroySurface(scale_surface); X11_XCopyArea(data->display, data->drawable, data->window, data->ctx, 0, 0, data->window_width, data->window_height, 0, 0); } else { X11_XShmGetImage(data->display, data->drawable, data->image, 0, 0, AllPlanes); scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), data->image->data, data->image->bytes_per_line); SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); X11_XShmPutImage(data->display, data->window, data->ctx, data->image, 0, 0, 0, 0, data->window_width, data->window_height, False); } } else #endif { XImage *image; image = X11_XGetImage(data->display, data->drawable, 0, 0 , data->pixmap_width, data->pixmap_height, AllPlanes, ZPixmap); scale_surface = SDL_CreateSurfaceFrom(data->pixmap_width, data->pixmap_height, X11_GetPixelFormatFromVisualInfo(data->display, &data->vi), image->data, image->bytes_per_line); SDL_BlitSurfaceScaled(scale_surface, NULL, scale_surface, &rect, SDL_SCALEMODE_LINEAR); X11_XPutImage(data->display, data->window, data->ctx, image, 0, 0, 0, 0, data->window_width, data->window_height); XDestroyImage(image); SDL_DestroySurface(scale_surface); } } X11_XFlush(data->display); } static SDL_ToolkitControlX11 *X11Toolkit_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; } // NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef static Bool X11Toolkit_EventTest(Display *display, XEvent *event, XPointer arg) { SDL_ToolkitWindowX11 *data = (SDL_ToolkitWindowX11 *)arg; if (event->xany.display != data->display) { return False; } if (event->xany.window == data->window) { return True; } return False; } void X11Toolkit_ProcessWindowEvents(SDL_ToolkitWindowX11 *data, XEvent *e) { /* 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)) { return; } data->draw = false; data->e = e; switch (e->type) { case Expose: data->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 (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; } break; case MotionNotify: if (data->has_focus) { data->previous_control = data->fiddled_control; data->fiddled_control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); if (data->previous_control) { data->previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; if (data->previous_control->func_on_state_change) { data->previous_control->func_on_state_change(data->previous_control); } data->draw = true; } if (data->fiddled_control) { if (data->fiddled_control->dynamic) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_HOVER; if (data->fiddled_control->func_on_state_change) { data->fiddled_control->func_on_state_change(data->fiddled_control); } data->draw = true; } else { data->fiddled_control = NULL; } } } break; case ButtonPress: data->previous_control = data->fiddled_control; if (data->previous_control) { data->previous_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL; if (data->previous_control->func_on_state_change) { data->previous_control->func_on_state_change(data->previous_control); } data->draw = true; } if (e->xbutton.button == Button1) { data->fiddled_control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); if (data->fiddled_control) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD; if (data->fiddled_control->func_on_state_change) { data->fiddled_control->func_on_state_change(data->fiddled_control); } data->draw = true; } } break; case ButtonRelease: if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { int cx; int cy; cx = e->xbutton.x; cy = e->xbutton.y; if (cy < 0 || cx < 0) { data->close = true; } if (cy > data->window_height || cx > data->window_width) { data->close = true; } } if ((e->xbutton.button == Button1) && (data->fiddled_control)) { SDL_ToolkitControlX11 *control; control = X11Toolkit_GetControlMouseIsOn(data, SDL_lroundf((e->xbutton.x/ data->ev_scale)* data->ev_iscale), SDL_lroundf((e->xbutton.y/ data->ev_scale)* data->ev_iscale)); if (data->fiddled_control == control) { data->fiddled_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; 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; data->draw = true; } } break; case KeyPress: data->last_key_pressed = X11_XLookupKeysym(&e->xkey, 0); if (data->last_key_pressed == XK_Escape) { for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { if(data->controls[data->ev_i]->is_default_esc) { data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; data->draw = true; data->key_control_esc = data->controls[data->ev_i]; } } } else if ((data->last_key_pressed == XK_Return) || (data->last_key_pressed == XK_KP_Enter)) { for (data->ev_i = 0; data->ev_i < data->controls_sz; data->ev_i++) { if(data->controls[data->ev_i]->selected) { data->controls[data->ev_i]->state = SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED; data->draw = true; data->key_control_enter = data->controls[data->ev_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 != data->last_key_pressed) { break; } if (key == XK_Escape) { if (data->key_control_esc) { if (data->key_control_esc->func_on_state_change) { data->key_control_esc->func_on_state_change(data->key_control_esc); } } } else if ((key == XK_Return) || (key == XK_KP_Enter)) { if (data->key_control_enter) { if (data->key_control_enter->func_on_state_change) { data->key_control_enter->func_on_state_change(data->key_control_enter); } } } else if (key == XK_Tab || key == XK_Left || key == XK_Right) { if (data->focused_control) { data->focused_control->selected = false; } data->draw = true; for (data->ev_i = 0; data->ev_i < data->dyn_controls_sz; data->ev_i++) { if (data->dyn_controls[data->ev_i] == data->focused_control) { int next_index; if (key == XK_Left) { next_index = data->ev_i - 1; } else { next_index = data->ev_i + 1; } if ((next_index >= data->dyn_controls_sz) || (next_index < 0)) { if (key == XK_Right || key == XK_Left) { next_index = data->ev_i; } else { next_index = 0; } } data->focused_control = data->dyn_controls[next_index]; data->focused_control->selected = true; break; } } } break; } } if (data->draw) { X11Toolkit_DrawWindow(data); } } void X11Toolkit_DoWindowEventLoop(SDL_ToolkitWindowX11 *data) { while (!data->close) { XEvent e; /* Process settings events */ X11_XPeekEvent(data->display, &e); if (data->xsettings) { xsettings_client_process_event(data->xsettings, &e); } /* Do actual event loop */ X11_XIfEvent(data->display, &e, X11Toolkit_EventTest, (XPointer)data); X11Toolkit_ProcessWindowEvents(data, &e); } } 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); X11Toolkit_InitWindowPixmap(data); } 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_wh; control = (SDL_ToolkitIconControlX11 *)base_control; X11Toolkit_GetTextWidthHeightForFont(control->icon_char_font, &control->icon_char, 1, &icon_char_w, &control->icon_char_h, &control->icon_char_a); base_control->rect.w = icon_char_w; base_control->rect.h = control->icon_char_h; icon_wh = SDL_max(icon_char_w, control->icon_char_h) + SDL_TOOLKIT_X11_ELEMENT_PADDING * 2 * base_control->window->iscale; base_control->rect.w = icon_wh; base_control->rect.h = icon_wh; base_control->rect.y = 0; base_control->rect.x = 0; control->icon_char_y = control->icon_char_a + (base_control->rect.h - control->icon_char_h)/2; control->icon_char_x = (base_control->rect.w - icon_char_w)/2; base_control->rect.w += 2 * base_control->window->iscale; base_control->rect.h += 2 * base_control->window->iscale; } static void X11Toolkit_OnIconControlScaleChange(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_OnIconControlScaleChange; 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; if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red > 32896) { control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 12500, 0, 65535); } else if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red == 0) { control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red + 9000, 0, 65535); } else { control->xcolor_bg_shadow.red = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].red - 3000, 0, 65535); } if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green > 32896) { control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 12500, 0, 65535); } else if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green == 0) { control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green + 9000, 0, 65535); } else { control->xcolor_bg_shadow.green = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].green - 3000, 0, 65535); } if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue > 32896) { control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 12500, 0, 65535); } else if (window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue == 0) { control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue + 9000, 0, 65535); } else { control->xcolor_bg_shadow.blue = SDL_clamp(window->xcolor[SDL_MESSAGEBOX_COLOR_BACKGROUND].blue - 3000, 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; int text_d; button_control = (SDL_ToolkitButtonControlX11 *)control; X11Toolkit_GetTextWidthHeight(control->window, button_control->data->text, button_control->str_sz, &button_control->text_rect.w, &button_control->text_rect.h, &button_control->text_a, &text_d, NULL); if (control->do_size) { control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.w; control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.h; } 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; } static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) { SDL_ToolkitButtonControlX11 *button_control; char *text; button_control = (SDL_ToolkitButtonControlX11 *)control; #ifdef HAVE_FRIBIDI_H text = button_control->text; #else text = (char *)button_control->data->text; #endif 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 || control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD) { 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, 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, 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 && control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED) { button_control->cb(control, button_control->cb_data); } } static void X11Toolkit_DestroyButtonControl(SDL_ToolkitControlX11 *control) { #ifdef HAVE_FRIBIDI_H SDL_ToolkitButtonControlX11 *button_control; button_control = (SDL_ToolkitButtonControlX11 *)control; if (button_control->free_text) { SDL_free(button_control->text); } #endif 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_DestroyButtonControl; 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; } base_control->do_size = false; control->data = data; control->str_sz = SDL_strlen(control->data->text); #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { control->text = SDL_FriBidi_Process(base_control->window->fribidi, (char *)control->data->text, control->str_sz, base_control->window->do_shaping, NULL); if (control->text) { control->free_text = true; control->str_sz = SDL_strlen(control->text); } else { control->text = (char *)control->data->text; control->free_text = false; } } else { control->text = (char *)control->data->text; control->free_text = false; } #endif control->cb = NULL; X11Toolkit_GetTextWidthHeight(base_control->window, control->data->text, control->str_sz, &control->text_rect.w, &control->text_rect.h, &control->text_a, &text_d, NULL); 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; } #if SDL_GRAB if (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_MENU || data->mode== SDL_TOOLKIT_WINDOW_MODE_X11_TOOLTIP) { X11_XUngrabPointer(data->display, CurrentTime); X11_XUngrabKeyboard(data->display, CurrentTime); } #endif for (i = 0; i < data->controls_sz; i++) { if (data->controls[i]->func_free) { data->controls[i]->func_free(data->controls[i]); } } if (data->controls) { SDL_free(data->controls); } if (data->dyn_controls) { SDL_free(data->dyn_controls); } if (data->popup_windows) { SDL_ListClear(&data->popup_windows); } if (data->pixmap) { X11_XFreePixmap(data->display, data->drawable); } #ifndef NO_SHARED_MEMORY if (data->pixmap && data->shm) { X11_XShmDetach(data->display, &data->shm_info); if (!data->shm_pixmap) { XDestroyImage(data->image); } shmdt(data->shm_info.shmaddr); } #endif #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; } if (data->display_close) { X11_XCloseDisplay(data->display); } data->display = NULL; } #if SDL_SET_LOCALE if (data->origlocale && (data->mode == SDL_TOOLKIT_WINDOW_MODE_X11_DIALOG)) { (void)setlocale(LC_ALL, data->origlocale); SDL_free(data->origlocale); } #endif #ifdef HAVE_FRIBIDI_H SDL_FriBidi_Destroy(data->fribidi); #endif SDL_free(data); } static int X11Toolkit_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; int x; 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++) { x = control->rect.x; #ifdef HAVE_FRIBIDI_H if (control->window->fribidi) { x += label_control->x[i]; } #endif #ifdef X_HAVE_UTF8_STRING if (control->window->utf8) { X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx, 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, 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; #ifdef HAVE_FRIBIDI_H if (control->window->fribidi) { int i; for (i = 0; i < label_control->sz; i++) { if (label_control->free_lines[i]) { SDL_free(label_control->lines[i]); } } SDL_free(label_control->x); SDL_free(label_control->free_lines); SDL_free(label_control->w); SDL_free(label_control->par_types); } #endif 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 last_h; int ascent; int descent; int font_h; int w; int h; int i; #ifdef HAVE_FRIBIDI_H FriBidiParType first_ndn_dir; int last_ndn; #endif last_h = 0; control = (SDL_ToolkitLabelControlX11 *)base_control; for (i = 0; i < control->sz; i++) { X11Toolkit_GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent, &font_h); base_control->rect.w = SDL_max(base_control->rect.w, w); if (i > 0) { control->y[i] = font_h + control->y[i-1]; } else { control->y[i] = ascent; } last_h = h; } base_control->rect.h = control->y[control->sz -1] + last_h; #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { first_ndn_dir = FRIBIDI_PAR_LTR; for (i = 0; i < control->sz; i++) { if (control->par_types[i] != FRIBIDI_PAR_ON) { first_ndn_dir = control->par_types[i]; } } last_ndn = -1; for (i = 0; i < control->sz; i++) { switch (control->par_types[i]) { case FRIBIDI_PAR_LTR: control->x[i] = 0; last_ndn = i; break; case FRIBIDI_PAR_RTL: control->x[i] = base_control->rect.w - control->w[i]; last_ndn = i; break; default: if (last_ndn != -1) { if (control->par_types[last_ndn] == FRIBIDI_PAR_RTL) { control->x[i] = base_control->rect.w - control->w[i]; } else { control->x[i] = 0; } } else { if (first_ndn_dir == FRIBIDI_PAR_RTL) { control->x[i] = base_control->rect.w - control->w[i]; } else { control->x[i] = 0; } } } } } #endif } SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8) { SDL_ToolkitLabelControlX11 *control; SDL_ToolkitControlX11 *base_control; #ifdef HAVE_FRIBIDI_H FriBidiParType first_ndn_dir; int last_ndn; #endif int font_h; int last_h; 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 = X11Toolkit_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)); #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { control->x = (int *)SDL_calloc(control->sz, sizeof(int)); control->free_lines = (bool *)SDL_calloc(control->sz, sizeof(bool)); control->par_types = (FriBidiParType *)SDL_calloc(control->sz, sizeof(FriBidiParType)); control->w = (int *)SDL_calloc(control->sz, sizeof(int)); } #endif last_h = 0; 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; #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { control->lines[i] = SDL_FriBidi_Process(base_control->window->fribidi, utf8, length, base_control->window->do_shaping, &control->par_types[i]); control->szs[i] = SDL_strlen(control->lines[i]); control->free_lines[i] = true; } else #endif { control->lines[i] = utf8; control->szs[i] = length; #ifdef HAVE_FRIBIDI_H control->free_lines[i] = false; #endif } X11Toolkit_GetTextWidthHeight(window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent, &font_h); #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { control->w[i] = w; } #endif base_control->rect.w = SDL_max(base_control->rect.w, w); if (lf && (lf > control->lines[i]) && (lf[-1] == '\r')) { control->szs[i]--; } if (i > 0) { control->y[i] = font_h + control->y[i-1]; } else { control->y[i] = ascent; } last_h = h; utf8 += length + 1; if (!lf) { break; } } base_control->rect.h = control->y[control->sz -1] + last_h; #ifdef HAVE_FRIBIDI_H if (base_control->window->fribidi) { first_ndn_dir = FRIBIDI_PAR_LTR; for (i = 0; i < control->sz; i++) { if (control->par_types[i] != FRIBIDI_PAR_ON) { first_ndn_dir = control->par_types[i]; } } last_ndn = -1; for (i = 0; i < control->sz; i++) { switch (control->par_types[i]) { case FRIBIDI_PAR_LTR: control->x[i] = 0; last_ndn = i; break; case FRIBIDI_PAR_RTL: control->x[i] = base_control->rect.w - control->w[i]; last_ndn = i; break; default: if (last_ndn != -1) { if (control->par_types[last_ndn] == FRIBIDI_PAR_RTL) { control->x[i] = base_control->rect.w - control->w[i]; } else { control->x[i] = 0; } } else { if (first_ndn_dir == FRIBIDI_PAR_RTL) { control->x[i] = base_control->rect.w - control->w[i]; } else { control->x[i] = 0; } } } } } #endif 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 + icon_control->icon_char_h/4; } void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data) { data->close = true; } #endif // SDL_VIDEO_DRIVER_X11