Files
SDL/src/video/x11/SDL_x11toolkit.c

1863 lines
78 KiB
C

/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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"
#include "SDL_x11dyn.h"
#include "SDL_x11toolkit.h"
#include "SDL_x11settings.h"
#include "SDL_x11modes.h"
#include "xsettings-client.h"
#include <X11/keysym.h>
#include <locale.h>
#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;
/* 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;
/* 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;
} 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 18
/* General UI font */
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 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_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 = X11Toolkit_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 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 *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 X11Toolkit_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, SDL_ToolkitWindowX11 *tkparent, SDL_ToolkitWindowModeX11 mode, 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;
}
window->mode = mode;
window->tk_parent = tkparent;
#if SDL_SET_LOCALE
if (mode != SDL_TOOLKIT_WINDOW_MODE_X11_CHILD) {
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
if (parent) {
SDL_VideoData *videodata = SDL_GetVideoDevice()->internal;
window->display = videodata->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
/* 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 (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;
}
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;
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
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");
}
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) {
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 *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);
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;
X11Toolkit_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_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;
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;
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);
//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;
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 || 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,
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 && control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED) {
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;
X11Toolkit_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;
}
#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);
}
#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_CHILD)) {
(void)setlocale(LC_ALL, data->origlocale);
SDL_free(data->origlocale);
}
#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;
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++) {
X11Toolkit_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 = 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));
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;
X11Toolkit_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