SDL: Added missing files

From 88cbe85fe2302054bc945090e398cf60f8d62362 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 31 Jul 2025 08:57:59 -0700
Subject: [PATCH] Added missing files

---
 src/core/unix/SDL_gtk.c | 229 ++++++++++++++++++++++++++++++++++++++++
 src/core/unix/SDL_gtk.h | 123 +++++++++++++++++++++
 2 files changed, 352 insertions(+)
 create mode 100644 src/core/unix/SDL_gtk.c
 create mode 100644 src/core/unix/SDL_gtk.h

diff --git a/src/core/unix/SDL_gtk.c b/src/core/unix/SDL_gtk.c
new file mode 100644
index 0000000000000..f5f62ce9350ae
--- /dev/null
+++ b/src/core/unix/SDL_gtk.c
@@ -0,0 +1,229 @@
+/*
+  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"
+#include "SDL_gtk.h"
+
+#define SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sym)                     \
+    ctx.sub.fn = (void *)SDL_LoadFunction(lib, #sym)
+
+#define SDL_GTK_SYM2(ctx, lib, sub, fn, sym)                              \
+    if (!(ctx.sub.fn = (void *)SDL_LoadFunction(lib, #sym))) {            \
+        return SDL_SetError("Could not load GTK functions");              \
+    }
+
+#define SDL_GTK_SYM_OPTIONAL(ctx, lib, sub, fn) \
+    SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sub##_##fn)
+
+#define SDL_GTK_SYM(ctx, lib, sub, fn) \
+    SDL_GTK_SYM2(ctx, lib, sub, fn, sub##_##fn)
+
+// we never link directly to gtk
+static const char *gdk_names[] = {
+#ifdef SDL_PLATFORM_OPENBSD
+    "libgdk-3.so",
+#else
+    "libgdk-3.so.0",
+#endif
+    NULL
+};
+
+static const char *gtk_names[] = {
+#ifdef SDL_PLATFORM_OPENBSD
+    "libgtk-3.so",
+#else
+    "libgtk-3.so.0",
+#endif
+    NULL
+};
+
+static void *libgdk = NULL;
+static void *libgtk = NULL;
+
+static SDL_GtkContext gtk;
+static GMainContext *sdl_main_context;
+
+gulong signal_connect(gpointer instance, const gchar *detailed_signal, void *c_handler, gpointer data)
+{
+    return gtk.g.signal_connect_data(instance, detailed_signal, SDL_G_CALLBACK(c_handler), data, NULL, (SDL_GConnectFlags)0);
+}
+
+static void QuitGtk(void)
+{
+    SDL_UnloadObject(libgdk);
+    SDL_UnloadObject(libgtk);
+
+    libgdk = NULL;
+    libgtk = NULL;
+}
+
+static void *FindLib(const char **names)
+{
+    const char **name_ptr = names;
+    void *handle = NULL;
+
+    do {
+        handle = SDL_LoadObject(*name_ptr);
+    } while (*++name_ptr && !handle);
+
+    return handle;
+}
+
+static bool IsGtkInit()
+{
+    return libgdk != NULL && libgtk != NULL;
+}
+
+static bool InitGtk(void)
+{
+    if (!SDL_GetHintBoolean("SDL_ENABLE_GTK", true)) {
+        return false;
+    }
+
+    if (IsGtkInit()) {
+        return true;
+    }
+
+    libgdk = FindLib(gdk_names);
+    libgtk = FindLib(gtk_names);
+
+    if (!libgdk || !libgtk) {
+        QuitGtk();
+        return SDL_SetError("Could not load GTK libraries");
+    }
+
+    SDL_GTK_SYM(gtk, libgtk, gtk, init_check);
+    SDL_GTK_SYM(gtk, libgtk, gtk, menu_new);
+    SDL_GTK_SYM(gtk, libgtk, gtk, separator_menu_item_new);
+    SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_new_with_label);
+    SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_submenu);
+    SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_get_label);
+    SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_label);
+	SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_append);
+	SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_insert);
+    SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_new_with_label);
+    SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_get_active);
+    SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_set_active);
+    SDL_GTK_SYM(gtk, libgtk, gtk, widget_show);
+    SDL_GTK_SYM(gtk, libgtk, gtk, widget_destroy);
+    SDL_GTK_SYM(gtk, libgtk, gtk, widget_get_sensitive);
+    SDL_GTK_SYM(gtk, libgtk, gtk, widget_set_sensitive);
+    SDL_GTK_SYM(gtk, libgtk, gtk, settings_get_default);
+
+    SDL_GTK_SYM(gtk, libgdk, g, signal_connect_data);
+    SDL_GTK_SYM(gtk, libgdk, g, mkdtemp);
+    SDL_GTK_SYM(gtk, libgdk, g, object_ref);
+    SDL_GTK_SYM(gtk, libgdk, g, object_ref_sink);
+    SDL_GTK_SYM(gtk, libgdk, g, object_unref);
+    SDL_GTK_SYM(gtk, libgdk, g, object_get);
+    SDL_GTK_SYM(gtk, libgdk, g, signal_handler_disconnect);
+    SDL_GTK_SYM(gtk, libgdk, g, main_context_push_thread_default);
+    SDL_GTK_SYM(gtk, libgdk, g, main_context_pop_thread_default);
+    SDL_GTK_SYM(gtk, libgdk, g, main_context_new);
+    SDL_GTK_SYM(gtk, libgdk, g, main_context_acquire);
+    SDL_GTK_SYM(gtk, libgdk, g, main_context_iteration);
+
+    gtk.g.signal_connect = signal_connect;
+
+    if (gtk.gtk.init_check(0, NULL) == GTK_FALSE) {
+        QuitGtk();
+        return SDL_SetError("Could not init GTK");
+    }
+
+    sdl_main_context = gtk.g.main_context_new();
+    if (!sdl_main_context) {
+        QuitGtk();
+        return SDL_SetError("Could not create GTK context");
+    }
+
+    if (!gtk.g.main_context_acquire(sdl_main_context)) {
+        QuitGtk();
+        return SDL_SetError("Could not acquire GTK context");
+    }
+
+    return true;
+}
+
+static SDL_InitState gtk_init;
+
+bool SDL_Gtk_Init(void)
+{
+    static bool is_gtk_available = true;
+
+    if (!is_gtk_available) {
+        return false; // don't keep trying if this fails.
+    }
+
+    if (SDL_ShouldInit(&gtk_init)) {
+        if (InitGtk()) {
+            SDL_SetInitialized(&gtk_init, true);
+        } else {
+            is_gtk_available = false;
+            SDL_SetInitialized(&gtk_init, true);
+            SDL_Gtk_Quit();
+        }
+    }
+
+    return IsGtkInit();
+}
+
+void SDL_Gtk_Quit(void)
+{
+    if (!SDL_ShouldQuit(&gtk_init)) {
+        return;
+    }
+
+    QuitGtk();
+    SDL_zero(gtk);
+    sdl_main_context = NULL;
+
+    SDL_SetInitialized(&gtk_init, false);
+}
+
+SDL_GtkContext *SDL_Gtk_GetContext(void)
+{
+    return IsGtkInit() ? &gtk : NULL;
+}
+
+SDL_GtkContext *SDL_Gtk_EnterContext(void)
+{
+    SDL_Gtk_Init();
+
+    if (IsGtkInit()) {
+        gtk.g.main_context_push_thread_default(sdl_main_context);
+        return &gtk;
+    }
+
+    return NULL;
+}
+
+void SDL_Gtk_ExitContext(SDL_GtkContext *ctx)
+{
+    if (ctx) {
+        ctx->g.main_context_pop_thread_default(sdl_main_context);
+    }
+}
+
+void SDL_UpdateGtk(void)
+{
+    if (IsGtkInit()) {
+        gtk.g.main_context_iteration(sdl_main_context, GTK_FALSE);
+    }
+}
diff --git a/src/core/unix/SDL_gtk.h b/src/core/unix/SDL_gtk.h
new file mode 100644
index 0000000000000..e76720ddb61f8
--- /dev/null
+++ b/src/core/unix/SDL_gtk.h
@@ -0,0 +1,123 @@
+/*
+  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_gtk_h_
+#define SDL_gtk_h_
+
+/* Glib 2.0 */
+
+typedef unsigned long gulong;
+typedef void *gpointer;
+typedef char gchar;
+typedef int gint;
+typedef unsigned int guint;
+typedef double gdouble;
+typedef gint gboolean;
+typedef void (*GCallback)(void);
+typedef struct _GClosure GClosure;
+typedef void (*GClosureNotify) (gpointer data, GClosure *closure);
+typedef gboolean (*GSourceFunc) (gpointer user_data);
+
+typedef struct _GParamSpec GParamSpec;
+typedef struct _GMainContext GMainContext;
+
+typedef enum SDL_GConnectFlags
+{
+    SDL_G_CONNECT_DEFAULT = 0,
+    SDL_G_CONNECT_AFTER = 1 << 0,
+    SDL_G_CONNECT_SWAPPED = 1 << 1
+} SDL_GConnectFlags;
+
+#define SDL_G_CALLBACK(f) ((GCallback) (f))
+#define SDL_G_TYPE_CIC(ip, gt, ct) ((ct*) ip)
+#define SDL_G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type) (SDL_G_TYPE_CIC ((instance), (g_type), c_type))
+
+#define GTK_FALSE 0
+#define GTK_TRUE 1
+
+
+/* GTK 3.0 */
+
+typedef struct _GtkMenu GtkMenu;
+typedef struct _GtkMenuItem GtkMenuItem;
+typedef struct _GtkMenuShell GtkMenuShell;
+typedef struct _GtkWidget GtkWidget;
+typedef struct _GtkCheckMenuItem GtkCheckMenuItem;
+typedef struct _GtkSettings GtkSettings;
+
+#define GTK_MENU_ITEM(obj) (SDL_G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MENU_ITEM, GtkMenuItem))
+#define GTK_WIDGET(widget) (SDL_G_TYPE_CHECK_INSTANCE_CAST ((widget), GTK_TYPE_WIDGET, GtkWidget))
+#define GTK_CHECK_MENU_ITEM(obj) (SDL_G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CHECK_MENU_ITEM, GtkCheckMenuItem))
+#define GTK_MENU(obj) (SDL_G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_MENU, GtkMenu))
+
+
+typedef struct SDL_GtkContext
+{
+	/* Glib 2.0 */
+	struct 
+	{
+		gulong (*signal_connect)(gpointer instance, const gchar *detailed_signal, void *c_handler, gpointer data);
+		gulong (*signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, SDL_GConnectFlags connect_flags);
+		void (*object_unref)(gpointer object);
+		gchar *(*mkdtemp)(gchar *template);
+		gpointer (*object_ref_sink)(gpointer object);
+		gpointer (*object_ref)(gpointer object);
+		void (*object_get)(gpointer object, const gchar *first_property_name, ...);
+		void (*signal_handler_disconnect)(gpointer instance, gulong handler_id);
+		void (*main_context_push_thread_default)(GMainContext *context);
+		void (*main_context_pop_thread_default)(GMainContext *context);
+		GMainContext *(*main_context_new)(void);
+		gboolean (*main_context_acquire)(GMainContext *context);
+		gboolean (*main_context_iteration)(GMainContext *context, gboolean may_block);
+	} g;
+
+	/* GTK 3.0 */
+	struct
+	{
+		gboolean (*init_check)(int *argc, char ***argv);
+		GtkWidget *(*menu_new)(void);
+		GtkWidget *(*separator_menu_item_new)(void);
+		GtkWidget *(*menu_item_new_with_label)(const gchar *label);
+		void (*menu_item_set_submenu)(GtkMenuItem *menu_item, GtkWidget *submenu);
+		GtkWidget *(*check_menu_item_new_with_label)(const gchar *label);
+		void (*check_menu_item_set_active)(GtkCheckMenuItem *check_menu_item, gboolean is_active);
+		void (*widget_set_sensitive)(GtkWidget *widget, gboolean sensitive);
+		void (*widget_show)(GtkWidget *widget);
+		void (*menu_shell_append)(GtkMenuShell *menu_shell, GtkWidget *child);
+		void (*menu_shell_insert)(GtkMenuShell *menu_shell, GtkWidget *child, gint position);
+		void (*widget_destroy)(GtkWidget *widget);
+		const gchar *(*menu_item_get_label)(GtkMenuItem *menu_item);
+		void (*menu_item_set_label)(GtkMenuItem *menu_item, const gchar *label);
+		gboolean (*check_menu_item_get_active)(GtkCheckMenuItem *check_menu_item);
+		gboolean (*widget_get_sensitive)(GtkWidget *widget);
+		GtkSettings *(*settings_get_default)(void);
+	} gtk;
+} SDL_GtkContext;
+
+extern bool SDL_Gtk_Init(void);
+extern void SDL_Gtk_Quit(void);
+extern SDL_GtkContext *SDL_Gtk_EnterContext(void);
+extern void SDL_Gtk_ExitContext(SDL_GtkContext *gtk);
+extern void SDL_UpdateGtk(void);
+
+#endif // SDL_gtk_h_