From 2c0094ead6c257e4311fc2c4dfa2b0d70c5aaabe Mon Sep 17 00:00:00 2001
From: eafton <[EMAIL REDACTED]>
Date: Thu, 9 Oct 2025 17:48:10 +0300
Subject: [PATCH] X11TK: Flip the positioning of the UI if the locale is RTL
(#14183)
---
src/video/x11/SDL_x11messagebox.c | 116 +++++++++++++++++++++++++++++-
src/video/x11/SDL_x11toolkit.c | 41 +++++++++++
src/video/x11/SDL_x11toolkit.h | 18 ++---
3 files changed, 165 insertions(+), 10 deletions(-)
diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c
index 94d386f50a7a8..b5b3871dbc600 100644
--- a/src/video/x11/SDL_x11messagebox.c
+++ b/src/video/x11/SDL_x11messagebox.c
@@ -96,7 +96,7 @@ static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp,
controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon);
} else {
controls->message->rect.x = 0;
- controls->message->rect.y = -2;
+ controls->message->rect.y = -2 * controls->window->iscale;
controls->icon = &controls->fake_icon;
controls->icon->rect.w = 0;
controls->icon->rect.h = 0;
@@ -168,6 +168,114 @@ static void X11_PositionMessageBox(SDL_MessageBoxControlsX11 *controls, int *wp,
*hp = h;
}
+static void X11_PositionMessageBoxFlipped(SDL_MessageBoxControlsX11 *controls, int *wp, int *hp)
+{
+ int max_button_w;
+ int max_button_h;
+ int total_button_w;
+ int total_text_and_icon_w;
+ int w;
+ int h;
+ int i;
+ int t;
+
+ /* Init vars */
+ max_button_w = 50;
+ max_button_h = 0;
+ w = h = 2;
+ i = t = total_button_w = total_text_and_icon_w = 0;
+ max_button_w *= controls->window->iscale;
+
+ /* Positioning and sizing */
+ for (i = 0; i < controls->messageboxdata->numbuttons; i++) {
+ max_button_w = SDL_max(max_button_w, controls->buttons[i]->rect.w);
+ max_button_h = SDL_max(max_button_h, controls->buttons[i]->rect.h);
+ controls->buttons[i]->rect.x = 0;
+ }
+
+ if (controls->icon) {
+ controls->icon->rect.y = 0;
+ }
+
+ if (controls->icon) {
+ controls->message->rect.x = 0;
+ controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon);
+ controls->icon->rect.x = (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) + controls->message->rect.w + controls->message->rect.x;
+ } else {
+ controls->message->rect.x = 0;
+ controls->message->rect.y = -2 * controls->window->iscale;
+ controls->icon = &controls->fake_icon;
+ controls->icon->rect.w = 0;
+ controls->icon->rect.h = 0;
+ controls->icon->rect.x = 0;
+ controls->icon->rect.y = 0;
+ }
+ if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
+ for (i = controls->messageboxdata->numbuttons; i != -1; i--) {
+ controls->buttons[i]->rect.w = max_button_w;
+ controls->buttons[i]->rect.h = max_button_h;
+ X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]);
+
+ if (controls->icon->rect.h > controls->message->rect.h) {
+ controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 *controls-> window->iscale);
+ } else {
+ controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ }
+
+ if (i) {
+ controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale);
+ }
+ }
+ } else {
+ for (i = 0; i < controls->messageboxdata->numbuttons; i++) {
+ controls->buttons[i]->rect.w = max_button_w;
+ controls->buttons[i]->rect.h = max_button_h;
+ X11Toolkit_NotifyControlOfSizeChange(controls->buttons[i]);
+
+ if (controls->icon->rect.h > controls->message->rect.h) {
+ controls->buttons[i]->rect.y = controls->icon->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ } else {
+ controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ }
+
+ if (i) {
+ controls->buttons[i]->rect.x = controls->buttons[i-1]->rect.x + controls->buttons[i-1]->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_3 * controls->window->iscale);
+ }
+ }
+ }
+ total_button_w = controls->buttons[controls->messageboxdata->numbuttons-1]->rect.x + controls->buttons[controls->messageboxdata->numbuttons-1]->rect.w;
+ total_text_and_icon_w = controls->message->rect.w + controls->icon->rect.w + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ if (total_button_w > total_text_and_icon_w) {
+ w = total_button_w;
+ } else {
+ w = total_text_and_icon_w;
+ }
+ w += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) * 2;
+ if (controls->message->rect.h > controls->icon->rect.h) {
+ h = controls->message->rect.h;
+ } else {
+ h = controls->icon->rect.h;
+ }
+ h += max_button_h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) * 3;
+ t = (w - total_text_and_icon_w) / 2;
+ controls->icon->rect.x += t;
+ controls->message->rect.x += t;
+ controls->icon->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ controls->message->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ t = (w - total_button_w) / 2;
+ for (i = 0; i < controls->messageboxdata->numbuttons; i++) {
+ controls->buttons[i]->rect.x += t;
+ controls->buttons[i]->rect.y += (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+ }
+ if (!controls->messageboxdata->message) {
+ controls->icon->rect.x = (w - controls->icon->rect.w)/2;
+ }
+
+ *wp = w;
+ *hp = h;
+}
+
+
static void X11_OnMessageBoxScaleChange(SDL_ToolkitWindowX11 *window, void *data) {
SDL_MessageBoxControlsX11 *controls;
int w;
@@ -220,7 +328,11 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int
}
/* Positioning */
- X11_PositionMessageBox(&controls, &w, &h);
+ if (data.window->flip_interface) {
+ X11_PositionMessageBoxFlipped(&controls, &w, &h);
+ } else {
+ X11_PositionMessageBox(&controls, &w, &h);
+ }
/* Actually create window, do event loop, cleanup */
X11Toolkit_CreateWindowRes(controls.window, w, h, 0, 0, (char *)messageboxdata->title);
diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c
index 14aba12a3afe5..3790d2894a80a 100644
--- a/src/video/x11/SDL_x11toolkit.c
+++ b/src/video/x11/SDL_x11toolkit.c
@@ -603,6 +603,42 @@ static void X11Toolkit_GetTextWidthHeight(SDL_ToolkitWindowX11 *data, const char
}
}
+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;
@@ -809,9 +845,14 @@ SDL_ToolkitWindowX11 *X11Toolkit_CreateWindowStruct(SDL_Window *parent, SDL_Tool
/* Menu windows */
window->popup_windows = NULL;
+ /* BIDI engine */
#ifdef HAVE_FRIBIDI_H
window->fribidi = SDL_FriBidi_Create();
#endif
+
+ /* Interface direction */
+ window->flip_interface = X11Toolkit_ShouldFlipUI();
+
return window;
}
diff --git a/src/video/x11/SDL_x11toolkit.h b/src/video/x11/SDL_x11toolkit.h
index 69ec8dcb97d66..0e882b32a928a 100644
--- a/src/video/x11/SDL_x11toolkit.h
+++ b/src/video/x11/SDL_x11toolkit.h
@@ -72,9 +72,9 @@ typedef struct SDL_ToolkitWindowX11
Window window;
Drawable drawable;
#ifndef NO_SHARED_MEMORY
- XImage *image;
- XShmSegmentInfo shm_info;
- int shm_bytes_per_line;
+ XImage *image;
+ XShmSegmentInfo shm_info;
+ int shm_bytes_per_line;
#endif
/* Visuals and drawing */
@@ -94,8 +94,8 @@ typedef struct SDL_ToolkitWindowX11
bool xrandr; // Whether Xrandr is present or not
#endif
#ifndef NO_SHARED_MEMORY
- bool shm;
- Bool shm_pixmap;
+ bool shm;
+ Bool shm_pixmap;
#endif
bool utf8;
/* Atoms */
@@ -160,9 +160,11 @@ typedef struct SDL_ToolkitWindowX11
#ifdef HAVE_FRIBIDI_H
/* BIDI engine */
- SDL_FriBidi *fribidi;
- bool do_shaping;
+ SDL_FriBidi *fribidi;
+ bool do_shaping;
#endif
+
+ bool flip_interface;
} SDL_ToolkitWindowX11;
typedef enum SDL_ToolkitControlStateX11
@@ -183,7 +185,7 @@ typedef struct SDL_ToolkitControlX11
bool dynamic;
bool is_default_enter;
bool is_default_esc;
- bool do_size;
+ bool do_size;
/* User data */
void *data;