#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 <X11/keysym.h>
#include <locale.h>
#define SDL_SET_LOCALE 1
#define SDL_GRAB 1
typedef enum SDL_ToolkitTextTypeX11
{
SDL_TOOLKIT_TEXT_TYPE_X11_GENERIC,
SDL_TOOLKIT_TEXT_TYPE_X11_THAI
} SDL_ToolkitTextTypeX11;
#ifdef HAVE_LIBTHAI_H
typedef struct SDL_ToolkitThaiOverlayX11
{
bool top;
char *str;
size_t sz;
SDL_Rect rect;
} SDL_ToolkitThaiOverlayX11;
#endif
typedef struct SDL_ToolkitTextElementX11
{
SDL_ToolkitTextTypeX11 type;
char *str;
size_t sz;
SDL_Rect rect;
int font_h;
void (*str_free)(void *);
#ifdef HAVE_LIBTHAI_H
SDL_ListNode *thai_overlays;
#endif
} SDL_ToolkitTextElementX11;
typedef struct SDL_ToolkitIconControlX11
{
SDL_ToolkitControlX11 parent;
SDL_MessageBoxFlags flags;
char icon_char;
XFontStruct *icon_char_font;
int icon_char_x;
int icon_char_y;
int icon_char_a;
int icon_char_h;
XColor xcolor_black;
XColor xcolor_red;
XColor xcolor_red_darker;
XColor xcolor_white;
XColor xcolor_yellow;
XColor xcolor_blue;
XColor xcolor_bg_shadow;
} SDL_ToolkitIconControlX11;
typedef struct SDL_ToolkitButtonControlX11
{
SDL_ToolkitControlX11 parent;
const SDL_MessageBoxButtonData *data;
SDL_ListNode *text;
SDL_Rect text_rect;
void *cb_data;
void (*cb)(struct SDL_ToolkitControlX11 *, void *);
} SDL_ToolkitButtonControlX11;
typedef struct SDL_ToolkitLabelControlLineX11
{
SDL_ListNode *text;
SDL_Rect rect;
#ifdef HAVE_FRIBIDI_H
FriBidiParType par;
#endif
} SDL_ToolkitLabelControlLineX11;
typedef struct SDL_ToolkitLabelControlX11
{
SDL_ToolkitControlX11 parent;
SDL_ToolkitLabelControlLineX11 *lines;
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;
static const char *g_IconFont = "-*-*-bold-r-normal-*-%d-*-*-*-*-*-iso8859-1[33 88 105]";
#define G_ICONFONT_SIZE 22
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,*", "-*-*-medium-r-*--*-%d-*-*-*-*-iso10646-1,*", "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1,*", "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1,*", "-*-*-medium-r-normal--*-%d-*-*-*-*-iso8859-1,*", "-*-*-medium-r-*--*-%d-*-*-*-*-iso8859-1,*", "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1,*", "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1,*", NULL
};
#define G_TOOLKITFONT_SIZE 140
static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = {
{ 191, 184, 191 }, { 0, 0, 0 }, { 127, 120, 127 }, { 191, 184, 191 }, { 235, 235, 235 }, };
#ifdef SDL_USE_LIBDBUS
static const SDL_MessageBoxColor g_default_colors_dark[SDL_MESSAGEBOX_COLOR_COUNT] = {
{ 20, 20, 20 }, { 192, 192, 192 }, { 12, 12, 12 }, { 20, 20, 20 }, { 36, 36, 36 }, };
#if 0#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);
}
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;
return;
}
data->shm_info.readOnly = False;
data->shm_info.shmaddr = data->image->data = (char *)shmat(data->shm_info.shmid, NULL, 0);
if (((signed char *)data->shm_info.shmaddr) == (signed char *)-1) {
XDestroyImage(data->image);
data->shm = false;
data->image = NULL;
return;
}
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, NULL);
data->image = NULL;
data->shm = false;
return;
}
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, NULL);
} else {
data->shm = false;
}
}
#endif
}
}
static void X11Toolkit_InitWindowFonts(SDL_ToolkitWindowX11 *window)
{
window->thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_NONE;
window->thai_font = SDL_TOOLKIT_THAI_FONT_X11_CELL;
#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 {
XFontStruct **font_structs;
char **font_names;
int font_sz;
int i;
#ifdef HAVE_FRIBIDI_H
window->do_shaping = !X11_XContextDependentDrawing(window->font_set);
#endif
font_sz = X11_XFontsOfFontSet(window->font_set, &font_structs, &font_names);
for (i = 0; i < font_sz; i++) {
SDL_ToolkitThaiEncodingX11 thai_encoding;
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_NONE;
if (SDL_strstr(font_names[i], "tis620-0")) {
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_TIS;
} else if (SDL_strstr(font_names[i], "tis620-1")) {
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_TIS_MAC;
} else if (SDL_strstr(font_names[i], "tis620-2")) {
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_TIS_WIN;
} else if (SDL_strstr(font_names[i], "iso8859-11")) {
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_8859;
} else if (SDL_strstr(font_names[i], "iso10646-1")) {
thai_encoding = SDL_TOOLKIT_THAI_ENCODING_X11_UNICODE;
}
if (thai_encoding != SDL_TOOLKIT_THAI_ENCODING_X11_NONE) {
XFontStruct *font_struct;
font_struct = X11_XLoadQueryFont(window->display, font_names[i]);
if (font_struct) {
if (font_struct->per_char) {
int glyphs_sz;
int j;
glyphs_sz = (font_struct->max_char_or_byte2 - font_struct->min_char_or_byte2 + 1) * (font_struct->max_byte1 - font_struct->min_byte1 + 1);
for (j = 0; j < glyphs_sz; j++) {
if (font_struct->per_char[j].lbearing < 0) {
window->thai_font = SDL_TOOLKIT_THAI_FONT_X11_OFFSET;
}
}
}
X11_XFreeFont(window->display, font_struct);
}
}
window->thai_encoding = thai_encoding;
}
}
} 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;
}
window->scale = X11_GetGlobalContentScale(window->display, window->xsettings);
window->iscale = (int)SDL_ceilf(window->scale);
if (window->scale < 1) {
window->scale = 1;
}
if (SDL_roundf(window->scale) == window->scale) {
window->scale = 0;
}
#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);
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
}
}
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;
}
if (window->cb_on_scale_change) {
window->cb_on_scale_change(window, window->cb_data);
}
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, int *font_height)
{
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;
if (font_height) {
*font_height = font_ascent + font_descent;
}
}
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;
}
}
}
#ifdef HAVE_FRIBIDI_H
SDL_ListNode *X11Toolkit_MakeTextElements(SDL_ToolkitWindowX11 *data, char *txt, size_t sz, FriBidiParType *par)
#else
SDL_ListNode *X11Toolkit_MakeTextElements(SDL_ToolkitWindowX11 *data, char *txt, size_t sz)
#endif
{
SDL_ListNode *list;
char *str;
char *buffer;
Uint32 cp;
bool thai;
bool free_txt;
free_txt = false;
list = NULL;
thai = 0;
str = txt;
buffer = SDL_malloc(1);
buffer[0] = 0;
#ifdef HAVE_FRIBIDI_H
if (par) {
*par = FRIBIDI_PAR_LTR;
}
if (data->fribidi) {
char *fstr;
fstr = SDL_FriBidi_Process(data->fribidi, str, sz, data->do_shaping, par);
if (fstr) {
txt = fstr;
str = fstr;
sz = SDL_strlen(str);
free_txt = true;
}
}
#endif
while (1) {
char *new;
char utf8[5];
size_t csz;
bool cond;
SDL_zeroa(utf8);
cp = SDL_StepUTF8((const char **)&str, &sz);
cond = (0xe00 <= cp && cp <= 0xe7f) ? true : false;
if (cp == 0 || cond == (thai ? false : true)) {
SDL_ToolkitTextElementX11 *element;
element = SDL_malloc(sizeof(SDL_ToolkitTextElementX11));
if (thai) {
element->type = SDL_TOOLKIT_TEXT_TYPE_X11_THAI;
} else {
element->type = SDL_TOOLKIT_TEXT_TYPE_X11_GENERIC;
}
element->str = SDL_strdup(buffer);
element->sz = SDL_strlen(buffer);
element->str_free = SDL_free;
SDL_ListAdd(&list, element);
SDL_free(buffer);
buffer = SDL_malloc(1);
buffer[0] = 0;
thai = thai ? false : true;
}
if (!cp) {
break;
}
SDL_UCS4ToUTF8(cp, utf8);
csz = SDL_strlen(buffer) + SDL_strlen(utf8) + 1;
new = SDL_malloc(csz);
SDL_strlcpy(new, buffer, csz);
SDL_strlcat(new, utf8, csz);
SDL_free(buffer);
buffer = new;
}
SDL_free(buffer);
if (free_txt) {
SDL_free(txt);
}
return list;
}
void X11Toolkit_ShapeTextElements(SDL_ToolkitWindowX11 *data, SDL_ListNode *list)
{
SDL_ListNode *cursor;
SDL_ToolkitTextElementX11 *prev;
int temp;
cursor = list;
while (cursor) {
SDL_ToolkitTextElementX11 *element;
element = cursor->entry;
#ifdef HAVE_LIBTHAI_H
element->thai_overlays = NULL;
#endif
if (element->type == SDL_TOOLKIT_TEXT_TYPE_X11_THAI) {
if (data->thai_font == SDL_TOOLKIT_THAI_FONT_X11_OFFSET) {
X11Toolkit_GetTextWidthHeight(data, element->str, element->sz, &element->rect.w, &element->rect.h, &element->rect.y, &temp, NULL);
} else {
#ifdef HAVE_LIBTHAI_H
if (data->th) {
struct thcell_t *cells;
char *tis_str;
char *base_tis_str;
size_t cells_sz;
size_t base_tis_str_sz;
size_t tis_str_sz;
tis_str = SDL_iconv_string("TIS-620", "UTF-8", element->str, element->sz);
cells_sz = tis_str_sz = SDL_strlen(tis_str);
cells = SDL_calloc(cells_sz, sizeof(struct thcell_t));
data->th->make_cells((const thchar_t *)tis_str, tis_str_sz, cells, &cells_sz, 0);
base_tis_str_sz = cells_sz;
base_tis_str = SDL_malloc(base_tis_str_sz + 1);
for (temp = 0; temp < cells_sz; temp++) {
base_tis_str[temp] = cells[temp].base;
if (cells[temp].hilo) {
SDL_ToolkitThaiOverlayX11 *overlay;
char *pre;
int temp2;
overlay = SDL_malloc(sizeof(SDL_ToolkitThaiOverlayX11));
pre = SDL_iconv_string("UTF-8", "TIS-620", base_tis_str, temp);
overlay->str = SDL_iconv_string("UTF-8", "TIS-620", (const char *)&cells[temp].hilo, 1);
overlay->sz = SDL_strlen(overlay->str);
overlay->top = false;
X11Toolkit_GetTextWidthHeight(data, pre, SDL_strlen(pre), &overlay->rect.x, &temp2, &temp2, &temp2, NULL);
X11Toolkit_GetTextWidthHeight(data, overlay->str, overlay->sz, &overlay->rect.w, &overlay->rect.h, &overlay->rect.y, &temp2, NULL);
SDL_ListAdd(&element->thai_overlays, overlay);
SDL_free(pre);
}
if (cells[temp].top) {
SDL_ToolkitThaiOverlayX11 *overlay;
char *pre;
int temp2;
overlay = SDL_malloc(sizeof(SDL_ToolkitThaiOverlayX11));
pre = SDL_iconv_string("UTF-8", "TIS-620", base_tis_str, temp);
overlay->str = SDL_iconv_string("UTF-8", "TIS-620", (const char *)&cells[temp].top, 1);
overlay->sz = SDL_strlen(overlay->str);
overlay->top = true;
X11Toolkit_GetTextWidthHeight(data, pre, SDL_strlen(pre), &overlay->rect.x, &temp2, &temp2, &temp2, NULL);
X11Toolkit_GetTextWidthHeight(data, overlay->str, overlay->sz, &overlay->rect.w, &overlay->rect.h, &overlay->rect.y, &temp2, NULL);
SDL_ListAdd(&element->thai_overlays, overlay);
SDL_free(pre);
}
}
base_tis_str[base_tis_str_sz] = '\0';
element->str_free(element->str);
element->str = SDL_iconv_string("UTF-8", "TIS-620", base_tis_str, base_tis_str_sz);
element->sz = SDL_strlen(element->str);
X11Toolkit_GetTextWidthHeight(data, element->str, element->sz, &element->rect.w, &element->rect.h, &element->rect.y, &temp, &element->font_h);
SDL_free(tis_str);
SDL_free(cells);
SDL_free(base_tis_str);
}
#else
X11Toolkit_GetTextWidthHeight(data, element->str, element->sz, &element->rect.w, &element->rect.h, &element->rect.y, &temp, &element->font_h);
#endif
}
} else {
X11Toolkit_GetTextWidthHeight(data, element->str, element->sz, &element->rect.w, &element->rect.h, &element->rect.y, &temp, &element->font_h);
}
cursor = cursor->next;
}
prev = NULL;
cursor = list;
while (cursor) {
SDL_ToolkitTextElementX11 *element;
element = cursor->entry;
if (prev) {
element->rect.x = prev->rect.x + prev->rect.w;
} else {
element->rect.x = 0;
}
prev = element;
cursor = cursor->next;
}
}
void X11Toolkit_DrawTextElements(SDL_ToolkitWindowX11 *data, SDL_ListNode *list, int x, int y)
{
SDL_ListNode *cursor;
cursor = list;
while (cursor) {
SDL_ToolkitTextElementX11 *element;
element = cursor->entry;
if (element->type == SDL_TOOLKIT_TEXT_TYPE_X11_THAI) {
if (data->thai_font == SDL_TOOLKIT_THAI_FONT_X11_OFFSET) {
#ifdef X_HAVE_UTF8_STRING
if (data->utf8) {
X11_Xutf8DrawString(data->display, data->drawable, data->font_set, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
} else
#endif
{
X11_XDrawString(data->display, data->drawable, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
}
} else {
#ifdef HAVE_LIBTHAI_H
SDL_ListNode *overlay_cursor;
#ifdef X_HAVE_UTF8_STRING
if (data->utf8) {
X11_Xutf8DrawString(data->display, data->drawable, data->font_set, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
} else
#endif
{
X11_XDrawString(data->display, data->drawable, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
}
overlay_cursor = element->thai_overlays;
while (overlay_cursor) {
SDL_ToolkitThaiOverlayX11 *overlay;
overlay = overlay_cursor->entry;
#ifdef X_HAVE_UTF8_STRING
if (data->utf8) {
X11_Xutf8DrawString(data->display, data->drawable, data->font_set, data->ctx, x + element->rect.x + overlay->rect.x, y + overlay->rect.y, overlay->str, overlay->sz);
} else
#endif
{
X11_XDrawString(data->display, data->drawable, data->ctx, x + element->rect.x + overlay->rect.x, y + element->rect.y + overlay->rect.y, overlay->str, overlay->sz);
}
overlay_cursor = overlay_cursor->next;
}
#endif
}
} else {
#ifdef X_HAVE_UTF8_STRING
if (data->utf8) {
X11_Xutf8DrawString(data->display, data->drawable, data->font_set, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
} else
#endif
{
X11_XDrawString(data->display, data->drawable, data->ctx, x + element->rect.x, y + element->rect.y, element->str, element->sz);
}
}
cursor = cursor->next;
}
}
int X11Toolkit_GetTextElementsRect(SDL_ListNode *list, SDL_Rect *out)
{
SDL_ListNode *cursor;
int ret;
ret = 0;
out->x = out->y = 0;
out->w = out->h = 0;
cursor = list;
while (cursor) {
SDL_ToolkitTextElementX11 *element;
element = cursor->entry;
out->w += element->rect.w;
out->h = SDL_max(out->h, element->rect.h);
ret = SDL_max(ret, element->font_h);
cursor = cursor->next;
}
return ret;
}
void X11Toolkit_FreeTextElementsListContents(SDL_ListNode *list)
{
SDL_ListNode *cursor;
cursor = list;
while (cursor) {
SDL_ToolkitTextElementX11 *element;
#ifdef HAVE_LIBTHAI_H
SDL_ListNode *overlay_cursor;
#endif
element = cursor->entry;
if (element->str_free) {
element->str_free(element->str);
}
#ifdef HAVE_LIBTHAI_H
overlay_cursor = element->thai_overlays;
while (overlay_cursor) {
SDL_ToolkitThaiOverlayX11 *overlay;
overlay = overlay_cursor->entry;
SDL_free(overlay->str);
SDL_free(overlay);
overlay_cursor = overlay_cursor->next;
}
SDL_ListClear(&element->thai_overlays);
#endif
SDL_free(element);
cursor = cursor->next;
}
}
#define X11Toolkit_FreeTextElements(x) X11Toolkit_FreeTextElementsListContents(x); SDL_ListClear(&x)
static bool X11Toolkit_ShouldFlipUI(void)
{
SDL_Locale **current_locales;
static const SDL_Locale rtl_locales[] = {
{ "ar", NULL, },
{ "fa", "AF", },
{ "fa", "IR", },
{ "he", NULL, },
{ "iw", NULL, },
{ "yi", NULL, },
{ "ur", NULL, },
{ "ug", NULL, },
{ "kd", NULL, },
{ "pk", "PK", },
{ "ps", NULL, }
};
int current_locales_sz;
int i;
current_locales = SDL_GetPreferredLocales(¤t_locales_sz);
if (current_locales_sz <= 0) {
return false;
}
for (i = 0; i < SDL_arraysize(rtl_locales); ++i) {
if (SDL_startswith(current_locales[0]->language, rtl_locales[i].language)) {
if (!rtl_locales[i].country) {
return true;
} else {
return SDL_startswith(current_locales[0]->country, rtl_locales[i].country);
}
}
}
return false;
}
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;
}
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) {
SDL_free(window);
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
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 = X11_GetGlobalContentScale(window->display, window->xsettings);
if (window->scale < 1) {
window->scale = 1;
}
window->iscale = (int)SDL_ceilf(window->scale);
if (SDL_roundf(window->scale) == window->scale) {
window->scale = 0;
}
X11Toolkit_InitWindowFonts(window);
#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#endif
default:
colorhints = g_default_colors;
}
#else
colorhints = g_default_colors;
#endif
}
window->color_hints = colorhints;
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;
}
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);
window->parent = parent;
if (parent) {
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(parent);
window->screen = displaydata->screen;
} else {
window->screen = DefaultScreen(window->display);
}
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);
}
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);
window->has_focus = false;
window->controls = NULL;
window->controls_sz = 0;
window->dyn_controls_sz = 0;
window->fiddled_control = NULL;
window->dyn_controls = NULL;
window->popup_windows = NULL;
#ifdef HAVE_FRIBIDI_H
window->fribidi = SDL_FriBidi_Create();
#endif
#ifdef HAVE_LIBTHAI_H
window->th = SDL_LibThai_Create();
#endif
window->flip_interface = X11Toolkit_ShouldFlipUI();
return window;
}
static void X11Toolkit_AddControlToWindow(SDL_ToolkitWindowX11 *window, SDL_ToolkitControlX11 *control) {
window->controls_sz++;
if (window->controls_sz == 1) {
window->controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *));
} else {
window->controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->controls, sizeof(struct SDL_ToolkitControlX11 *) * window->controls_sz);
}
window->controls[window->controls_sz - 1] = control;
if (control->dynamic) {
window->dyn_controls_sz++;
if (window->dyn_controls_sz == 1) {
window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_malloc(sizeof(struct SDL_ToolkitControlX11 *));
} else {
window->dyn_controls = (struct SDL_ToolkitControlX11 **)SDL_realloc(window->dyn_controls, sizeof(struct SDL_ToolkitControlX11 *) * window->dyn_controls_sz);
}
window->dyn_controls[window->dyn_controls_sz - 1] = control;
}
if (control->selected) {
window->focused_control = control;
}
}
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;
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);
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);
}
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;
}
if (out_info->crtc != None) {
crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc);
} else if (out_info->ncrtc > 0) {
crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtcs[0]);
} else {
crtc_info = NULL;
}
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;
}
if (out_info->crtc != None) {
crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtc);
} else if (out_info->ncrtc > 0) {
crtc_info = X11_XRRGetCrtcInfo(display, screen_res, out_info->crtcs[0]);
} else {
crtc_info = NULL;
}
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 {
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
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;
}
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 ((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 (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;
X11_XPeekEvent(data->display, &e);
if (data->xsettings) {
xsettings_client_process_event(data->xsettings, &e);
}
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, NULL);
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;
control = (SDL_ToolkitIconControlX11 *)SDL_malloc(sizeof(SDL_ToolkitIconControlX11));
base_control = (SDL_ToolkitControlX11 *)control;
if (!control) {
SDL_SetError("Unable to allocate icon control structure");
return NULL;
}
base_control->window = window;
base_control->func_draw = X11Toolkit_DrawIconControl;
base_control->func_free = X11Toolkit_DestroyIconControl;
base_control->func_on_state_change = NULL;
base_control->func_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;
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;
}
}
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);
X11Toolkit_CalculateIconControl(base_control);
X11Toolkit_AddControlToWindow(window, base_control);
return base_control;
}
bool X11Toolkit_NotifyControlOfSizeChange(SDL_ToolkitControlX11 *control) {
if (control->func_calc_size) {
control->func_calc_size(control);
return true;
} else {
return false;
}
}
static void X11Toolkit_CalculateButtonControl(SDL_ToolkitControlX11 *control) {
SDL_ToolkitButtonControlX11 *button_control;
button_control = (SDL_ToolkitButtonControlX11 *)control;
X11Toolkit_GetTextElementsRect(button_control->text, &button_control->text_rect);
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 = (control->rect.h - button_control->text_rect.h)/2;
}
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);
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);
X11Toolkit_DrawTextElements(control->window, button_control->text, control->rect.x + button_control->text_rect.x, control->rect.y + button_control->text_rect.y);
}
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) {
SDL_ToolkitButtonControlX11 *button_control;
button_control = (SDL_ToolkitButtonControlX11 *)control;
X11Toolkit_FreeTextElements(button_control->text);
SDL_free(control);
}
SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *window, const SDL_MessageBoxButtonData *data) {
SDL_ToolkitButtonControlX11 *control;
SDL_ToolkitControlX11 *base_control;
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;
}
control->cb = NULL;
control->data = data;
#ifdef HAVE_FRIBIDI_H
control->text = X11Toolkit_MakeTextElements(base_control->window, (char *)control->data->text, SDL_strlen(control->data->text), NULL);
#else
control->text = X11Toolkit_MakeTextElements(base_control->window, (char *)control->data->text, SDL_strlen(control->data->text));
#endif
X11Toolkit_ShapeTextElements(base_control->window, control->text);
base_control->do_size = true;
X11Toolkit_CalculateButtonControl(base_control);
base_control->do_size = false;
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]);
}
}
SDL_free(data->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
#ifdef HAVE_LIBTHAI_H
SDL_LibThai_Destroy(data->th);
#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++; 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++) {
X11Toolkit_DrawTextElements(control->window, label_control->lines[i].text, control->rect.x + label_control->lines[i].rect.x, control->rect.y + label_control->lines[i].rect.y);
}
}
static void X11Toolkit_DestroyLabelControl(SDL_ToolkitControlX11 *control) {
SDL_ToolkitLabelControlX11 *label_control;
int i;
label_control = (SDL_ToolkitLabelControlX11 *)control;
for (i = 0; i < label_control->sz; i++) {
X11Toolkit_FreeTextElements(label_control->lines[i].text);
}
SDL_free(label_control->lines);
SDL_free(label_control);
}
static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control) {
SDL_ToolkitLabelControlX11 *control;
int i;
control = (SDL_ToolkitLabelControlX11 *)base_control;
if (base_control->do_size) {
base_control->rect.w = 0;
base_control->rect.h = 0;
}
for (i = 0; i < control->sz; i++) {
int font_h;
font_h = X11Toolkit_GetTextElementsRect(control->lines[i].text, &control->lines[i].rect);
if (base_control->do_size) {
base_control->rect.w = SDL_max(base_control->rect.w, control->lines[i].rect.w);
}
if (i > 0) {
control->lines[i].rect.y = font_h + control->lines[i - 1].rect.y;
} else {
control->lines[i].rect.y = 0;
}
}
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
FriBidiParType first_ndn_dir;
int last_ndn;
first_ndn_dir = FRIBIDI_PAR_LTR;
for (i = 0; i < control->sz; i++) {
if (control->lines[i].par != FRIBIDI_PAR_ON) {
first_ndn_dir = control->lines[i].par;
}
}
last_ndn = -1;
for (i = 0; i < control->sz; i++) {
switch (control->lines[i].par) {
case FRIBIDI_PAR_LTR:
control->lines[i].rect.x = 0;
last_ndn = i;
break;
case FRIBIDI_PAR_RTL:
control->lines[i].rect.x = base_control->rect.w - control->lines[i].rect.w;
last_ndn = i;
break;
default:
if (last_ndn != -1) {
if (control->lines[last_ndn].par == FRIBIDI_PAR_RTL) {
control->lines[i].rect.x = base_control->rect.w - control->lines[i].rect.w;
} else {
control->lines[i].rect.x = 0;
}
} else {
if (first_ndn_dir == FRIBIDI_PAR_RTL) {
control->lines[i].rect.x = base_control->rect.w - control->lines[i].rect.w;
} else {
control->lines[i].rect.x = 0;
}
}
}
}
}
#endif
if (base_control->do_size && control->sz) {
base_control->rect.h = control->lines[control->sz - 1].rect.y + control->lines[control->sz - 1].rect.h;
}
}
SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8) {
SDL_ToolkitLabelControlX11 *control;
SDL_ToolkitControlX11 *base_control;
int i;
if (!utf8) {
return NULL;
}
if (!SDL_strcmp(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 = SDL_calloc(control->sz, sizeof(SDL_ToolkitLabelControlLineX11));
for (i = 0; i < control->sz; i++) {
const char *lf = SDL_strchr(utf8, '\n');
const int length = lf ? (lf - utf8) : SDL_strlen(utf8);
int sz;
sz = length;
if (lf && (lf > utf8) && (lf[-1] == '\r')) {
sz--;
}
#ifdef HAVE_FRIBIDI_H
control->lines[i].text = X11Toolkit_MakeTextElements(base_control->window, (char *)utf8, sz, &control->lines[i].par);
#else
control->lines[i].text = X11Toolkit_MakeTextElements(base_control->window, (char *)utf8, sz);
#endif
X11Toolkit_ShapeTextElements(base_control->window, control->lines[i].text);
utf8 += length + 1;
if (!lf) {
break;
}
}
base_control->do_size = true;
X11Toolkit_CalculateLabelControl(base_control);
base_control->do_size = false;
X11Toolkit_AddControlToWindow(window, base_control);
return base_control;
}
int X11Toolkit_GetLabelControlFirstLineHeight(SDL_ToolkitControlX11 *control) {
SDL_ToolkitLabelControlX11 *label_control;
label_control = (SDL_ToolkitLabelControlX11 *)control;
return label_control->lines[0].rect.h;
}
void X11Toolkit_SignalWindowClose(SDL_ToolkitWindowX11 *data) {
data->close = true;
}
#endif