#include "SDL_internal.h"
#include "SDL_gtk.h"
#include <dlfcn.h>
#include <errno.h>
#include <unistd.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) \
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
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()
{
return libgdk != NULL && libgtk != NULL;
}
#ifndef HAVE_GETRESUID
static inline int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) {
errno = ENOSYS;
return -1;
}
#endif
#ifndef HAVE_GETRESGID
static inline int getresgid(uid_t *ruid, uid_t *euid, uid_t *suid) {
errno = ENOSYS;
return -1;
}
#endif
bool SDL_CanUseGtk(void)
{
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;
}
if (getresuid(&ruid, &euid, &suid) != 0) {
ruid = suid = getuid();
euid = geteuid();
}
if (getresgid(&rgid, &egid, &sgid) != 0) {
rgid = sgid = getgid();
egid = getegid();
}
if (ruid != euid || rgid != egid) {
SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to setuid/setgid");
return false;
}
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;
}
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; }
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);
}
}