SDL: X11TK: Introduce Thai support and rewrite/cleanup messagebox positioning code (#14474)

From 36976ecb43132b25d7286d4fe386600922929207 Mon Sep 17 00:00:00 2001
From: eafton <[EMAIL REDACTED]>
Date: Fri, 21 Nov 2025 02:26:46 +0300
Subject: [PATCH] X11TK: Introduce Thai support and rewrite/cleanup messagebox
 positioning code (#14474)

---
 CMakeLists.txt                                |   3 +
 cmake/sdlchecks.cmake                         |  25 +
 include/build_config/SDL_build_config.h.cmake |   2 +
 src/core/unix/SDL_libthai.c                   |  76 ++
 src/core/unix/SDL_libthai.h                   |  43 +
 src/video/x11/SDL_x11messagebox.c             | 333 ++++----
 src/video/x11/SDL_x11sym.h                    |   1 +
 src/video/x11/SDL_x11toolkit.c                | 742 ++++++++++++------
 src/video/x11/SDL_x11toolkit.h                |  29 +-
 test/testmessage.c                            |   5 +
 10 files changed, 829 insertions(+), 430 deletions(-)
 create mode 100644 src/core/unix/SDL_libthai.c
 create mode 100644 src/core/unix/SDL_libthai.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 150f76fe8e4ea..585119e84a82a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -349,6 +349,8 @@ 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_LIBTHAI             "Enable Thai support" ON SDL_X11 OFF)
+dep_option(SDL_LIBTHAI_SHARED      "Dynamically load Thai support" ON "SDL_LIBTHAI;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)
@@ -1813,6 +1815,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
     CheckROCKCHIP()
     CheckX11()
     CheckFribidi()
+    CheckLibThai()
     # Need to check for EGL first because KMSDRM and Wayland depend on it.
     CheckEGL()
     CheckKMSDRM()
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index 0180eaa124d0a..625c895a5e133 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -588,6 +588,31 @@ macro(CheckFribidi)
   endif()
 endmacro()
 
+macro(CheckLibThai)
+  if(SDL_LIBTHAI)
+    set(LIBTHAI_PKG_CONFIG_SPEC libthai)
+    set(PC_LIBTHAI_FOUND FALSE)
+    if(PKG_CONFIG_FOUND)
+      pkg_check_modules(PC_LIBTHAI IMPORTED_TARGET ${LIBTHAI_PKG_CONFIG_SPEC})
+    endif()
+    if(PC_LIBTHAI_FOUND)
+      set(HAVE_LIBTHAI TRUE)
+      set(HAVE_LIBTHAI_H 1)
+      if(SDL_LIBTHAI_SHARED AND NOT HAVE_SDL_LOADSO)
+        message(WARNING "You must have SDL_LoadObject() support for dynamic libthai loading")
+      endif()
+      FindLibraryAndSONAME("thai" LIBDIRS ${PC_LIBTHAI_LIBRARY_DIRS})
+      if(SDL_LIBTHAI_SHARED AND THAI_LIB AND HAVE_SDL_LOADSO)
+        set(SDL_LIBTHAI_DYNAMIC "\"${THAI_LIB_SONAME}\"")
+        set(HAVE_LIBTHAI_SHARED TRUE)
+        sdl_include_directories(PRIVATE SYSTEM $<TARGET_PROPERTY:PkgConfig::PC_LIBTHAI,INTERFACE_INCLUDE_DIRECTORIES>)
+      else()
+        sdl_link_dependency(libthai LIBS PkgConfig::PC_LIBTHAI PKG_CONFIG_PREFIX PC_LIBTHAI PKG_CONFIG_SPECS ${LIBTHAI_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")
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 35560da940507..5501d4931021c 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -218,6 +218,8 @@
 #cmakedefine HAVE_LIBURING_H 1
 #cmakedefine HAVE_FRIBIDI_H 1
 #cmakedefine SDL_FRIBIDI_DYNAMIC @SDL_FRIBIDI_DYNAMIC@
+#cmakedefine HAVE_LIBTHAI_H 1
+#cmakedefine SDL_LIBTHAI_DYNAMIC @SDL_LIBTHAI_DYNAMIC@
 
 #cmakedefine HAVE_DDRAW_H 1
 #cmakedefine HAVE_DSOUND_H 1
diff --git a/src/core/unix/SDL_libthai.c b/src/core/unix/SDL_libthai.c
new file mode 100644
index 0000000000000..022db50409eb6
--- /dev/null
+++ b/src/core/unix/SDL_libthai.c
@@ -0,0 +1,76 @@
+/*
+  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_LIBTHAI_H
+
+#include "SDL_libthai.h"
+
+#ifdef SDL_LIBTHAI_DYNAMIC
+SDL_ELF_NOTE_DLOPEN(
+    "Thai",
+    "Thai language support",
+    SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
+    SDL_LIBTHAI_DYNAMIC
+);
+#endif
+
+
+SDL_LibThai *SDL_LibThai_Create(void)
+{
+    SDL_LibThai *th;
+
+    th = (SDL_LibThai *)SDL_malloc(sizeof(SDL_LibThai));
+    if (!th) {
+        return NULL;
+    }
+
+#ifdef SDL_LIBTHAI_DYNAMIC
+    #define SDL_LIBTHAI_LOAD_SYM(a, x, n, t) x = ((t)SDL_LoadFunction(a->lib, n)); if (!x) { SDL_UnloadObject(a->lib); SDL_free(a); return NULL; }
+
+    th->lib = SDL_LoadObject(SDL_LIBTHAI_DYNAMIC);
+    if (!th->lib) {
+        SDL_free(th);
+        return NULL;
+    }
+
+    SDL_LIBTHAI_LOAD_SYM(th, th->make_cells, "th_make_cells", SDL_LibThaiMakeCells);
+#else
+    th->make_cells = th_make_cells;
+#endif
+
+    return th;
+}
+
+void SDL_LibThai_Destroy(SDL_LibThai *th)
+{
+    if (!th) {
+        return;
+    }
+
+#ifdef SDL_LIBTHAI_DYNAMIC
+    SDL_UnloadObject(th->lib);
+#endif
+
+    SDL_free(th);
+}
+
+#endif
diff --git a/src/core/unix/SDL_libthai.h b/src/core/unix/SDL_libthai.h
new file mode 100644
index 0000000000000..05a3c06fc6643
--- /dev/null
+++ b/src/core/unix/SDL_libthai.h
@@ -0,0 +1,43 @@
+/*
+  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"
+
+#ifndef SDL_libthai_h_
+#define SDL_libthai_h_
+
+#ifdef HAVE_LIBTHAI_H
+#include <thai/thcell.h>
+
+typedef size_t (*SDL_LibThaiMakeCells)(const thchar_t *s, size_t, struct thcell_t cells[], size_t *, int);
+
+typedef struct SDL_LibThai {
+    SDL_SharedObject *lib;
+ 
+    SDL_LibThaiMakeCells make_cells;
+} SDL_LibThai;
+
+extern SDL_LibThai *SDL_LibThai_Create(void);
+extern void SDL_LibThai_Destroy(SDL_LibThai *th);
+
+#endif // HAVE_LIBTHAI_H
+
+#endif // SDL_libthai_h_
diff --git a/src/video/x11/SDL_x11messagebox.c b/src/video/x11/SDL_x11messagebox.c
index 158a65b45e268..8486c7a292d6d 100644
--- a/src/video/x11/SDL_x11messagebox.c
+++ b/src/video/x11/SDL_x11messagebox.c
@@ -28,7 +28,7 @@
 #include "SDL_x11toolkit.h"
 
 #ifndef SDL_FORK_MESSAGEBOX
-#define SDL_FORK_MESSAGEBOX 1
+#define SDL_FORK_MESSAGEBOX 0
 #endif
 
 #if SDL_FORK_MESSAGEBOX
@@ -38,246 +38,181 @@
 #include <errno.h>
 #endif
 
-typedef struct SDL_MessageBoxCallbackDataX11
-{
-    int *buttonID;
-    SDL_ToolkitWindowX11 *window;
-} SDL_MessageBoxCallbackDataX11;
-
-typedef struct SDL_MessageBoxControlsX11
+typedef struct SDL_MessageBoxX11
 {
     SDL_ToolkitWindowX11 *window;
     SDL_ToolkitControlX11 *icon;
-    SDL_ToolkitControlX11 fake_icon;
     SDL_ToolkitControlX11 *message;
     SDL_ToolkitControlX11 **buttons;
     const SDL_MessageBoxData *messageboxdata;
-} SDL_MessageBoxControlsX11;
+    int *buttonID;
+} SDL_MessageBoxX11;
 
 static void X11_MessageBoxButtonCallback(SDL_ToolkitControlX11 *control, void *data)
 {
-    SDL_MessageBoxCallbackDataX11 *cbdata;
+    SDL_MessageBoxX11 *cbdata;
 
-    cbdata = (SDL_MessageBoxCallbackDataX11 *)data;
+    cbdata = data;
     *cbdata->buttonID = X11Toolkit_GetButtonControlData(control)->buttonID;
     X11Toolkit_SignalWindowClose(cbdata->window);
 }
 
-static void X11_PositionMessageBox(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;
+static void X11_PositionMessageBox(SDL_MessageBoxX11 *controls, int *wp, int *hp) {
+    int first_line_width;
+    int first_line_height;
+    int second_line_width;
+    int second_line_height;
+    int max_button_width;
+    int max_button_height;
+    int window_width;
+    int window_height;
     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.x = controls->icon->rect.y = 0;
-    }
-
-    if (controls->icon) {
-        controls->message->rect.x = (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale) + controls->icon->rect.x + controls->icon->rect.w;
-        controls->message->rect.y = X11Toolkit_GetIconControlCharY(controls->icon);
+    bool rtl;
+    
+    /* window size */
+    window_width = 1;
+    window_height = 1;
+    
+    /* rtl */
+    if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
+        rtl = true;
+    } else if (controls->messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
+        rtl = false;
     } else {
+        rtl = controls->window->flip_interface;
+    }
+    
+    /* first line */
+    first_line_width = first_line_height = 0;
+    if (controls->icon && controls->message) {
+        controls->icon->rect.y = 0;
+        
+        first_line_width = controls->icon->rect.w + SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale + controls->message->rect.w;  
+        
+        if (!controls->window->flip_interface) {
+            controls->message->rect.x = controls->icon->rect.w + SDL_TOOLKIT_X11_ELEMENT_PADDING_2  * controls->window->iscale;
+            controls->icon->rect.x = 0;
+        } else {
+            controls->message->rect.x = 0;    
+            controls->icon->rect.x = controls->message->rect.w + SDL_TOOLKIT_X11_ELEMENT_PADDING_2  * controls->window->iscale;;
+        }
+        
+        if (controls->message->rect.h > controls->icon->rect.h) {
+            controls->message->rect.y = (controls->icon->rect.h - X11Toolkit_GetLabelControlFirstLineHeight(controls->message))/2;
+            first_line_height = controls->message->rect.y + controls->message->rect.h;
+        } else {
+            controls->message->rect.y = (controls->icon->rect.h - controls->message->rect.h)/2;
+            first_line_height = controls->icon->rect.h;
+        }        
+    } else if (!controls->icon && controls->message) {
+        first_line_width = controls->message->rect.w;  
+        first_line_height = controls->message->rect.h;
         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->message->rect.y = 0;
+    } else if (controls->icon && !controls->message) {
+        first_line_width = controls->icon->rect.w;  
+        first_line_height = controls->icon->rect.h;
         controls->icon->rect.x = 0;
-        controls->icon->rect.y = 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;
+    
+    /* second line */
+    max_button_width = 50;
+    max_button_height = 0;
+    second_line_width = second_line_height = 0;
+    
+    for (i = 0; i < controls->messageboxdata->numbuttons; i++) {
+        max_button_width = SDL_max(max_button_width, controls->buttons[i]->rect.w);
+        max_button_height = SDL_max(max_button_height, controls->buttons[i]->rect.h);
+        controls->buttons[i]->rect.x = 0;
+        controls->buttons[i]->rect.y = 0;
+    }
+
+    if (rtl) {
+        for (i = (controls->messageboxdata->numbuttons - 1); i != -1; i--) {
+            controls->buttons[i]->rect.w = max_button_width;
+            controls->buttons[i]->rect.h = max_button_height;
             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);
+            if (first_line_height) {
+                controls->buttons[i]->rect.y = first_line_height + SDL_TOOLKIT_X11_ELEMENT_PADDING_4 * controls->window->iscale;
+                second_line_height = max_button_height + SDL_TOOLKIT_X11_ELEMENT_PADDING_4 * controls->window->iscale;
             } else {
-                controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+                second_line_height = max_button_height;
             }
-
-            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);
+            
+            if ((i + 1) < controls->messageboxdata->numbuttons) {
+                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;
+            controls->buttons[i]->rect.w = max_button_width;
+            controls->buttons[i]->rect.h = max_button_height;
             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);
+            if (first_line_height) {
+                controls->buttons[i]->rect.y = first_line_height + SDL_TOOLKIT_X11_ELEMENT_PADDING_4 * controls->window->iscale;
+                second_line_height = max_button_height + SDL_TOOLKIT_X11_ELEMENT_PADDING_4 * controls->window->iscale;
             } else {
-                controls->buttons[i]->rect.y = controls->message->rect.h + (SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale);
+                second_line_height = max_button_height;
             }
-
+            
             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.x + controls->message->rect.w;
-    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_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->numbuttons) {
+        if (rtl) {
+            second_line_width = controls->buttons[0]->rect.x + controls->buttons[0]->rect.w;
+        } else {
+            second_line_width = controls->buttons[controls->messageboxdata->numbuttons - 1]->rect.x + controls->buttons[controls->messageboxdata->numbuttons - 1]->rect.w;            
+        }
     }
-    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);
-            }
+    /* center lines */
+    if (second_line_width > first_line_width) {
+        int pad;
+        
+        pad = (second_line_width - first_line_width)/2;
+        if (controls->message) {
+            controls->message->rect.x += pad;
+        }
+        if (controls->icon) {
+            controls->icon->rect.x += pad;
         }
     } else {
+        int pad;
+        
+        pad = (first_line_width - second_line_width)/2;
         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);
-            }
+            controls->buttons[i]->rect.x += pad;
         }
     }
-    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;
+     
+    /* window size and final padding */
+    window_width = SDL_max(first_line_width, second_line_width) + SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * 2 * controls->window->iscale;
+    window_height = first_line_height + second_line_height + SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * 2 * controls->window->iscale;
+    *wp = window_width;
+    *hp = window_height;
+    if (controls->message) {
+        controls->message->rect.x += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale;
+        controls->message->rect.y += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale;
     }
-    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;
+    if (controls->icon) {
+        controls->icon->rect.x += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale;
+        controls->icon->rect.y += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale;
     }
-    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);
+        controls->buttons[i]->rect.x += SDL_TOOLKIT_X11_ELEMENT_PADDING_2 * controls->window->iscale;
+        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;
+    SDL_MessageBoxX11 *controls;
     int w;
     int h;
 
@@ -290,8 +225,7 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int
 {
     SDL_VideoDevice *video = SDL_GetVideoDevice();
     SDL_Window *parent_window = NULL;
-    SDL_MessageBoxControlsX11 controls;
-    SDL_MessageBoxCallbackDataX11 data;
+    SDL_MessageBoxX11 controls;
     const SDL_MessageBoxColor *colorhints;
     int i;
     int w;
@@ -323,22 +257,17 @@ static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int
     }
 
     /* Create controls */
+    controls.buttonID = buttonID;
     controls.buttons = SDL_calloc(messageboxdata->numbuttons, sizeof(SDL_ToolkitControlX11 *));
     controls.icon = X11Toolkit_CreateIconControl(controls.window, messageboxdata->flags);
     controls.message = X11Toolkit_CreateLabelControl(controls.window, (char *)messageboxdata->message);
-    data.buttonID = buttonID;
-    data.window = controls.window;
     for (i = 0; i < messageboxdata->numbuttons; i++) {
         controls.buttons[i] = X11Toolkit_CreateButtonControl(controls.window, &messageboxdata->buttons[i]);
-        X11Toolkit_RegisterCallbackForButtonControl(controls.buttons[i], &data, X11_MessageBoxButtonCallback);
+        X11Toolkit_RegisterCallbackForButtonControl(controls.buttons[i], &controls, X11_MessageBoxButtonCallback);
     }
 
     /* Positioning */
-    if (data.window->flip_interface) {
-        X11_PositionMessageBoxFlipped(&controls, &w, &h);
-    } else {
-        X11_PositionMessageBox(&controls, &w, &h);
-    }
+    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_x11sym.h b/src/video/x11/SDL_x11sym.h
index 62c3e00237ff3..1e0db473cf217 100644
--- a/src/video/x11/SDL_x11sym.h
+++ b/src/video/x11/SDL_x11sym.h
@@ -56,6 +56,7 @@ SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c))
 SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b))
 SDL_X11_SYM(int,XDisplayKeycodes,(Display* a,int* b,int* c))
 SDL_X11_SYM(int,XDrawRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g))
+SDL_X11_SYM(int,XFontsOfFontSet,(XFontSet a,XFontStruct ***b,char ***c))
 SDL_X11_SYM(int,XFillArc,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g, int h, int i))
 SDL_X11_SYM(char*,XDisplayName,(_Xconst char* a))
 SDL_X11_SYM(int,XDrawString,(Display* a,Drawable b,GC c,int d,int e,_Xconst char* f,int g))
diff --git a/src/video/x11/SDL_x11toolkit.c b/src/video/x11/SDL_x11toolkit.c
index 6f895fbd784d8..f690c72cca5db 100644
--- a/src/video/x11/SDL_x11toolkit.c
+++ b/src/video/x11/SDL_x11toolkit.c
@@ -42,6 +42,35 @@
 #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;
@@ -75,25 +104,28 @@ typedef struct SDL_ToolkitButtonControlX11
     const SDL_MessageBoxButtonData *data;
 
     /* Text */
+    SDL_ListNode *text;
     SDL_Rect text_rect;
-    int text_a;
-    int text_d;
-    int str_sz;
-#ifdef HAVE_FRIBIDI_H
-    char *text;
-    bool free_text;
-#endif
-
+    
     /* Callback */
     void *cb_data;
     void (*cb)(struct SDL_ToolkitControlX11 *, void *);
 } SDL_ToolkitButtonControlX11;
 
+typedef struct SDL_ToolkitLabelControlLineX11
+{
+    SDL_ListNode *text;
+    SDL_Rect rect;
+#ifdef HAVE_FRIBIDI_H
+    FriBidiParType par;
+#endif
+} SDL_ToolkitLabelControlLineX11;
+
 typedef struct SDL_ToolkitLabelControlX11
 {
     SDL_ToolkitControlX11 parent;
 
-    char **lines;
+ /*   char **lines;
     int *y;
     size_t *szs;
     size_t sz;
@@ -102,7 +134,9 @@ typedef struct SDL_ToolkitLabelControlX11
     int *w;
     bool *free_lines;
     FriBidiParType *par_types;
-#endif
+#endif*/
+    SDL_ToolkitLabelControlLineX11 *lines;
+    size_t sz;
 } SDL_ToolkitLabelControlX11;
 
 typedef struct SDL_ToolkitMenuBarControlX11
@@ -258,7 +292,9 @@ static void X11Toolkit_InitWindowPixmap(SDL_ToolkitWindowX11 *data) {
 }
 
 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;
@@ -301,9 +337,56 @@ static void X11Toolkit_InitWindowFonts(SDL_ToolkitWindowX11 *window)
         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
+            /* TODO: What to do the XFontSet happens to have more than one Thai font? */
+            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;
+                }
+                                                
+      

(Patch may be truncated, please check the link at the top of this post.)