X11TK: Add Arabic/Hebrew/Farsi support by increasing font size slightly and using FriBidi. (#14134)

This commit is contained in:
eafton
2025-10-04 05:08:24 +03:00
committed by GitHub
parent 49d51a0d3c
commit c2429e85ec
9 changed files with 503 additions and 30 deletions

View File

@@ -347,6 +347,8 @@ dep_option(SDL_X11_XSCRNSAVER "Enable Xscrnsaver support" ON SDL_X11 OFF)
dep_option(SDL_X11_XSHAPE "Enable XShape support" ON SDL_X11 OFF)
dep_option(SDL_X11_XSYNC "Enable Xsync support" ON SDL_X11 OFF)
dep_option(SDL_X11_XTEST "Enable XTest support" ON SDL_X11 OFF)
dep_option(SDL_FRIBIDI "Enable Fribidi support" ON SDL_X11 OFF)
dep_option(SDL_FRIBIDI_SHARED "Dynamically load Fribidi support" ON "SDL_FRIBIDI;SDL_DEPS_SHARED" OFF)
dep_option(SDL_WAYLAND "Use Wayland video driver" ${UNIX_SYS} "SDL_VIDEO" OFF)
dep_option(SDL_WAYLAND_SHARED "Dynamically load Wayland support" ON "SDL_WAYLAND;SDL_DEPS_SHARED" OFF)
dep_option(SDL_WAYLAND_LIBDECOR "Use client-side window decorations on Wayland" ON "SDL_WAYLAND" OFF)
@@ -1776,6 +1778,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
# Need to check for ROCKCHIP platform and get rid of "Can't window GBM/EGL surfaces on window creation."
CheckROCKCHIP()
CheckX11()
CheckFribidi()
# Need to check for EGL first because KMSDRM and Wayland depend on it.
CheckEGL()
CheckKMSDRM()
@@ -1925,7 +1928,6 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
set(HAVE_LIBURING TRUE)
endif()
endif()
if((FREEBSD OR NETBSD) AND NOT HAVE_INOTIFY)
set(LibInotify_PKG_CONFIG_SPEC libinotify)
pkg_check_modules(PC_LIBINOTIFY IMPORTED_TARGET ${LibInotify_PKG_CONFIG_SPEC})

View File

@@ -511,6 +511,35 @@ macro(CheckX11)
cmake_pop_check_state()
endmacro()
macro(CheckFribidi)
if(SDL_FRIBIDI)
set(FRIBIDI_PKG_CONFIG_SPEC fribidi)
set(PC_FRIBIDI_FOUND FALSE)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_FRIBIDI IMPORTED_TARGET ${FRIBIDI_PKG_CONFIG_SPEC})
endif()
if(PC_FRIBIDI_FOUND)
set(HAVE_FRIBIDI TRUE)
set(HAVE_FRIBIDI_H 1)
sdl_sources(
"${SDL3_SOURCE_DIR}/src/core/unix/SDL_fribidi.c"
"${SDL3_SOURCE_DIR}/src/core/unix/SDL_fribidi.h"
)
if(SDL_FRIBIDI_SHARED AND NOT HAVE_SDL_LOADSO)
message(WARNING "You must have SDL_LoadObject() support for dynamic fribidi loading")
endif()
FindLibraryAndSONAME("fribidi" LIBDIRS ${PC_FRIBIDI_LIBRARY_DIRS})
if(SDL_FRIBIDI_SHARED AND FRIBIDI_LIB AND HAVE_SDL_LOADSO)
set(SDL_FRIBIDI_DYNAMIC "\"${FRIBIDI_LIB_SONAME}\"")
set(HAVE_FRIBIDI_SHARED TRUE)
sdl_include_directories(PRIVATE SYSTEM $<TARGET_PROPERTY:PkgConfig::PC_FRIBIDI,INTERFACE_INCLUDE_DIRECTORIES>)
else()
sdl_link_dependency(fribidi LIBS PkgConfig::PC_FRIBIDI PKG_CONFIG_PREFIX PC_FRIBIDI PKG_CONFIG_SPECS ${FRIBIDI_PKG_CONFIG_SPEC})
endif()
endif()
endif()
endmacro()
macro(WaylandProtocolGen _SCANNER _CODE_MODE _XML _PROTL)
set(_WAYLAND_PROT_C_CODE "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols/${_PROTL}-protocol.c")
set(_WAYLAND_PROT_H_CODE "${CMAKE_CURRENT_BINARY_DIR}/wayland-generated-protocols/${_PROTL}-client-protocol.h")

View File

@@ -216,6 +216,8 @@
#cmakedefine HAVE_LIBUDEV_H 1
#cmakedefine HAVE_LIBDECOR_H 1
#cmakedefine HAVE_LIBURING_H 1
#cmakedefine HAVE_FRIBIDI_H 1
#cmakedefine SDL_FRIBIDI_DYNAMIC @SDL_FRIBIDI_DYNAMIC@
#cmakedefine HAVE_DDRAW_H 1
#cmakedefine HAVE_DSOUND_H 1

160
src/core/unix/SDL_fribidi.c Normal file
View File

@@ -0,0 +1,160 @@
/*
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.
*/
#ifdef HAVE_FRIBIDI_H
#include "SDL_internal.h"
#include "SDL_fribidi.h"
#include <fribidi.h>
SDL_FriBidi *SDL_FriBidi_Create(void) {
SDL_FriBidi *fribidi;
fribidi = (SDL_FriBidi *)SDL_malloc(sizeof(SDL_FriBidi));
if (!fribidi) {
return NULL;
}
#ifdef SDL_FRIBIDI_DYNAMIC
#define SDL_FRIBIDI_LOAD_SYM(x, n, t) x = ((t)SDL_LoadFunction(fribidi->lib, n)); if (!x) { SDL_UnloadObject(fribidi->lib); SDL_free(fribidi); return NULL; }
fribidi->lib = SDL_LoadObject(SDL_FRIBIDI_DYNAMIC);
if (!fribidi->lib) {
SDL_free(fribidi);
return NULL;
}
SDL_FRIBIDI_LOAD_SYM(fribidi->unicode_to_charset, "fribidi_unicode_to_charset", SDL_FriBidiUnicodeToCharset);
SDL_FRIBIDI_LOAD_SYM(fribidi->charset_to_unicode, "fribidi_charset_to_unicode", SDL_FriBidiCharsetToUnicode);
SDL_FRIBIDI_LOAD_SYM(fribidi->get_bidi_types, "fribidi_get_bidi_types", SDL_FriBidiGetBidiTypes);
SDL_FRIBIDI_LOAD_SYM(fribidi->get_par_direction, "fribidi_get_par_direction", SDL_FriBidiGetParDirection);
SDL_FRIBIDI_LOAD_SYM(fribidi->get_par_embedding_levels, "fribidi_get_par_embedding_levels", SDL_FriBidiGetParEmbeddingLevels);
SDL_FRIBIDI_LOAD_SYM(fribidi->get_joining_types, "fribidi_get_joining_types", SDL_FriBidiGetJoiningTypes);
SDL_FRIBIDI_LOAD_SYM(fribidi->join_arabic, "fribidi_join_arabic", SDL_FriBidiJoinArabic);
SDL_FRIBIDI_LOAD_SYM(fribidi->shape, "fribidi_shape", SDL_FriBidiShape);
SDL_FRIBIDI_LOAD_SYM(fribidi->reorder_line, "fribidi_reorder_line", SDL_FriBidiReorderLine);
#else
fribidi->unicode_to_charset = fribidi_unicode_to_charset;
fribidi->charset_to_unicode = fribidi_charset_to_unicode;
fribidi->get_bidi_types = fribidi_get_bidi_types;
fribidi->get_par_direction = fribidi_get_par_direction;
fribidi->get_par_embedding_levels = fribidi_get_par_embedding_levels;
fribidi->get_joining_types = fribidi_get_joining_types;
fribidi->join_arabic = fribidi_join_arabic;
fribidi->shape = fribidi_shape;
fribidi->reorder_line = fribidi_reorder_line;
#endif
return fribidi;
}
char *SDL_FriBidi_Process(SDL_FriBidi *fribidi, char *utf8, ssize_t utf8_len, bool shaping, FriBidiParType *out_par_type) {
FriBidiCharType *types;
FriBidiLevel *levels;
FriBidiArabicProp *props;
FriBidiChar *str;
char *result;
FriBidiStrIndex len;
FriBidiLevel max_level;
FriBidiLevel start;
FriBidiLevel end;
FriBidiParType direction;
FriBidiParType str_direction;
unsigned int i;
unsigned int c;
if (!fribidi || !utf8) {
return NULL;
}
/* Convert to UTF32 */
if (utf8_len < 0) {
utf8_len = SDL_strlen(utf8);
}
str = SDL_calloc(SDL_utf8strnlen(utf8, utf8_len), sizeof(FriBidiChar));
len = fribidi->charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, utf8, utf8_len, str);
/* Setup various BIDI structures */
direction = FRIBIDI_PAR_LTR;
types = NULL;
levels = NULL;
props = SDL_calloc(len + 1, sizeof(FriBidiArabicProp));
levels = SDL_calloc(len + 1, sizeof(FriBidiLevel));
types = SDL_calloc(len + 1, sizeof(FriBidiCharType));
/* Shape */
fribidi->get_bidi_types(str, len, types);
str_direction = fribidi->get_par_direction(types, len);
max_level = fribidi->get_par_embedding_levels(types, len, &direction, levels);
if (shaping) {
fribidi->get_joining_types(str, len, props);
fribidi->join_arabic(types, len, levels, props);
fribidi->shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, levels, len, props, str);
}
/* BIDI */
for (end = 0, start = 0; end < len; end++) {
if (str[end] == '\n' || str[end] == '\r' || str[end] == '\f' || str[end] == '\v' || end == len - 1) {
max_level = fribidi->reorder_line(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, types, end - start + 1, start, direction, levels, str, NULL);
start = end + 1;
}
}
/* Silence warning */
(void)max_level;
/* Remove fillers */
for (i = 0, c = 0; i < len; i++) {
if (str[i] != FRIBIDI_CHAR_FILL) {
str[c++] = str[i];
}
}
len = c;
/* Convert back to UTF8 */
result = SDL_malloc(len * 4 + 1);
fribidi->unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, str, len, result);
/* Cleanup */
SDL_free(levels);
SDL_free(props);
SDL_free(types);
/* Return */
if (out_par_type) {
*out_par_type = str_direction;
}
return result;
}
void SDL_FriBidi_Destroy(SDL_FriBidi *fribidi) {
if (!fribidi) {
return;
}
#ifdef SDL_FRIBIDI_DYNAMIC
SDL_UnloadObject(fribidi->lib);
#endif
SDL_free(fribidi);
}
#endif

View File

@@ -0,0 +1,58 @@
/*
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 HAVE_FRIBIDI_H
#include <fribidi.h>
#ifndef SDL_fribidi_h_
#define SDL_fribidi_h_
typedef FriBidiStrIndex (*SDL_FriBidiUnicodeToCharset)(FriBidiCharSet, const FriBidiChar *, FriBidiStrIndex, char *);
typedef FriBidiStrIndex (*SDL_FriBidiCharsetToUnicode)(FriBidiCharSet, const char *, FriBidiStrIndex, FriBidiChar *);
typedef void (*SDL_FriBidiGetBidiTypes)(const FriBidiChar *, const FriBidiStrIndex, FriBidiCharType *);
typedef FriBidiParType (*SDL_FriBidiGetParDirection)(const FriBidiCharType *, const FriBidiStrIndex);
typedef FriBidiLevel (*SDL_FriBidiGetParEmbeddingLevels)(const FriBidiCharType *, const FriBidiStrIndex, FriBidiParType *, FriBidiLevel *);
typedef void (*SDL_FriBidiGetJoiningTypes)(const FriBidiChar *, const FriBidiStrIndex, FriBidiJoiningType *);
typedef void (*SDL_FriBidiJoinArabic)(const FriBidiCharType *, const FriBidiStrIndex, const FriBidiLevel *, FriBidiArabicProp *);
typedef void (*SDL_FriBidiShape)(FriBidiFlags flags, const FriBidiLevel *, const FriBidiStrIndex, FriBidiArabicProp *, FriBidiChar *str);
typedef FriBidiLevel (*SDL_FriBidiReorderLine)(FriBidiFlags flags, const FriBidiCharType *, const FriBidiStrIndex, const FriBidiStrIndex, const FriBidiParType, FriBidiLevel *, FriBidiChar *, FriBidiStrIndex *);
typedef struct SDL_FriBidi {
SDL_SharedObject *lib;
SDL_FriBidiUnicodeToCharset unicode_to_charset;
SDL_FriBidiCharsetToUnicode charset_to_unicode;
SDL_FriBidiGetBidiTypes get_bidi_types;
SDL_FriBidiGetParDirection get_par_direction;
SDL_FriBidiGetParEmbeddingLevels get_par_embedding_levels;
SDL_FriBidiGetJoiningTypes get_joining_types;
SDL_FriBidiJoinArabic join_arabic;
SDL_FriBidiShape shape;
SDL_FriBidiReorderLine reorder_line;
} SDL_FriBidi;
extern SDL_FriBidi *SDL_FriBidi_Create(void);
extern char *SDL_FriBidi_Process(SDL_FriBidi *fribidi, char *utf8, ssize_t utf8_len, bool shaping, FriBidiParType *out_par_type);
extern void SDL_FriBidi_Destroy(SDL_FriBidi *fribidi);
#endif
#endif // SDL_fribidi_h_

View File

@@ -235,8 +235,10 @@ SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle
SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a))
SDL_X11_SYM(char*,Xutf8ResetIC,(XIC a))
SDL_X11_SYM(XFontSetExtents*,XExtentsOfFontSet,(XFontSet a))
SDL_X11_SYM(Bool,XContextDependentDrawing,(XFontSet a))
#endif
#ifndef NO_SHARED_MEMORY
SDL_X11_MODULE(SHM)
SDL_X11_SYM(Status,XShmAttach,(Display* a,XShmSegmentInfo* b))

View File

@@ -28,6 +28,9 @@
#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"
@@ -76,6 +79,10 @@ typedef struct SDL_ToolkitButtonControlX11
int text_a;
int text_d;
int str_sz;
#ifdef HAVE_FRIBIDI_H
char *text;
bool free_text;
#endif
/* Callback */
void *cb_data;
@@ -90,6 +97,12 @@ typedef struct SDL_ToolkitLabelControlX11
int *y;
size_t *szs;
size_t sz;
#ifdef HAVE_FRIBIDI_H
int *x;
int *w;
bool *free_lines;
FriBidiParType *par_types;
#endif
} SDL_ToolkitLabelControlX11;
typedef struct SDL_ToolkitMenuBarControlX11
@@ -109,7 +122,7 @@ typedef struct 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
#define G_ICONFONT_SIZE 22
/* General UI font */
static const char g_ToolkitFontLatin1[] =
@@ -128,7 +141,7 @@ static const char *g_ToolkitFont[] = {
"-*-*-*-*-*--*-*-*-*-*-*-iso8859-1,*", // just give me anything latin1.
NULL
};
#define G_TOOLKITFONT_SIZE 120
#define G_TOOLKITFONT_SIZE 140
static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = {
{ 191, 184, 191 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
@@ -399,6 +412,10 @@ static void X11Toolkit_InitWindowFonts(SDL_ToolkitWindowX11 *window)
if (!window->font_set) {
goto load_font_traditional;
} else {
#ifdef HAVE_FRIBIDI_H
window->do_shaping = !X11_XContextDependentDrawing(window->font_set);
#endif
}
} else
#endif
@@ -549,27 +566,40 @@ static void X11Toolkit_GetTextWidthHeightForFont(XFontStruct *font, const char *
*ascent = text_structure.ascent;
}
static void X11Toolkit_GetTextWidthHeight(SDL_ToolkitWindowX11 *data, const char *str, int nbytes, int *pwidth, int *pheight, int *ascent, int *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;
*font_descent = overall_logical.height - *ascent;
*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;
int font_direction, font_ascent, font_descent;
X11_XTextExtents(data->font_struct, str, nbytes,
&font_direction, &font_ascent, font_descent,
&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;
}
}
}
@@ -779,6 +809,9 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool
/* Menu windows */
window->popup_windows = NULL;
#ifdef HAVE_FRIBIDI_H
window->fribidi = SDL_FriBidi_Create();
#endif
return window;
}
@@ -1653,7 +1686,7 @@ static void X11Toolkit_CalculateButtonControl(SDL_ToolkitControlX11 *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);
X11Toolkit_GetTextWidthHeight(control->window, button_control->data->text, button_control->str_sz, &button_control->text_rect.w, &button_control->text_rect.h, &button_control->text_a, &text_d, NULL);
if (control->do_size) {
control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.w;
control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * control->window->iscale + button_control->text_rect.h;
@@ -1665,8 +1698,16 @@ static void X11Toolkit_CalculateButtonControl(SDL_ToolkitControlX11 *control) {
static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) {
SDL_ToolkitButtonControlX11 *button_control;
char *text;
button_control = (SDL_ToolkitButtonControlX11 *)control;
#ifdef HAVE_FRIBIDI_H
text = button_control->text;
#else
text = (char *)button_control->data->text;
#endif
X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel);
/* Draw bevel */
if (control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED || control->state == SDL_TOOLKIT_CONTROL_STATE_X11_PRESSED_HELD) {
@@ -1754,13 +1795,13 @@ static void X11Toolkit_DrawButtonControl(SDL_ToolkitControlX11 *control) {
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);
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);
text, button_control->str_sz);
}
}
@@ -1773,7 +1814,15 @@ static void X11Toolkit_OnButtonControlStateChange(SDL_ToolkitControlX11 *control
}
}
static void X11Toolkit_DestroyGenericControl(SDL_ToolkitControlX11 *control) {
static void X11Toolkit_DestroyButtonControl(SDL_ToolkitControlX11 *control) {
#ifdef HAVE_FRIBIDI_H
SDL_ToolkitButtonControlX11 *button_control;
button_control = (SDL_ToolkitButtonControlX11 *)control;
if (button_control->free_text) {
SDL_free(button_control->text);
}
#endif
SDL_free(control);
}
@@ -1793,7 +1842,7 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *wind
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_free = X11Toolkit_DestroyButtonControl;
base_control->func_on_scale_change = NULL;
base_control->state = SDL_TOOLKIT_CONTROL_STATE_X11_NORMAL;
base_control->selected = false;
@@ -1810,8 +1859,23 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateButtonControl(SDL_ToolkitWindowX11 *wind
base_control->do_size = false;
control->data = data;
control->str_sz = SDL_strlen(control->data->text);
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
control->text = SDL_FriBidi_Process(base_control->window->fribidi, (char *)control->data->text, control->str_sz, base_control->window->do_shaping, NULL);
if (control->text) {
control->free_text = true;
control->str_sz = SDL_strlen(control->text);
} else {
control->text = (char *)control->data->text;
control->free_text = false;
}
} else {
control->text = (char *)control->data->text;
control->free_text = false;
}
#endif
control->cb = NULL;
X11Toolkit_GetTextWidthHeight(base_control->window, control->data->text, control->str_sz, &control->text_rect.w, &control->text_rect.h, &control->text_a, &text_d);
X11Toolkit_GetTextWidthHeight(base_control->window, control->data->text, control->str_sz, &control->text_rect.w, &control->text_rect.h, &control->text_a, &text_d, NULL);
base_control->rect.w = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * window->iscale + control->text_rect.w;
base_control->rect.h = SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * 2 * window->iscale + control->text_rect.h;
control->text_rect.x = control->text_rect.y = 0;
@@ -1923,6 +1987,10 @@ void X11Toolkit_DestroyWindow(SDL_ToolkitWindowX11 *data) {
}
#endif
#ifdef HAVE_FRIBIDI_H
SDL_FriBidi_Destroy(data->fribidi);
#endif
SDL_free(data);
}
@@ -1940,20 +2008,27 @@ static int X11Toolkit_CountLinesOfText(const char *text)
static void X11Toolkit_DrawLabelControl(SDL_ToolkitControlX11 *control) {
SDL_ToolkitLabelControlX11 *label_control;
int i;
int x;
label_control = (SDL_ToolkitLabelControlX11 *)control;
X11_XSetForeground(control->window->display, control->window->ctx, control->window->xcolor[SDL_MESSAGEBOX_COLOR_TEXT].pixel);
for (i = 0; i < label_control->sz; i++) {
x = control->rect.x;
#ifdef HAVE_FRIBIDI_H
if (control->window->fribidi) {
x += label_control->x[i];
}
#endif
#ifdef X_HAVE_UTF8_STRING
if (control->window->utf8) {
X11_Xutf8DrawString(control->window->display, control->window->drawable, control->window->font_set, control->window->ctx,
control->rect.x, control->rect.y + label_control->y[i],
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],
x, control->rect.y + label_control->y[i],
label_control->lines[i], label_control->szs[i]);
}
}
@@ -1963,6 +2038,21 @@ static void X11Toolkit_DestroyLabelControl(SDL_ToolkitControlX11 *control) {
SDL_ToolkitLabelControlX11 *label_control;
label_control = (SDL_ToolkitLabelControlX11 *)control;
#ifdef HAVE_FRIBIDI_H
if (control->window->fribidi) {
int i;
for (i = 0; i < label_control->sz; i++) {
if (label_control->free_lines[i]) {
SDL_free(label_control->lines[i]);
}
}
SDL_free(label_control->x);
SDL_free(label_control->free_lines);
SDL_free(label_control->w);
SDL_free(label_control->par_types);
}
#endif
SDL_free(label_control->lines);
SDL_free(label_control->szs);
SDL_free(label_control->y);
@@ -1974,17 +2064,23 @@ static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control
int last_h;
int ascent;
int descent;
int font_h;
int w;
int h;
int i;
#ifdef HAVE_FRIBIDI_H
FriBidiParType first_ndn_dir;
int last_ndn;
#endif
last_h = 0;
control = (SDL_ToolkitLabelControlX11 *)base_control;
for (i = 0; i < control->sz; i++) {
X11Toolkit_GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent);
X11Toolkit_GetTextWidthHeight(base_control->window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent, &font_h);
base_control->rect.w = SDL_max(base_control->rect.w, w);
if (i > 0) {
control->y[i] = ascent + descent + control->y[i-1];
control->y[i] = font_h + control->y[i-1];
} else {
control->y[i] = ascent;
}
@@ -1992,11 +2088,55 @@ static void X11Toolkit_CalculateLabelControl(SDL_ToolkitControlX11 *base_control
last_h = h;
}
base_control->rect.h = control->y[control->sz -1] + last_h;
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
first_ndn_dir = FRIBIDI_PAR_LTR;
for (i = 0; i < control->sz; i++) {
if (control->par_types[i] != FRIBIDI_PAR_ON) {
first_ndn_dir = control->par_types[i];
}
}
last_ndn = -1;
for (i = 0; i < control->sz; i++) {
switch (control->par_types[i]) {
case FRIBIDI_PAR_LTR:
control->x[i] = 0;
last_ndn = i;
break;
case FRIBIDI_PAR_RTL:
control->x[i] = base_control->rect.w - control->w[i];
last_ndn = i;
break;
default:
if (last_ndn != -1) {
if (control->par_types[last_ndn] == FRIBIDI_PAR_RTL) {
control->x[i] = base_control->rect.w - control->w[i];
} else {
control->x[i] = 0;
}
} else {
if (first_ndn_dir == FRIBIDI_PAR_RTL) {
control->x[i] = base_control->rect.w - control->w[i];
} else {
control->x[i] = 0;
}
}
}
}
}
#endif
}
SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *window, char *utf8) {
SDL_ToolkitLabelControlX11 *control;
SDL_ToolkitControlX11 *base_control;
#ifdef HAVE_FRIBIDI_H
FriBidiParType first_ndn_dir;
int last_ndn;
#endif
int font_h;
int last_h;
int ascent;
int descent;
@@ -2028,6 +2168,14 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo
control->lines = (char **)SDL_malloc(sizeof(char *) * control->sz);
control->y = (int *)SDL_calloc(control->sz, sizeof(int));
control->szs = (size_t *)SDL_calloc(control->sz, sizeof(size_t));
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
control->x = (int *)SDL_calloc(control->sz, sizeof(int));
control->free_lines = (bool *)SDL_calloc(control->sz, sizeof(bool));
control->par_types = (FriBidiParType *)SDL_calloc(control->sz, sizeof(FriBidiParType));
control->w = (int *)SDL_calloc(control->sz, sizeof(int));
}
#endif
last_h = 0;
for (i = 0; i < control->sz; i++) {
const char *lf = SDL_strchr(utf8, '\n');
@@ -2035,17 +2183,34 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo
int w;
int h;
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
control->lines[i] = SDL_FriBidi_Process(base_control->window->fribidi, utf8, length, base_control->window->do_shaping, &control->par_types[i]);
control->szs[i] = SDL_strlen(control->lines[i]);
control->free_lines[i] = true;
} else
#endif
{
control->lines[i] = utf8;
X11Toolkit_GetTextWidthHeight(window, utf8, length, &w, &h, &ascent, &descent);
control->szs[i] = length;
#ifdef HAVE_FRIBIDI_H
control->free_lines[i] = false;
#endif
}
X11Toolkit_GetTextWidthHeight(window, control->lines[i], control->szs[i], &w, &h, &ascent, &descent, &font_h);
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
control->w[i] = w;
}
#endif
base_control->rect.w = SDL_max(base_control->rect.w, w);
control->szs[i] = length;
if (lf && (lf > utf8) && (lf[-1] == '\r')) {
if (lf && (lf > control->lines[i]) && (lf[-1] == '\r')) {
control->szs[i]--;
}
if (i > 0) {
control->y[i] = ascent + descent + control->y[i-1];
control->y[i] = font_h + control->y[i-1];
} else {
control->y[i] = ascent;
}
@@ -2057,6 +2222,44 @@ SDL_ToolkitControlX11 *X11Toolkit_CreateLabelControl(SDL_ToolkitWindowX11 *windo
}
}
base_control->rect.h = control->y[control->sz -1] + last_h;
#ifdef HAVE_FRIBIDI_H
if (base_control->window->fribidi) {
first_ndn_dir = FRIBIDI_PAR_LTR;
for (i = 0; i < control->sz; i++) {
if (control->par_types[i] != FRIBIDI_PAR_ON) {
first_ndn_dir = control->par_types[i];
}
}
last_ndn = -1;
for (i = 0; i < control->sz; i++) {
switch (control->par_types[i]) {
case FRIBIDI_PAR_LTR:
control->x[i] = 0;
last_ndn = i;
break;
case FRIBIDI_PAR_RTL:
control->x[i] = base_control->rect.w - control->w[i];
last_ndn = i;
break;
default:
if (last_ndn != -1) {
if (control->par_types[last_ndn] == FRIBIDI_PAR_RTL) {
control->x[i] = base_control->rect.w - control->w[i];
} else {
control->x[i] = 0;
}
} else {
if (first_ndn_dir == FRIBIDI_PAR_RTL) {
control->x[i] = base_control->rect.w - control->w[i];
} else {
control->x[i] = 0;
}
}
}
}
}
#endif
X11Toolkit_AddControlToWindow(window, base_control);

View File

@@ -29,6 +29,9 @@
#include "SDL_x11settings.h"
#include "SDL_x11toolkit.h"
#include "xsettings-client.h"
#ifdef HAVE_FRIBIDI_H
#include "../../core/unix/SDL_fribidi.h"
#endif
#ifdef SDL_VIDEO_DRIVER_X11
@@ -95,7 +98,6 @@ typedef struct SDL_ToolkitWindowX11
Bool shm_pixmap;
#endif
bool utf8;
/* Atoms */
Atom wm_protocols;
Atom wm_delete_message;
@@ -155,6 +157,12 @@ typedef struct SDL_ToolkitWindowX11
bool draw;
bool close;
long event_mask;
#ifdef HAVE_FRIBIDI_H
/* BIDI engine */
SDL_FriBidi *fribidi;
bool do_shaping;
#endif
} SDL_ToolkitWindowX11;
typedef enum SDL_ToolkitControlStateX11

View File

@@ -213,6 +213,15 @@ int main(int argc, char *argv[])
quit(1);
}
success = SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"Arabic (multi line)",
"سطر طويل جدًا من النص\nخط قصير\nسطر طويل للغاية من النص مذهل بشكل لا يصدق في اللغة العربية التي يتم التحدث بها في منطقة الشرق الأوسط وشمال أفريقيا",
NULL);
if (!success) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error Presenting MessageBox: %s", SDL_GetError());
quit(1);
}
success = SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
"Cyrillic (Ukranian)",
"Для запису людської мови використовуються системи письма.",