diff --git a/src/SDL.c b/src/SDL.c index cd7ec4b656..2d62cf42c6 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -30,10 +30,6 @@ // this checks for HAVE_DBUS_DBUS_H internally. #include "core/linux/SDL_dbus.h" -#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) -#include "core/unix/SDL_gtk.h" -#endif - #ifdef SDL_PLATFORM_EMSCRIPTEN #include #endif @@ -714,10 +710,6 @@ void SDL_Quit(void) SDL_DBus_Quit(); #endif -#if defined(SDL_PLATFORM_UNIX) && !defined(SDL_PLATFORM_ANDROID) && !defined(SDL_PLATFORM_EMSCRIPTEN) && !defined(SDL_PLATFORM_PRIVATE) - SDL_Gtk_Quit(); -#endif - SDL_QuitTimers(); SDL_QuitAsyncIO(); diff --git a/src/core/unix/SDL_gtk.c b/src/core/unix/SDL_gtk.c deleted file mode 100644 index 92e8a16ff9..0000000000 --- a/src/core/unix/SDL_gtk.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2026 Sam Lantinga - - 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" - -#include -#include -#include - -#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) \ - SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sym); \ - if (!ctx.sub.fn) { \ - 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) - -#ifdef SDL_PLATFORM_OPENBSD -#define GDK3_LIB "libgdk-3.so" -#else -#define GDK3_LIB "libgdk-3.so.0" -#endif - -#ifdef SDL_PLATFORM_OPENBSD -#define GTK3_LIB "libgtk-3.so" -#else -#define GTK3_LIB "libgtk-3.so.0" -#endif - -// we never link directly to gtk -static void *libgdk = NULL; -static void *libgtk = NULL; - -static SDL_GtkContext gtk; -static GMainContext *sdl_main_context; - -static 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) -{ - if (sdl_main_context) { - gtk.g.main_context_unref(sdl_main_context); - sdl_main_context = NULL; - } - - SDL_UnloadObject(libgdk); - SDL_UnloadObject(libgtk); - - libgdk = NULL; - libgtk = NULL; -} - -static bool IsGtkInit(void) -{ - return libgdk != NULL && libgtk != NULL; -} - -#ifndef HAVE_GETRESUID -// Non-POSIX, but Linux and some BSDs have it. -// To reduce the number of code paths, if getresuid() isn't available at -// compile-time, we behave as though it existed but failed at runtime. -static inline int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { - errno = ENOSYS; - return -1; -} -#endif - -#ifndef HAVE_GETRESGID -// Same as getresuid() but for the primary group -static inline int getresgid(uid_t *ruid, uid_t *euid, uid_t *suid) { - errno = ENOSYS; - return -1; -} -#endif - -bool SDL_CanUseGtk(void) -{ - // "Real", "effective" and "saved" IDs: see e.g. Linux credentials(7) - uid_t ruid = -1, euid = -1, suid = -1; - gid_t rgid = -1, egid = -1, sgid = -1; - - if (!SDL_GetHintBoolean("SDL_ENABLE_GTK", true)) { - SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to hint"); - return false; - } - - // This is intended to match the check in gtkmain.c, rather than being - // an exhaustive check for having elevated privileges: as a result - // we don't use Linux getauxval() or prctl PR_GET_DUMPABLE, - // BSD issetugid(), or similar OS-specific detection - - if (getresuid(&ruid, &euid, &suid) != 0) { - ruid = suid = getuid(); - euid = geteuid(); - } - - if (getresgid(&rgid, &egid, &sgid) != 0) { - rgid = sgid = getgid(); - egid = getegid(); - } - - // Real ID != effective ID means we are setuid or setgid: - // GTK will refuse to initialize, and instead will call exit(). - if (ruid != euid || rgid != egid) { - SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to setuid/setgid"); - return false; - } - - // Real ID != saved ID means we are setuid or setgid, we previously - // dropped privileges, but we can regain them; this protects against - // accidents but does not protect against arbitrary code execution. - // Again, GTK will refuse to initialize if this is the case. - if (ruid != suid || rgid != sgid) { - SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to saved uid/gid"); - return false; - } - - return true; -} - -static bool InitGtk(void) -{ - if (!SDL_CanUseGtk()) { - return false; - } - - if (IsGtkInit()) { - return true; - } - - // GTK only allows a single version to be loaded into a process at a time, - // so if there is one already loaded ensure it is the version we use. - void *progress_get_type = dlsym(RTLD_DEFAULT, "gtk_progress_get_type"); - void *misc_get_type = dlsym(RTLD_DEFAULT, "gtk_misc_get_type"); - if (progress_get_type || misc_get_type) { - void *libgtk3 = dlopen(GTK3_LIB, RTLD_NOLOAD | RTLD_LAZY); - if (!libgtk3) { - QuitGtk(); - return SDL_SetError("Could not load GTK-3, another GTK version already present"); - } - - dlclose(libgtk3); - } - - libgdk = SDL_LoadObject(GDK3_LIB); - libgtk = SDL_LoadObject(GTK3_LIB); - - 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, get_user_cache_dir); - 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_unref); - 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(NULL, 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(>k_init)) { - if (InitGtk()) { - SDL_SetInitialized(>k_init, true); - } else { - is_gtk_available = false; - SDL_SetInitialized(>k_init, true); - SDL_Gtk_Quit(); - } - } - - return IsGtkInit(); -} - -void SDL_Gtk_Quit(void) -{ - if (!SDL_ShouldQuit(>k_init)) { - return; - } - - QuitGtk(); - SDL_zero(gtk); - - SDL_SetInitialized(>k_init, false); -} - -SDL_GtkContext *SDL_Gtk_GetContext(void) -{ - return IsGtkInit() ? >k : NULL; -} - -SDL_GtkContext *SDL_Gtk_EnterContext(void) -{ - SDL_Gtk_Init(); - - if (IsGtkInit()) { - gtk.g.main_context_push_thread_default(sdl_main_context); - return >k; - } - - 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); - gtk.g.main_context_iteration(NULL, GTK_FALSE); - } -} diff --git a/src/core/unix/SDL_gtk.h b/src/core/unix/SDL_gtk.h deleted file mode 100644 index 663b28516d..0000000000 --- a/src/core/unix/SDL_gtk.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2026 Sam Lantinga - - 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); - gchar *(*get_user_cache_dir)(void); - 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); - void (*main_context_unref)(GMainContext *context); - 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_CanUseGtk(void); -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 *ctx); -extern void SDL_UpdateGtk(void); - -#endif // SDL_gtk_h_ diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 9474cacc61..3e0ca45679 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -25,7 +25,6 @@ #include "../../core/linux/SDL_system_theme.h" #include "../../core/linux/SDL_progressbar.h" -#include "../../core/unix/SDL_gtk.h" #include "../../events/SDL_events_c.h" #include "SDL_waylandclipboard.h" @@ -1503,6 +1502,69 @@ static int SDLCALL LibdecorNewInThread(void *data) } #endif +#ifndef HAVE_GETRESUID +// Non-POSIX, but Linux and some BSDs have it. +// To reduce the number of code paths, if getresuid() isn't available at +// compile-time, we behave as though it existed but failed at runtime. +static inline int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { + errno = ENOSYS; + return -1; +} +#endif + +#ifndef HAVE_GETRESGID +// Same as getresuid() but for the primary group +static inline int getresgid(uid_t *ruid, uid_t *euid, uid_t *suid) { + errno = ENOSYS; + return -1; +} +#endif + +bool CanUseGtk(void) +{ + // "Real", "effective" and "saved" IDs: see e.g. Linux credentials(7) + uid_t ruid = -1, euid = -1, suid = -1; + gid_t rgid = -1, egid = -1, sgid = -1; + + if (!SDL_GetHintBoolean("SDL_ENABLE_GTK", true)) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to hint"); + return false; + } + + // This is intended to match the check in gtkmain.c, rather than being + // an exhaustive check for having elevated privileges: as a result + // we don't use Linux getauxval() or prctl PR_GET_DUMPABLE, + // BSD issetugid(), or similar OS-specific detection + + if (getresuid(&ruid, &euid, &suid) != 0) { + ruid = suid = getuid(); + euid = geteuid(); + } + + if (getresgid(&rgid, &egid, &sgid) != 0) { + rgid = sgid = getgid(); + egid = getegid(); + } + + // Real ID != effective ID means we are setuid or setgid: + // GTK will refuse to initialize, and instead will call exit(). + if (ruid != euid || rgid != egid) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to setuid/setgid"); + return false; + } + + // Real ID != saved ID means we are setuid or setgid, we previously + // dropped privileges, but we can regain them; this protects against + // accidents but does not protect against arbitrary code execution. + // Again, GTK will refuse to initialize if this is the case. + if (ruid != suid || rgid != sgid) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to saved uid/gid"); + return false; + } + + return true; +} + bool Wayland_LoadLibdecor(SDL_VideoData *data, bool ignore_xdg) { #ifdef HAVE_LIBDECOR_H @@ -1510,7 +1572,7 @@ bool Wayland_LoadLibdecor(SDL_VideoData *data, bool ignore_xdg) return true; // Already loaded! } if (should_use_libdecor(data, ignore_xdg)) { - if (SDL_CanUseGtk()) { + if (CanUseGtk()) { LibdecorNew(data); } else { // Intentionally initialize libdecor in a non-main thread diff --git a/src/video/x11/SDL_x11modes.c b/src/video/x11/SDL_x11modes.c index 2b7ab0ffef..1fbbc86876 100644 --- a/src/video/x11/SDL_x11modes.c +++ b/src/video/x11/SDL_x11modes.c @@ -27,8 +27,6 @@ #include "edid.h" #include "../../events/SDL_displayevents_c.h" -#include "../../core/unix/SDL_gtk.h" - // #define X11MODES_DEBUG /* Timeout and revert mode switches if the timespan has elapsed without the window becoming fullscreen.