Atlas - SDL_gtk.c
Home / ext / SDL / src / core / unix Lines: 1 | Size: 9104 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "SDL_internal.h" 22#include "SDL_gtk.h" 23 24#include <dlfcn.h> 25#include <errno.h> 26#include <unistd.h> 27 28#define SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sym) \ 29 ctx.sub.fn = (void *)SDL_LoadFunction(lib, #sym) 30 31#define SDL_GTK_SYM2(ctx, lib, sub, fn, sym) \ 32 SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sym); \ 33 if (!ctx.sub.fn) { \ 34 return SDL_SetError("Could not load GTK functions"); \ 35 } 36 37#define SDL_GTK_SYM_OPTIONAL(ctx, lib, sub, fn) \ 38 SDL_GTK_SYM2_OPTIONAL(ctx, lib, sub, fn, sub##_##fn) 39 40#define SDL_GTK_SYM(ctx, lib, sub, fn) \ 41 SDL_GTK_SYM2(ctx, lib, sub, fn, sub##_##fn) 42 43#ifdef SDL_PLATFORM_OPENBSD 44#define GDK3_LIB "libgdk-3.so" 45#else 46#define GDK3_LIB "libgdk-3.so.0" 47#endif 48 49#ifdef SDL_PLATFORM_OPENBSD 50#define GTK3_LIB "libgtk-3.so" 51#else 52#define GTK3_LIB "libgtk-3.so.0" 53#endif 54 55// we never link directly to gtk 56static void *libgdk = NULL; 57static void *libgtk = NULL; 58 59static SDL_GtkContext gtk; 60static GMainContext *sdl_main_context; 61 62static gulong signal_connect(gpointer instance, const gchar *detailed_signal, void *c_handler, gpointer data) 63{ 64 return gtk.g.signal_connect_data(instance, detailed_signal, SDL_G_CALLBACK(c_handler), data, NULL, (SDL_GConnectFlags)0); 65} 66 67static void QuitGtk(void) 68{ 69 if (sdl_main_context) { 70 gtk.g.main_context_unref(sdl_main_context); 71 sdl_main_context = NULL; 72 } 73 74 SDL_UnloadObject(libgdk); 75 SDL_UnloadObject(libgtk); 76 77 libgdk = NULL; 78 libgtk = NULL; 79} 80 81static bool IsGtkInit(void) 82{ 83 return libgdk != NULL && libgtk != NULL; 84} 85 86#ifndef HAVE_GETRESUID 87// Non-POSIX, but Linux and some BSDs have it. 88// To reduce the number of code paths, if getresuid() isn't available at 89// compile-time, we behave as though it existed but failed at runtime. 90static inline int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { 91 errno = ENOSYS; 92 return -1; 93} 94#endif 95 96#ifndef HAVE_GETRESGID 97// Same as getresuid() but for the primary group 98static inline int getresgid(uid_t *ruid, uid_t *euid, uid_t *suid) { 99 errno = ENOSYS; 100 return -1; 101} 102#endif 103 104bool SDL_CanUseGtk(void) 105{ 106 // "Real", "effective" and "saved" IDs: see e.g. Linux credentials(7) 107 uid_t ruid = -1, euid = -1, suid = -1; 108 gid_t rgid = -1, egid = -1, sgid = -1; 109 110 if (!SDL_GetHintBoolean("SDL_ENABLE_GTK", true)) { 111 SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to hint"); 112 return false; 113 } 114 115 // This is intended to match the check in gtkmain.c, rather than being 116 // an exhaustive check for having elevated privileges: as a result 117 // we don't use Linux getauxval() or prctl PR_GET_DUMPABLE, 118 // BSD issetugid(), or similar OS-specific detection 119 120 if (getresuid(&ruid, &euid, &suid) != 0) { 121 ruid = suid = getuid(); 122 euid = geteuid(); 123 } 124 125 if (getresgid(&rgid, &egid, &sgid) != 0) { 126 rgid = sgid = getgid(); 127 egid = getegid(); 128 } 129 130 // Real ID != effective ID means we are setuid or setgid: 131 // GTK will refuse to initialize, and instead will call exit(). 132 if (ruid != euid || rgid != egid) { 133 SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to setuid/setgid"); 134 return false; 135 } 136 137 // Real ID != saved ID means we are setuid or setgid, we previously 138 // dropped privileges, but we can regain them; this protects against 139 // accidents but does not protect against arbitrary code execution. 140 // Again, GTK will refuse to initialize if this is the case. 141 if (ruid != suid || rgid != sgid) { 142 SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to saved uid/gid"); 143 return false; 144 } 145 146 return true; 147} 148 149static bool InitGtk(void) 150{ 151 if (!SDL_CanUseGtk()) { 152 return false; 153 } 154 155 if (IsGtkInit()) { 156 return true; 157 } 158 159 // GTK only allows a single version to be loaded into a process at a time, 160 // so if there is one already loaded ensure it is the version we use. 161 void *progress_get_type = dlsym(RTLD_DEFAULT, "gtk_progress_get_type"); 162 void *misc_get_type = dlsym(RTLD_DEFAULT, "gtk_misc_get_type"); 163 if (progress_get_type || misc_get_type) { 164 void *libgtk3 = dlopen(GTK3_LIB, RTLD_NOLOAD | RTLD_LAZY); 165 if (!libgtk3) { 166 QuitGtk(); 167 return SDL_SetError("Could not load GTK-3, another GTK version already present"); 168 } 169 170 dlclose(libgtk3); 171 } 172 173 libgdk = SDL_LoadObject(GDK3_LIB); 174 libgtk = SDL_LoadObject(GTK3_LIB); 175 176 if (!libgdk || !libgtk) { 177 QuitGtk(); 178 return SDL_SetError("Could not load GTK libraries"); 179 } 180 181 SDL_GTK_SYM(gtk, libgtk, gtk, init_check); 182 SDL_GTK_SYM(gtk, libgtk, gtk, menu_new); 183 SDL_GTK_SYM(gtk, libgtk, gtk, separator_menu_item_new); 184 SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_new_with_label); 185 SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_submenu); 186 SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_get_label); 187 SDL_GTK_SYM(gtk, libgtk, gtk, menu_item_set_label); 188 SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_append); 189 SDL_GTK_SYM(gtk, libgtk, gtk, menu_shell_insert); 190 SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_new_with_label); 191 SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_get_active); 192 SDL_GTK_SYM(gtk, libgtk, gtk, check_menu_item_set_active); 193 SDL_GTK_SYM(gtk, libgtk, gtk, widget_show); 194 SDL_GTK_SYM(gtk, libgtk, gtk, widget_destroy); 195 SDL_GTK_SYM(gtk, libgtk, gtk, widget_get_sensitive); 196 SDL_GTK_SYM(gtk, libgtk, gtk, widget_set_sensitive); 197 SDL_GTK_SYM(gtk, libgtk, gtk, settings_get_default); 198 199 SDL_GTK_SYM(gtk, libgdk, g, signal_connect_data); 200 SDL_GTK_SYM(gtk, libgdk, g, mkdtemp); 201 SDL_GTK_SYM(gtk, libgdk, g, get_user_cache_dir); 202 SDL_GTK_SYM(gtk, libgdk, g, object_ref); 203 SDL_GTK_SYM(gtk, libgdk, g, object_ref_sink); 204 SDL_GTK_SYM(gtk, libgdk, g, object_unref); 205 SDL_GTK_SYM(gtk, libgdk, g, object_get); 206 SDL_GTK_SYM(gtk, libgdk, g, signal_handler_disconnect); 207 SDL_GTK_SYM(gtk, libgdk, g, main_context_push_thread_default); 208 SDL_GTK_SYM(gtk, libgdk, g, main_context_pop_thread_default); 209 SDL_GTK_SYM(gtk, libgdk, g, main_context_new); 210 SDL_GTK_SYM(gtk, libgdk, g, main_context_unref); 211 SDL_GTK_SYM(gtk, libgdk, g, main_context_acquire); 212 SDL_GTK_SYM(gtk, libgdk, g, main_context_iteration); 213 214 gtk.g.signal_connect = signal_connect; 215 216 if (gtk.gtk.init_check(NULL, NULL) == GTK_FALSE) { 217 QuitGtk(); 218 return SDL_SetError("Could not init GTK"); 219 } 220 221 sdl_main_context = gtk.g.main_context_new(); 222 if (!sdl_main_context) { 223 QuitGtk(); 224 return SDL_SetError("Could not create GTK context"); 225 } 226 227 if (!gtk.g.main_context_acquire(sdl_main_context)) { 228 QuitGtk(); 229 return SDL_SetError("Could not acquire GTK context"); 230 } 231 232 return true; 233} 234 235static SDL_InitState gtk_init; 236 237bool SDL_Gtk_Init(void) 238{ 239 static bool is_gtk_available = true; 240 241 if (!is_gtk_available) { 242 return false; // don't keep trying if this fails. 243 } 244 245 if (SDL_ShouldInit(>k_init)) { 246 if (InitGtk()) { 247 SDL_SetInitialized(>k_init, true); 248 } else { 249 is_gtk_available = false; 250 SDL_SetInitialized(>k_init, true); 251 SDL_Gtk_Quit(); 252 } 253 } 254 255 return IsGtkInit(); 256} 257 258void SDL_Gtk_Quit(void) 259{ 260 if (!SDL_ShouldQuit(>k_init)) { 261 return; 262 } 263 264 QuitGtk(); 265 SDL_zero(gtk); 266 267 SDL_SetInitialized(>k_init, false); 268} 269 270SDL_GtkContext *SDL_Gtk_GetContext(void) 271{ 272 return IsGtkInit() ? >k : NULL; 273} 274 275SDL_GtkContext *SDL_Gtk_EnterContext(void) 276{ 277 SDL_Gtk_Init(); 278 279 if (IsGtkInit()) { 280 gtk.g.main_context_push_thread_default(sdl_main_context); 281 return >k; 282 } 283 284 return NULL; 285} 286 287void SDL_Gtk_ExitContext(SDL_GtkContext *ctx) 288{ 289 if (ctx) { 290 ctx->g.main_context_pop_thread_default(sdl_main_context); 291 } 292} 293 294void SDL_UpdateGtk(void) 295{ 296 if (IsGtkInit()) { 297 gtk.g.main_context_iteration(sdl_main_context, GTK_FALSE); 298 gtk.g.main_context_iteration(NULL, GTK_FALSE); 299 } 300} 301[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.