- Add basic support for Qt5 GUI hosts with two new wrappers:
  libsuil_gtk2_in_qt5 and libsuil_x11_in_qt5. (EXPERIMENTAL)

  Signed-off-by: rncbc aka. Rui Nuno Capela <rncbc@rncbc.org>


Index: src/gtk2_in_qt5.cpp
===================================================================
--- src/gtk2_in_qt5.cpp	(revision 0)
+++ src/gtk2_in_qt5.cpp	(working copy)
@@ -0,0 +1,173 @@
+/*
+  Copyright 2011-2012 David Robillard <http://drobilla.net>
+  Copyright 2015 rncbc aka. Rui Nuno Capela <rncbc@rncbc.org>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <QWindow>
+#include <QWidget>
+
+#include <QVBoxLayout>
+
+#undef signals
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "./suil_config.h"
+#include "./suil_internal.h"
+
+extern "C" {
+
+typedef struct _SuilGtk2InQt5Wrapper SuilGtk2InQt5Wrapper;
+
+struct _SuilGtk2InQt5Wrapper {
+	QWidget*   host_widget;
+	QWidget*   parent;
+	GtkWidget* plug;
+};
+
+static void
+on_size_request(GtkWidget*      widget,
+                GtkRequisition* requisition,
+                gpointer        user_data)
+{
+	QWidget* const wrap = (QWidget*)user_data;
+	wrap->setMinimumSize(requisition->width, requisition->height);
+}
+
+static void
+on_size_allocate(GtkWidget*    widget,
+                 GdkRectangle* allocation,
+                 gpointer      user_data)
+{
+	QWidget* const wrap = (QWidget*)user_data;
+	wrap->resize(allocation->width, allocation->height);
+}
+
+static void
+wrapper_free(SuilWrapper* wrapper)
+{
+	SuilGtk2InQt5Wrapper* impl = (SuilGtk2InQt5Wrapper*)wrapper->impl;
+
+	if (impl->plug) {
+		gtk_widget_destroy(impl->plug);
+	}
+
+	if (impl->host_widget) {
+		delete impl->host_widget;
+	}
+
+	free(impl);
+}
+
+static int
+wrapper_wrap(SuilWrapper*  wrapper,
+             SuilInstance* instance)
+{
+	Qt::WindowFlags wflags = Qt::Window
+		| Qt::CustomizeWindowHint
+		| Qt::WindowTitleHint
+		| Qt::WindowSystemMenuHint
+		| Qt::WindowMinMaxButtonsHint
+		| Qt::WindowCloseButtonHint;
+
+	SuilGtk2InQt5Wrapper* const impl   = (SuilGtk2InQt5Wrapper*)wrapper->impl;
+	QWidget*                    parent = static_cast<QWidget*>(impl->parent);
+	QWidget* const              wrap   = new QWidget(parent, wflags);
+//	GtkWidget* const            plug   = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	GtkWidget* const            plug   = gtk_plug_new(0);
+	GtkWidget* const            widget = (GtkWidget*)instance->ui_widget;
+
+	gtk_container_add(GTK_CONTAINER(plug), widget);
+	gtk_widget_show_all(plug);
+
+//	const WId wid = GDK_WINDOW_XID(gtk_widget_get_window(plug));
+	const WId wid = gtk_plug_get_id((GtkPlug *)plug);
+	QWindow* window = QWindow::fromWinId(wid);
+	QWidget* container = QWidget::createWindowContainer(window, wrap);
+	QVBoxLayout* layout = new QVBoxLayout();
+	layout->setMargin(0);
+	layout->setSpacing(0);
+	layout->addWidget(container);
+	wrap->setLayout(layout);
+
+#ifdef SUIL_OLD_GTK
+	wrap->resize(widget->allocation.width, widget->allocation.height);
+#else
+	GtkAllocation alloc;
+	gtk_widget_get_allocation(widget, &alloc);
+	wrap->resize(alloc.width, alloc.height);
+#endif
+
+	g_signal_connect(
+		G_OBJECT(plug), "size-request", G_CALLBACK(on_size_request), wrap);
+
+	g_signal_connect(
+		G_OBJECT(plug), "size-allocate", G_CALLBACK(on_size_allocate), wrap);
+
+	impl->host_widget     = wrap;
+	impl->plug            = plug;
+	instance->host_widget = wrap;
+
+	return 0;
+}
+
+SUIL_LIB_EXPORT
+SuilWrapper*
+suil_wrapper_new(SuilHost*      host,
+                 const char*    host_type_uri,
+                 const char*    ui_type_uri,
+                 LV2_Feature*** features,
+                 unsigned       n_features)
+{
+	/* We have to open libgtk here, so Gtk type symbols are present and will be
+	   found by the introspection stuff.  This is required at least to make
+	   GtkBuilder use in UIs work, otherwise they will cause "Invalid object
+	   type" errors.
+	*/
+	if (!host->gtk_lib) {
+		dlerror();
+		host->gtk_lib = dlopen(SUIL_GTK2_LIB_NAME, RTLD_LAZY|RTLD_GLOBAL);
+		if (!host->gtk_lib) {
+			fprintf(stderr, "Failed to open %s (%s)\n",
+			        SUIL_GTK2_LIB_NAME, dlerror());
+			return NULL;
+		}
+		gtk_init(NULL, NULL);
+	}
+
+	/* Create wrapper implementation. */
+	SuilGtk2InQt5Wrapper* const impl = (SuilGtk2InQt5Wrapper*)
+		malloc(sizeof(SuilGtk2InQt5Wrapper));
+	impl->host_widget = NULL;
+	impl->parent      = NULL;
+	impl->plug        = NULL;
+
+	/* Set parent widget if given. */
+	for (unsigned i = 0; i < n_features; ++i) {
+		if (!strcmp((*features)[i]->URI, LV2_UI__parent)) {
+			impl->parent = static_cast<QWidget*>((*features)[i]->data);
+		}
+	}
+
+	SuilWrapper* wrapper = (SuilWrapper*)malloc(sizeof(SuilWrapper));
+	wrapper->wrap = wrapper_wrap;
+	wrapper->free = wrapper_free;
+	wrapper->impl = impl;
+
+	return wrapper;
+}
+
+}  // extern "C"
Index: src/instance.c
===================================================================
--- src/instance.c	(revision 5719)
+++ src/instance.c	(working copy)
@@ -24,6 +24,7 @@
 
 #define GTK2_UI_URI  LV2_UI__GtkUI
 #define QT4_UI_URI   LV2_UI__Qt4UI
+#define QT5_UI_URI   LV2_UI_PREFIX "Qt5UI"
 #define X11_UI_URI   LV2_UI__X11UI
 #define WIN_UI_URI   LV2_UI_PREFIX "WindowsUI"
 #define COCOA_UI_URI LV2_UI__CocoaUI
@@ -44,6 +45,8 @@
 	            && !strcmp(ui_type_uri, QT4_UI_URI))
 	           || (!strcmp(container_type_uri, QT4_UI_URI)
 	               && !strcmp(ui_type_uri, GTK2_UI_URI))
+	           || (!strcmp(container_type_uri, QT5_UI_URI)
+	               && !strcmp(ui_type_uri, GTK2_UI_URI))
 	           || (!strcmp(container_type_uri, GTK2_UI_URI)
 	               && !strcmp(ui_type_uri, X11_UI_URI))
 	           || (!strcmp(container_type_uri, GTK2_UI_URI)
@@ -51,6 +54,8 @@
 	           || (!strcmp(container_type_uri, GTK2_UI_URI)
 	               && !strcmp(ui_type_uri, COCOA_UI_URI))
 	           || (!strcmp(container_type_uri, QT4_UI_URI)
+	               && !strcmp(ui_type_uri, X11_UI_URI))
+	           || (!strcmp(container_type_uri, QT5_UI_URI)
 	               && !strcmp(ui_type_uri, X11_UI_URI))) {
 		return SUIL_WRAPPING_EMBEDDED;
 	} else {
@@ -76,6 +81,12 @@
 		module_name = "suil_gtk2_in_qt4";
 	}
 #endif
+#ifdef SUIL_WITH_GTK2_IN_QT5
+	if (!strcmp(container_type_uri, QT5_UI_URI)
+	    && !strcmp(ui_type_uri, GTK2_UI_URI)) {
+		module_name = "suil_gtk2_in_qt5";
+	}
+#endif
 #ifdef SUIL_WITH_QT4_IN_GTK2
 	if (!strcmp(container_type_uri, GTK2_UI_URI)
 	           && !strcmp(ui_type_uri, QT4_UI_URI)) {
@@ -106,6 +117,12 @@
 		module_name = "suil_x11_in_qt4";
 	}
 #endif
+#ifdef SUIL_WITH_X11_IN_QT5
+	if (!strcmp(container_type_uri, QT5_UI_URI)
+	           && !strcmp(ui_type_uri, X11_UI_URI)) {
+		module_name = "suil_x11_in_qt5";
+	}
+#endif
 
 	if (!module_name) {
 		SUIL_ERRORF("Unable to wrap UI type <%s> as type <%s>\n",
Index: src/x11_in_qt5.cpp
===================================================================
--- src/x11_in_qt5.cpp	(revision 0)
+++ src/x11_in_qt5.cpp	(working copy)
@@ -0,0 +1,165 @@
+/*
+  Copyright 2011-2013 David Robillard <http://drobilla.net>
+  Copyright 2015 rncbc aka. Rui Nuno Capela <rncbc@rncbc.org>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <QWidget>
+
+#include <QTimerEvent>
+
+#undef signals
+
+#include "./suil_config.h"
+#include "./suil_internal.h"
+
+#ifndef HAVE_LV2_1_6_0
+typedef struct _LV2UI_Idle_Interface LV2UI_Idle_Interface;
+#endif
+
+extern "C" {
+
+typedef struct {
+	QWidget* host_widget;
+	QWidget* parent;
+} SuilX11InQt5Wrapper;
+
+class SuilQX11Widget : public QWidget
+{
+public:
+	SuilQX11Widget(QWidget* parent, Qt::WindowFlags wflags)
+		: QWidget(parent, wflags)
+#ifdef HAVE_LV2_1_6_0
+		, _instance(NULL)
+		, _idle_iface(NULL)
+		, _ui_timer(0)
+#endif
+	{}
+
+#ifdef HAVE_LV2_1_6_0
+	void start_idle(SuilInstance*   instance,
+		const LV2UI_Idle_Interface* idle_iface) {
+		_instance = instance;
+		_idle_iface = idle_iface;
+		if (_idle_iface && _ui_timer == 0) {
+			_ui_timer = this->startTimer(30);
+		}
+	}
+protected:
+	void timerEvent(QTimerEvent* event) {
+		if (event->timerId() == _ui_timer && _idle_iface) {
+			_idle_iface->idle(_instance->handle);
+		}
+		QWidget::timerEvent(event);
+	}
+private:
+	SuilInstance*               _instance;
+	const LV2UI_Idle_Interface* _idle_iface;
+	int                         _ui_timer;
+#endif
+};
+
+static void
+wrapper_free(SuilWrapper* wrapper)
+{
+	SuilX11InQt5Wrapper* impl = (SuilX11InQt5Wrapper*)wrapper->impl;
+
+	if (impl->parent) {
+		delete impl->parent;
+	}
+/*
+	if (impl->host_widget) {
+		delete impl->host_widget;
+	}
+*/
+	free(impl);
+}
+
+static int
+wrapper_wrap(SuilWrapper*  wrapper,
+             SuilInstance* instance)
+{
+	SuilX11InQt5Wrapper* const impl = (SuilX11InQt5Wrapper*)wrapper->impl;
+	SuilQX11Widget* const      ew   = (SuilQX11Widget*)impl->parent;
+
+#ifdef HAVE_LV2_1_6_0
+	if (instance->descriptor->extension_data) {
+		const LV2UI_Idle_Interface* idle_iface
+			= (const LV2UI_Idle_Interface*)
+				instance->descriptor->extension_data(LV2_UI__idleInterface);
+		ew->start_idle(instance, idle_iface);
+	}
+#endif
+
+	impl->host_widget = ew;
+
+	instance->host_widget = impl->host_widget;
+
+	return 0;
+}
+
+static int
+wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
+{
+	QWidget* const ew = (QWidget*)handle;
+	ew->resize(width, height);
+	return 0;
+}
+
+SUIL_LIB_EXPORT
+SuilWrapper*
+suil_wrapper_new(SuilHost*      host,
+                 const char*    host_type_uri,
+                 const char*    ui_type_uri,
+                 LV2_Feature*** features,
+                 unsigned       n_features)
+{
+	SuilX11InQt5Wrapper* const impl = (SuilX11InQt5Wrapper*)
+		malloc(sizeof(SuilX11InQt5Wrapper));
+	impl->host_widget = NULL;
+	impl->parent      = NULL;
+
+	SuilWrapper* wrapper = (SuilWrapper*)malloc(sizeof(SuilWrapper));
+	wrapper->wrap = wrapper_wrap;
+	wrapper->free = wrapper_free;
+
+	Qt::WindowFlags wflags = Qt::Window
+		| Qt::CustomizeWindowHint
+		| Qt::WindowTitleHint
+		| Qt::WindowSystemMenuHint
+		| Qt::WindowMinMaxButtonsHint
+		| Qt::WindowCloseButtonHint;
+
+	QWidget* const ew = new SuilQX11Widget(NULL, wflags);
+
+	impl->parent = ew;
+
+	wrapper->impl             = impl;
+	wrapper->resize.handle    = ew;
+	wrapper->resize.ui_resize = wrapper_resize;
+
+	suil_add_feature(features, &n_features, LV2_UI__parent,
+	                 (void*)(intptr_t)ew->winId());
+
+	suil_add_feature(features, &n_features, LV2_UI__resize,
+	                 &wrapper->resize);
+
+#ifdef HAVE_LV2_1_6_0
+	suil_add_feature(features, &n_features, LV2_UI__idleInterface, NULL);
+#endif
+
+	return wrapper;
+}
+
+}  // extern "C"
Index: wscript
===================================================================
--- wscript	(revision 5719)
+++ wscript	(working copy)
@@ -29,8 +29,10 @@
                    help='Do not build shared library')
     opt.add_option('--no-gtk', action='store_true', dest='no_gtk',
                    help='Do not build support for Gtk')
-    opt.add_option('--no-qt', action='store_true', dest='no_qt',
-                   help='Do not build support for Qt')
+    opt.add_option('--no-qt4', action='store_true', dest='no_qt4',
+                   help='Do not build support for Qt4')
+    opt.add_option('--no-qt5', action='store_true', dest='no_qt5',
+                   help='Do not build support for Qt5')
     opt.add_option('--gtk2-lib-name', type='string', dest='gtk2_lib_name',
                    default="libgtk-x11-2.0.so.0",
                    help="Gtk2 library name [Default: libgtk-x11-2.0.so.0]")
@@ -75,10 +77,14 @@
         autowaf.check_pkg(conf, 'gtk+-quartz-2.0', uselib_store='GTK2_QUARTZ',
                           atleast_version='2.0.0', mandatory=False)
 
-    if not Options.options.no_qt:
+    if not Options.options.no_qt4:
         autowaf.check_pkg(conf, 'QtGui', uselib_store='QT4',
-                          atleast_version='4.0.0', mandatory=False)
+                          atleast_version='4.4.0', mandatory=False)
 
+    if not Options.options.no_qt5:
+        autowaf.check_pkg(conf, 'Qt5Widgets', uselib_store='QT5',
+                          atleast_version='5.1.0', mandatory=False)
+
     conf.check_cc(define_name   = 'HAVE_LIBDL',
                   lib           = 'dl',
                   mandatory     = False)
@@ -93,6 +99,9 @@
         autowaf.define(conf, 'SUIL_WITH_GTK2_IN_QT4', 1)
         autowaf.define(conf, 'SUIL_WITH_QT4_IN_GTK2', 1)
 
+    if conf.env.HAVE_GTK2 and conf.env.HAVE_QT5:
+        autowaf.define(conf, 'SUIL_WITH_GTK2_IN_QT5', 1)
+
     if conf.env.HAVE_GTK2 and conf.env.HAVE_GTK2_X11:
         autowaf.define(conf, 'SUIL_WITH_X11_IN_GTK2', 1)
 
@@ -105,6 +114,9 @@
     if conf.env.HAVE_QT4:
         autowaf.define(conf, 'SUIL_WITH_X11_IN_QT4', 1)
 
+    if conf.env.HAVE_QT5:
+        autowaf.define(conf, 'SUIL_WITH_X11_IN_QT5', 1)
+
     module_prefix = ''
     module_ext    = ''
     if conf.env.PARDEBUG:
@@ -129,6 +141,7 @@
         autowaf.display_msg(conf, "Gtk2 Library Name",
                             conf.env.SUIL_GTK2_LIB_NAME)
     autowaf.display_msg(conf, "Qt4 Support", bool(conf.env.HAVE_QT4))
+    autowaf.display_msg(conf, "Qt5 Support", bool(conf.env.HAVE_QT5))
     print('')
 
 def build(bld):
@@ -195,6 +208,17 @@
                   lib          = modlib)
         autowaf.use_lib(bld, obj, 'GTK2 QT4 LV2 LV2_1_6_0')
 
+    if bld.env.SUIL_WITH_GTK2_IN_QT5:
+        obj = bld(features     = 'cxx cxxshlib',
+                  source       = 'src/gtk2_in_qt5.cpp',
+                  target       = 'suil_gtk2_in_qt5',
+                  includes     = ['.'],
+                  defines      = ['SUIL_SHARED', 'SUIL_INTERNAL'],
+                  install_path = module_dir,
+                  cflags       = cflags,
+                  lib          = modlib)
+        autowaf.use_lib(bld, obj, 'GTK2 QT5 LV2 LV2_1_6_0')
+
     if bld.env.SUIL_WITH_QT4_IN_GTK2:
         obj = bld(features     = 'cxx cxxshlib',
                   source       = 'src/qt4_in_gtk2.cpp',
@@ -254,6 +278,17 @@
                   lib          = modlib)
         autowaf.use_lib(bld, obj, 'QT4 LV2 LV2_1_6_0')
 
+    if bld.env.SUIL_WITH_X11_IN_QT5:
+        obj = bld(features     = 'cxx cxxshlib',
+                  source       = 'src/x11_in_qt5.cpp',
+                  target       = 'suil_x11_in_qt5',
+                  includes     = ['.'],
+                  defines      = ['SUIL_SHARED', 'SUIL_INTERNAL'],
+                  install_path = module_dir,
+                  cflags       = cflags,
+                  lib          = modlib)
+        autowaf.use_lib(bld, obj, 'QT5 LV2 LV2_1_6_0')
+
     # Documentation
     autowaf.build_dox(bld, 'SUIL', SUIL_VERSION, top, out)
 
