summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gtk2_in_qt4.cpp77
-rw-r--r--src/instance.c111
-rw-r--r--src/qt4_in_gtk2.cpp146
-rw-r--r--src/suil_internal.h17
-rw-r--r--wscript30
5 files changed, 364 insertions, 17 deletions
diff --git a/src/gtk2_in_qt4.cpp b/src/gtk2_in_qt4.cpp
new file mode 100644
index 0000000..837cbac
--- /dev/null
+++ b/src/gtk2_in_qt4.cpp
@@ -0,0 +1,77 @@
+/* Suil, an LV2 plugin UI hosting library.
+ * Copyright 2011 David Robillard <d@drobilla.net>
+ *
+ * Suil is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Suil 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 Lesser General Public
+ * License for details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QX11EmbedContainer>
+#undef signals
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "suil_internal.h"
+
+extern "C" {
+
+SUIL_API
+int
+suil_wrap_init(const char* host_type_uri,
+ const char* ui_type_uri,
+ const LV2_Feature* const* features)
+{
+ return 0;
+}
+
+static void
+on_window_destroy(GtkWidget* gtk_window,
+ gpointer* user_data)
+{
+ //SuilInstance instance = (SuilInstance)user_data;
+ // TODO: Notify host via callback
+ printf("LV2 UI window destroyed\n");
+}
+
+
+/** Dynamic module entry point. */
+SUIL_API
+int
+suil_instance_wrap(SuilInstance instance,
+ const char* host_type_uri,
+ const char* ui_type_uri)
+{
+ GtkWidget* gtk_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_resizable(GTK_WINDOW(gtk_window), TRUE);
+ gtk_window_set_title(GTK_WINDOW(gtk_window), "LV2 Plugin UI");
+
+ gtk_container_add(GTK_CONTAINER(gtk_window),
+ (GtkWidget*)instance->ui_widget);
+
+ g_signal_connect(G_OBJECT(gtk_window),
+ "destroy",
+ G_CALLBACK(on_window_destroy),
+ instance);
+
+ gtk_widget_show_all(gtk_window);
+
+ GdkWindow* gdk_window = gtk_widget_get_window(gtk_window);
+ QX11EmbedContainer* wrapper = new QX11EmbedContainer();
+ wrapper->embedClient(GDK_WINDOW_XID(gdk_window));
+
+ instance->host_widget = wrapper;
+
+ return 0;
+}
+
+} // extern "C"
diff --git a/src/instance.c b/src/instance.c
index 74d0948..a56a11f 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -25,14 +25,78 @@
#include <dlfcn.h>
+#include "suil-config.h"
#include "suil_internal.h"
+#define NS_UI "http://lv2plug.in/ns/extensions/ui#"
+#define GTK2_UI_URI NS_UI "GtkUI"
+#define QT4_UI_URI NS_UI "Qt4UI"
+
SUIL_API
bool
suil_ui_type_supported(const char* host_type_uri,
const char* ui_type_uri)
{
- return !strcmp(ui_type_uri, "http://lv2plug.in/ns/extensions/ui#GtkUI");
+ return (!strcmp(host_type_uri, GTK2_UI_URI)
+ || !strcmp(host_type_uri, QT4_UI_URI))
+ && (!strcmp(ui_type_uri, GTK2_UI_URI)
+ || !strcmp(ui_type_uri, QT4_UI_URI));
+}
+
+struct _SuilModule {
+ SuilWrapInitFunc init;
+ SuilWrapFunc wrap;
+};
+
+typedef struct _SuilModule* SuilModule;
+
+static SuilModule
+get_wrap_module(const char* host_type_uri,
+ const char* ui_type_uri)
+{
+ if (!strcmp(host_type_uri, ui_type_uri)) {
+ return NULL;
+ }
+
+ const char* module_name = NULL;
+ if (!strcmp(host_type_uri, QT4_UI_URI)
+ && !strcmp(ui_type_uri, GTK2_UI_URI)) {
+ module_name = "libsuil_gtk2_in_qt4";
+ } else if (!strcmp(host_type_uri, GTK2_UI_URI)
+ && !strcmp(ui_type_uri, QT4_UI_URI)) {
+ module_name = "libsuil_qt4_in_gtk2";
+ }
+
+ if (!module_name) {
+ SUIL_ERRORF("Unable to wrap UI type <%s> as type <%s>\n",
+ ui_type_uri, host_type_uri);
+ return NULL;
+ }
+
+ const size_t path_len = strlen(SUIL_MODULE_DIR)
+ + strlen(module_name)
+ + strlen(SUIL_MODULE_EXT)
+ + 2;
+
+ char* const path = calloc(path_len, 1);
+ snprintf(path, path_len, "%s%s%s%s",
+ SUIL_MODULE_DIR, SUIL_DIR_SEP, module_name, SUIL_MODULE_EXT);
+
+ // Open wrap module
+ dlerror();
+ void* lib = dlopen(path, RTLD_NOW);
+ if (!lib) {
+ SUIL_ERRORF("Unable to open wrap module %s\n", path);
+ return NULL;
+ }
+
+ SuilModule module = (SuilModule)malloc(sizeof(struct _SuilModule));
+ module->init = (SuilWrapInitFunc)suil_dlfunc(lib, "suil_wrap_init");
+ module->wrap = (SuilWrapFunc)suil_dlfunc(lib, "suil_wrap");
+
+ free(path);
+
+ return module;
}
SUIL_API
@@ -92,30 +156,33 @@ suil_instance_new(SuilUIs uis,
return NULL;
}
- // Create empty local features array if necessary
- const bool local_features = (features == NULL);
- if (local_features) {
- features = malloc(sizeof(LV2_Feature));
- ((LV2_Feature**)features)[0] = NULL;
+ // Use empty local features array if necessary
+ const LV2_Feature* local_features[1];
+ local_features[0] = NULL;
+ if (!features) {
+ features = (const LV2_Feature* const*)&local_features;
+ }
+
+ SuilModule module = get_wrap_module(type_uri, ui->type_uri);
+ if (module) {
+ module->init(type_uri, ui->type_uri, features);
}
// Instantiate UI
struct _SuilInstance* instance = malloc(sizeof(struct _SuilInstance));
- instance->lib_handle = lib;
- instance->descriptor = descriptor;
- instance->handle = descriptor->instantiate(
+ instance->lib_handle = lib;
+ instance->descriptor = descriptor;
+ instance->host_widget = NULL;
+ instance->ui_widget = NULL;
+ instance->handle = descriptor->instantiate(
descriptor,
uis->plugin_uri,
ui->bundle_path,
write_function,
controller,
- &instance->widget,
+ &instance->ui_widget,
features);
- if (local_features) {
- free((LV2_Feature**)features);
- }
-
// Failed to find or instantiate UI
if (!instance || !instance->handle) {
SUIL_ERRORF("Failed to instantiate UI <%s> in %s\n",
@@ -126,13 +193,25 @@ suil_instance_new(SuilUIs uis,
}
// Got a handle, but failed to create a widget (buggy UI)
- if (!instance->widget) {
+ if (!instance->ui_widget) {
SUIL_ERRORF("Widget creation failed for UI <%s> in %s\n",
ui->uri, ui->binary_path);
suil_instance_free(instance);
return NULL;
}
+ if (module) {
+ if (module->wrap(type_uri, ui->type_uri, instance)) {
+ SUIL_ERRORF("Failed to wrap UI <%s> in type <%s>\n",
+ ui->uri, type_uri);
+ suil_instance_free(instance);
+ return NULL;
+ }
+ } else {
+ instance->host_widget = instance->ui_widget;
+ }
+
+
return instance;
}
@@ -151,7 +230,7 @@ SUIL_API
LV2UI_Widget
suil_instance_get_widget(SuilInstance instance)
{
- return instance->widget;
+ return instance->host_widget;
}
SUIL_API
diff --git a/src/qt4_in_gtk2.cpp b/src/qt4_in_gtk2.cpp
new file mode 100644
index 0000000..bdacbbd
--- /dev/null
+++ b/src/qt4_in_gtk2.cpp
@@ -0,0 +1,146 @@
+/* Suil, an LV2 plugin UI hosting library.
+ * Copyright 2011 David Robillard <d@drobilla.net>
+ *
+ * Suil is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Suil 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 Lesser General Public
+ * License for details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtk/gtk.h>
+
+#include <QApplication>
+#include <QX11EmbedWidget>
+#include <QVBoxLayout>
+
+#include "suil_internal.h"
+
+extern "C" {
+
+static int argc = 0;
+static QApplication application(argc, NULL, true);
+
+SUIL_API
+int
+suil_wrap_init(const char* host_type_uri,
+ const char* ui_type_uri,
+ const LV2_Feature* const* features)
+{
+ return 0;
+}
+
+#define WRAP_TYPE_WIDGET (wrap_widget_get_type())
+#define WRAP_WIDGET(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), WRAP_TYPE_WIDGET, WrapWidget))
+#define WRAP_WIDGET_GET_PRIVATE(obj) \
+ G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+ WRAP_TYPE_WIDGET, \
+ WrapWidgetPrivate)
+
+typedef struct _WrapWidget WrapWidget;
+typedef struct _WrapWidgetClass WrapWidgetClass;
+typedef struct _WrapWidgetPrivate WrapWidgetPrivate;
+
+struct _WrapWidget {
+ GtkSocket parent_instance;
+
+ WrapWidgetPrivate* priv;
+};
+
+struct _WrapWidgetClass {
+ GtkSocketClass parent_class;
+};
+
+GType wrap_widget_get_type(void); // Accessor for GTK_TYPE_WIDGET
+
+struct _WrapWidgetPrivate {
+ QX11EmbedWidget* qembed;
+ SuilInstance instance;
+};
+
+G_DEFINE_TYPE(WrapWidget, wrap_widget, GTK_TYPE_SOCKET);
+
+static void
+wrap_widget_dispose(GObject* gobject)
+{
+ WrapWidget* const self = WRAP_WIDGET(gobject);
+ WrapWidgetPrivate* const priv = WRAP_WIDGET_GET_PRIVATE(self);
+
+ if (priv->qembed) {
+ QWidget* const qwidget = (QWidget*)priv->instance->ui_widget;
+ qwidget->setParent(NULL);
+
+ delete self->priv->qembed;
+ self->priv->qembed = NULL;
+ }
+
+ G_OBJECT_CLASS(wrap_widget_parent_class)->dispose(gobject);
+}
+
+static void
+wrap_widget_class_init(WrapWidgetClass* klass)
+{
+ GObjectClass* const gobject_class = G_OBJECT_CLASS(klass);
+
+ gobject_class->dispose = wrap_widget_dispose;
+
+ g_type_class_add_private(klass, sizeof(WrapWidgetPrivate));
+}
+
+static void
+wrap_widget_init(WrapWidget* self)
+{
+ WrapWidgetPrivate* const priv = WRAP_WIDGET_GET_PRIVATE(self);
+ priv->qembed = NULL;
+ priv->instance = NULL;
+ self->priv = priv;
+}
+
+static void
+wrap_widget_realize(GtkWidget* w, gpointer data)
+{
+ WrapWidget* const wrap = WRAP_WIDGET(w);
+ GtkSocket* const s = GTK_SOCKET(w);
+ WrapWidgetPrivate* const priv = wrap->priv;
+
+ gtk_socket_add_id(s, priv->qembed->winId());
+ priv->qembed->show();
+}
+
+SUIL_API
+int
+suil_wrap(const char* host_type_uri,
+ const char* ui_type_uri,
+ SuilInstance instance)
+{
+ WrapWidget* const wrap = WRAP_WIDGET(g_object_new(WRAP_TYPE_WIDGET, NULL));
+
+ WrapWidgetPrivate* const priv = wrap->priv;
+ priv->qembed = new QX11EmbedWidget();
+ priv->instance = instance;
+
+ QWidget* qwidget = (QWidget*)instance->ui_widget;
+ QVBoxLayout* layout = new QVBoxLayout(priv->qembed);
+ layout->addWidget(qwidget);
+
+ qwidget->setParent(priv->qembed);
+
+ g_signal_connect_after(G_OBJECT(wrap),
+ "realize",
+ G_CALLBACK(wrap_widget_realize),
+ NULL);
+
+ instance->host_widget = GTK_WIDGET(wrap);
+
+ return 0;
+}
+
+} // extern "C"
diff --git a/src/suil_internal.h b/src/suil_internal.h
index ed7ebd2..23de999 100644
--- a/src/suil_internal.h
+++ b/src/suil_internal.h
@@ -47,7 +47,8 @@ struct _SuilInstance {
void* lib_handle;
const LV2UI_Descriptor* descriptor;
LV2UI_Handle handle;
- LV2UI_Widget widget;
+ LV2UI_Widget ui_widget;
+ LV2UI_Widget host_widget;
};
/** Get the UI with the given URI. */
@@ -60,6 +61,20 @@ SuilUI
suil_uis_get_best(SuilUIs uis,
const char* type_uri);
+/** Type of a module's suil_wrap_init function.
+ * This initialisation function must be called before instantiating any
+ * UI that will need to be wrapped by this wrapper (e.g. it will perform any
+ * initialisation required to create a widget for the given toolkit).
+ */
+typedef int (*SuilWrapInitFunc)(const char* host_type_uri,
+ const char* ui_type_uri,
+ const LV2_Feature* const* features);
+
+
+typedef int (*SuilWrapFunc)(const char* host_type_uri,
+ const char* ui_type_uri,
+ SuilInstance instance);
+
typedef void (*SuilVoidFunc)();
/** dlsym wrapper to return a function pointer (without annoying warning) */
diff --git a/wscript b/wscript
index e6c61a3..c62918b 100644
--- a/wscript
+++ b/wscript
@@ -39,9 +39,24 @@ def configure(conf):
autowaf.check_header(conf, 'lv2/lv2plug.in/ns/extensions/ui/ui.h')
+ autowaf.check_pkg(conf, 'gtk+-2.0', uselib_store='GTK2',
+ atleast_version='2.0.0', mandatory=False)
+
+ autowaf.check_pkg(conf, 'QtGui', uselib_store='QT4',
+ atleast_version='4.0.0', mandatory=False)
+
autowaf.define(conf, 'SUIL_VERSION', SUIL_VERSION)
+ autowaf.define(conf, 'SUIL_MODULE_DIR', conf.env['LIBDIR'] + '/suil')
+ autowaf.define(conf, 'SUIL_DIR_SEP', '/')
+ autowaf.define(conf, 'SUIL_MODULE_EXT', '.so')
conf.write_config_header('suil-config.h', remove=False)
+ autowaf.display_msg(conf, "Gtk2 Support",
+ bool(conf.env['HAVE_GTK2']))
+
+ autowaf.display_msg(conf, "Qt4 Support",
+ bool(conf.env['HAVE_QT4']))
+
print
def build(bld):
@@ -62,6 +77,21 @@ def build(bld):
obj.install_path = '${LIBDIR}'
obj.cflags = [ '-fvisibility=hidden', '-DSUIL_SHARED', '-DSUIL_INTERNAL' ]
+ if bld.env['HAVE_GTK2'] and bld.env['HAVE_QT4']:
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = 'src/gtk2_in_qt4.cpp'
+ obj.target = 'suil_gtk2_in_qt4'
+ obj.install_path = '${LIBDIR}/suil'
+ obj.cflags = [ '-fvisibility=hidden', '-DSUIL_SHARED', '-DSUIL_INTERNAL' ]
+ autowaf.use_lib(bld, obj, 'GTK2 QT4')
+
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = 'src/qt4_in_gtk2.cpp'
+ obj.target = 'suil_qt4_in_gtk2'
+ obj.install_path = '${LIBDIR}/suil'
+ obj.cflags = [ '-fvisibility=hidden', '-DSUIL_SHARED', '-DSUIL_INTERNAL' ]
+ autowaf.use_lib(bld, obj, 'GTK2 QT4')
+
# Documentation
autowaf.build_dox(bld, 'SUIL', SUIL_VERSION, top, out)