|
- /*
- * Carla Bridge UI
- * Copyright (C) 2011-2021 Filipe Coelho <falktx@falktx.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * For a full copy of the GNU General Public License see the doc/GPL.txt file.
- */
-
- #include "CarlaBridgeFormat.hpp"
- #include "CarlaBridgeToolkit.hpp"
- #include "CarlaLibUtils.hpp"
-
- #ifdef HAVE_X11
- # include <X11/Xlib.h>
- #endif
-
- struct GtkHandle;
-
- enum GtkWidgetType {
- GTK_WINDOW_TOPLEVEL
- };
-
- typedef ulong (*gsym_signal_connect_data)(void* instance,
- const char* detailed_signal,
- void (*c_handler)(GtkHandle*, void* data),
- void* data,
- void* destroy_data,
- int connect_flags);
- typedef uint (*gsym_timeout_add)(uint interval, int (*function)(void* user_data), void* data);
- typedef void (*gtksym_init)(int* argc, char*** argv);
- typedef void (*gtksym_main)(void);
- typedef uint (*gtksym_main_level)(void);
- typedef void (*gtksym_main_quit)(void);
- typedef void (*gtksym_container_add)(GtkHandle* container, GtkHandle* widget);
- typedef void (*gtksym_widget_destroy)(GtkHandle* widget);
- typedef void (*gtksym_widget_hide)(GtkHandle* widget);
- typedef void (*gtksym_widget_show_all)(GtkHandle* widget);
- typedef GtkHandle* (*gtksym_window_new)(GtkWidgetType type);
- typedef void (*gtksym_window_get_position)(GtkHandle* window, int* root_x, int* root_y);
- typedef void (*gtksym_window_get_size)(GtkHandle* window, int* width, int* height);
- typedef void (*gtksym_window_resize)(GtkHandle* window, int width, int height);
- typedef void (*gtksym_window_set_resizable)(GtkHandle* window, int resizable);
- typedef void (*gtksym_window_set_title)(GtkHandle* window, const char* title);
- #ifdef HAVE_X11
- typedef GtkHandle* (*gtksym_widget_get_window)(GtkHandle* widget);
- # ifdef BRIDGE_GTK3
- typedef GtkHandle* (*gdksym_window_get_display)(GtkHandle* window);
- typedef Display* (*gdksym_x11_display_get_xdisplay)(GtkHandle* display);
- typedef Window (*gdksym_x11_window_get_xid)(GtkHandle* window);
- # else
- typedef Display* (*gdksym_x11_drawable_get_xdisplay)(GtkHandle* drawable);
- typedef XID (*gdksym_x11_drawable_get_xid)(GtkHandle* drawable);
- # endif
- #endif
-
- CARLA_BRIDGE_UI_START_NAMESPACE
-
- // -------------------------------------------------------------------------
-
- struct GtkLoader {
- lib_t lib;
- gsym_signal_connect_data signal_connect_data;
- gsym_timeout_add timeout_add;
- gtksym_init init;
- gtksym_main main;
- gtksym_main_level main_level;
- gtksym_main_quit main_quit;
- gtksym_container_add container_add;
- gtksym_widget_destroy widget_destroy;
- gtksym_widget_hide widget_hide;
- gtksym_widget_show_all widget_show_all;
- gtksym_window_new window_new;
- gtksym_window_get_position window_get_position;
- gtksym_window_get_size window_get_size;
- gtksym_window_resize window_resize;
- gtksym_window_set_resizable window_set_resizable;
- gtksym_window_set_title window_set_title;
- bool ok;
-
- #ifdef HAVE_X11
- gtksym_widget_get_window widget_get_window;
- # ifdef BRIDGE_GTK3
- gdksym_window_get_display window_get_display;
- gdksym_x11_display_get_xdisplay x11_display_get_xdisplay;
- gdksym_x11_window_get_xid x11_window_get_xid;
- # else
- gdksym_x11_drawable_get_xdisplay x11_drawable_get_xdisplay;
- gdksym_x11_drawable_get_xid x11_drawable_get_xid;
- # endif
- #endif
-
- GtkLoader()
- : lib(nullptr),
- signal_connect_data(nullptr),
- timeout_add(nullptr),
- init(nullptr),
- main(nullptr),
- main_level(nullptr),
- main_quit(nullptr),
- container_add(nullptr),
- widget_destroy(nullptr),
- widget_hide(nullptr),
- widget_show_all(nullptr),
- window_new(nullptr),
- window_get_position(nullptr),
- window_get_size(nullptr),
- window_resize(nullptr),
- window_set_resizable(nullptr),
- window_set_title(nullptr),
- ok(false)
- #ifdef HAVE_X11
- , widget_get_window(nullptr),
- # ifdef BRIDGE_GTK3
- window_get_display(nullptr),
- x11_display_get_xdisplay(nullptr),
- x11_window_get_xid(nullptr)
- # else
- x11_drawable_get_xdisplay(nullptr),
- x11_drawable_get_xid(nullptr)
- # endif
- #endif
- {
- const char* filename;
- const char* const filenames[] = {
- #ifdef BRIDGE_GTK3
- # if defined(CARLA_OS_MAC)
- "libgtk-3.0.dylib",
- # else
- "libgtk-3.so.0",
- # endif
- #else
- # if defined(CARLA_OS_MAC)
- "libgtk-quartz-2.0.dylib",
- "libgtk-x11-2.0.dylib",
- "/opt/homebrew/opt/gtk+/lib/libgtk-quartz-2.0.0.dylib",
- "/opt/local/lib/libgtk-quartz-2.0.dylib",
- "/opt/local/lib/libgtk-x11-2.0.dylib",
- # else
- "libgtk-x11-2.0.so.0",
- # endif
- #endif
- };
-
- for (size_t i=0; i<sizeof(filenames)/sizeof(filenames[0]); ++i)
- {
- filename = filenames[i];
- if ((lib = lib_open(filename, true)) != nullptr)
- break;
- }
-
- if (lib == nullptr)
- {
- fprintf(stderr, "Failed to load Gtk, reason:\n%s\n", lib_error(filename));
- return;
- }
- else
- {
- fprintf(stdout, "%s loaded successfully!\n", filename);
- }
-
- #define G_LIB_SYMBOL(NAME) \
- NAME = lib_symbol<gsym_##NAME>(lib, "g_" #NAME); \
- CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
-
- #define GTK_LIB_SYMBOL(NAME) \
- NAME = lib_symbol<gtksym_##NAME>(lib, "gtk_" #NAME); \
- CARLA_SAFE_ASSERT_RETURN(NAME != nullptr,);
-
- #define GDK_LIB_SYMBOL(NAME) \
- NAME = lib_symbol<gdksym_##NAME>(lib, "gdk_" #NAME); \
- CARLA_SAFE_ASSERT(NAME != nullptr);
-
- G_LIB_SYMBOL(signal_connect_data)
- G_LIB_SYMBOL(timeout_add)
-
- GTK_LIB_SYMBOL(init)
- GTK_LIB_SYMBOL(main)
- GTK_LIB_SYMBOL(main_level)
- GTK_LIB_SYMBOL(main_quit)
- GTK_LIB_SYMBOL(container_add)
- GTK_LIB_SYMBOL(widget_destroy)
- GTK_LIB_SYMBOL(widget_hide)
- GTK_LIB_SYMBOL(widget_show_all)
- GTK_LIB_SYMBOL(window_new)
- GTK_LIB_SYMBOL(window_get_position)
- GTK_LIB_SYMBOL(window_get_size)
- GTK_LIB_SYMBOL(window_resize)
- GTK_LIB_SYMBOL(window_set_resizable)
- GTK_LIB_SYMBOL(window_set_title)
-
- ok = true;
-
- #ifdef HAVE_X11
- GTK_LIB_SYMBOL(widget_get_window)
- # ifdef BRIDGE_GTK3
- GDK_LIB_SYMBOL(window_get_display)
- GDK_LIB_SYMBOL(x11_display_get_xdisplay)
- GDK_LIB_SYMBOL(x11_window_get_xid)
- # else
- GDK_LIB_SYMBOL(x11_drawable_get_xdisplay)
- GDK_LIB_SYMBOL(x11_drawable_get_xid)
- # endif
- #endif
-
- #undef GDK_LIB_SYMBOL
- #undef GTK_LIB_SYMBOL
- }
-
- ~GtkLoader()
- {
- if (lib != nullptr)
- lib_close(lib);
- }
-
- void main_quit_if_needed()
- {
- if (main_level() != 0)
- main_quit();
- }
-
- CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GtkLoader)
- };
-
- // -------------------------------------------------------------------------
-
- static const bool gHideShowTesting = std::getenv("CARLA_UI_TESTING") != nullptr;
-
- // -------------------------------------------------------------------------
-
- class CarlaBridgeToolkitGtk : public CarlaBridgeToolkit
- {
- public:
- CarlaBridgeToolkitGtk(CarlaBridgeFormat* const format)
- : CarlaBridgeToolkit(format),
- gtk(),
- fNeedsShow(false),
- fWindow(nullptr),
- fLastX(0),
- fLastY(0),
- fLastWidth(0),
- fLastHeight(0)
- {
- carla_debug("CarlaBridgeToolkitGtk::CarlaBridgeToolkitGtk(%p)", format);
- }
-
- ~CarlaBridgeToolkitGtk() override
- {
- CARLA_SAFE_ASSERT(fWindow == nullptr);
- carla_debug("CarlaBridgeToolkitGtk::~CarlaBridgeToolkitGtk()");
- }
-
- bool init(const int /*argc*/, const char** /*argv[]*/) override
- {
- CARLA_SAFE_ASSERT_RETURN(fWindow == nullptr, false);
- carla_debug("CarlaBridgeToolkitGtk::init()");
-
- if (! gtk.ok)
- return false;
-
- static int gargc = 0;
- static char** gargv = nullptr;
- gtk.init(&gargc, &gargv);
-
- fWindow = gtk.window_new(GTK_WINDOW_TOPLEVEL);
- CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr, false);
-
- gtk.window_resize(fWindow, 30, 30);
- gtk.widget_hide(fWindow);
-
- return true;
- }
-
- void exec(const bool showUI) override
- {
- CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
- CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
- carla_debug("CarlaBridgeToolkitGtk::exec(%s)", bool2str(showUI));
-
- const CarlaBridgeFormat::Options& options(fPlugin->getOptions());
-
- GtkHandle* const widget((GtkHandle*)fPlugin->getWidget());
- CARLA_SAFE_ASSERT_RETURN(widget != nullptr,);
-
- gtk.container_add(fWindow, widget);
- gtk.window_set_resizable(fWindow, options.isResizable);
- gtk.window_set_title(fWindow, options.windowTitle.buffer());
-
- if (showUI || fNeedsShow)
- {
- show();
- fNeedsShow = false;
- }
-
- gtk.timeout_add(30, gtk_ui_timeout, this);
- gtk.signal_connect_data(fWindow, "destroy", gtk_ui_destroy, this, nullptr, 0);
- gtk.signal_connect_data(fWindow, "realize", gtk_ui_realize, this, nullptr, 0);
-
- // First idle
- handleTimeout();
-
- // Main loop
- gtk.main();
- }
-
- void quit() override
- {
- carla_debug("CarlaBridgeToolkitGtk::quit()");
-
- if (fWindow != nullptr)
- {
- gtk.widget_destroy(fWindow);
- fWindow = nullptr;
-
- gtk.main_quit_if_needed();
- }
- }
-
- void show() override
- {
- carla_debug("CarlaBridgeToolkitGtk::show()");
-
- fNeedsShow = true;
-
- if (fWindow != nullptr)
- gtk.widget_show_all(fWindow);
- }
-
- void focus() override
- {
- carla_debug("CarlaBridgeToolkitGtk::focus()");
- }
-
- void hide() override
- {
- carla_debug("CarlaBridgeToolkitGtk::hide()");
-
- fNeedsShow = false;
-
- if (fWindow != nullptr)
- gtk.widget_hide(fWindow);
- }
-
- void setChildWindow(void* const) override {}
-
- void setSize(const uint width, const uint height) override
- {
- CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
- carla_debug("CarlaBridgeToolkitGtk::resize(%i, %i)", width, height);
-
- gtk.window_resize(fWindow, static_cast<int>(width), static_cast<int>(height));
- }
-
- void setTitle(const char* const title) override
- {
- CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
- carla_debug("CarlaBridgeToolkitGtk::setTitle(\"%s\")", title);
-
- gtk.window_set_title(fWindow, title);
- }
-
- // ---------------------------------------------------------------------
-
- protected:
- GtkLoader gtk;
- bool fNeedsShow;
- GtkHandle* fWindow;
-
- int fLastX;
- int fLastY;
- int fLastWidth;
- int fLastHeight;
-
- void handleDestroy()
- {
- carla_debug("CarlaBridgeToolkitGtk::handleDestroy()");
-
- fWindow = nullptr;
- gtk.main_quit_if_needed();
- }
-
- void handleRealize()
- {
- carla_debug("CarlaBridgeToolkitGtk::handleRealize()");
-
- #ifdef HAVE_X11
- const CarlaBridgeFormat::Options& options(fPlugin->getOptions());
-
- if (options.transientWindowId != 0)
- setTransient(options.transientWindowId);
- #endif
- }
-
- int handleTimeout()
- {
- if (fWindow != nullptr)
- {
- gtk.window_get_position(fWindow, &fLastX, &fLastY);
- gtk.window_get_size(fWindow, &fLastWidth, &fLastHeight);
- }
-
- if (fPlugin->isPipeRunning())
- fPlugin->idlePipe();
-
- fPlugin->idleUI();
-
- if (gHideShowTesting)
- {
- static int counter = 0;
- ++counter;
-
- if (counter == 100)
- {
- hide();
- }
- else if (counter == 200)
- {
- show();
- counter = 0;
- }
- }
-
- return 1;
- }
-
- #ifdef HAVE_X11
- void setTransient(const uintptr_t winId)
- {
- CARLA_SAFE_ASSERT_RETURN(fWindow != nullptr,);
- carla_debug("CarlaBridgeToolkitGtk::setTransient(0x" P_UINTPTR ")", winId);
-
- if (gtk.widget_get_window == nullptr)
- return;
- # ifdef BRIDGE_GTK3
- if (gtk.window_get_display == nullptr)
- return;
- if (gtk.x11_display_get_xdisplay == nullptr)
- return;
- if (gtk.x11_window_get_xid == nullptr)
- return;
- # else
- if (gtk.x11_drawable_get_xdisplay == nullptr)
- return;
- if (gtk.x11_drawable_get_xid == nullptr)
- return;
- # endif
-
- GtkHandle* const gdkWindow = gtk.widget_get_window(fWindow);
- CARLA_SAFE_ASSERT_RETURN(gdkWindow != nullptr,);
-
- # ifdef BRIDGE_GTK3
- GtkHandle* const gdkDisplay = gtk.window_get_display(gdkWindow);
- CARLA_SAFE_ASSERT_RETURN(gdkDisplay != nullptr,);
-
- ::Display* const display = gtk.x11_display_get_xdisplay(gdkDisplay);
- CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
-
- const ::XID xid = gtk.x11_window_get_xid(gdkWindow);
- CARLA_SAFE_ASSERT_RETURN(xid != 0,);
- # else
- ::Display* const display = gtk.x11_drawable_get_xdisplay((GtkHandle*)gdkWindow);
- CARLA_SAFE_ASSERT_RETURN(display != nullptr,);
-
- const ::XID xid = gtk.x11_drawable_get_xid((GtkHandle*)gdkWindow);
- CARLA_SAFE_ASSERT_RETURN(xid != 0,);
- # endif
-
- XSetTransientForHint(display, xid, static_cast< ::Window>(winId));
- }
- #endif
-
- // ---------------------------------------------------------------------
-
- private:
- static void gtk_ui_destroy(GtkHandle*, void* data)
- {
- CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
-
- ((CarlaBridgeToolkitGtk*)data)->handleDestroy();
- }
-
- static void gtk_ui_realize(GtkHandle*, void* data)
- {
- CARLA_SAFE_ASSERT_RETURN(data != nullptr,);
-
- ((CarlaBridgeToolkitGtk*)data)->handleRealize();
- }
-
- static int gtk_ui_timeout(void* data)
- {
- CARLA_SAFE_ASSERT_RETURN(data != nullptr, false);
-
- return ((CarlaBridgeToolkitGtk*)data)->handleTimeout();
- }
-
- CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgeToolkitGtk)
- };
-
- // -------------------------------------------------------------------------
-
- CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeFormat* const format)
- {
- return new CarlaBridgeToolkitGtk(format);
- }
-
- // -------------------------------------------------------------------------
-
- CARLA_BRIDGE_UI_END_NAMESPACE
|