From eb33777e6ab2f9ebd5f398bb9c9a9ae92ac3531d Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Fri, 3 Aug 2012 01:57:22 +0000
Subject: Support for embedding native Windows UIs in Gtk2.

git-svn-id: http://svn.drobilla.net/lad/trunk/suil@4603 a436a847-0d15-0410-975c-d299462d15a1
---
 NEWS              |   1 +
 src/instance.c    |  19 +++++--
 src/win_in_gtk2.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 wscript           |  17 +++++-
 4 files changed, 191 insertions(+), 7 deletions(-)
 create mode 100644 src/win_in_gtk2.c

diff --git a/NEWS b/NEWS
index 3bedd7e..567c686 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,6 @@
 suil (9999) unstable;
 
+  * Support for wrapping native Windows UIs in Gtk2
   * Gracefully handle UIs with no port_event method
   * Replace host provided features that match Suil implemented features, rather
     than passing UIs duplicate features
diff --git a/src/instance.c b/src/instance.c
index 5b53328..951b12f 100644
--- a/src/instance.c
+++ b/src/instance.c
@@ -26,6 +26,7 @@
 #define GTK2_UI_URI LV2_UI__GtkUI
 #define QT4_UI_URI  LV2_UI__Qt4UI
 #define X11_UI_URI  LV2_UI__X11UI
+#define WIN_UI_URI  LV2_UI_PREFIX "WindowsUI"
 
 SUIL_API
 unsigned
@@ -45,6 +46,8 @@ suil_ui_supported(const char* container_type_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)
+	               && !strcmp(ui_type_uri, WIN_UI_URI))
 	           || (!strcmp(container_type_uri, QT4_UI_URI)
 	               && !strcmp(ui_type_uri, X11_UI_URI))) {
 		return SUIL_WRAPPING_EMBEDDED;
@@ -67,16 +70,19 @@ open_wrapper(SuilHost*      host,
 	const char* module_name = NULL;
 	if (!strcmp(container_type_uri, QT4_UI_URI)
 	    && !strcmp(ui_type_uri, GTK2_UI_URI)) {
-		module_name = "libsuil_gtk2_in_qt4";
+		module_name = "suil_gtk2_in_qt4";
 	} else if (!strcmp(container_type_uri, GTK2_UI_URI)
 	           && !strcmp(ui_type_uri, QT4_UI_URI)) {
-		module_name = "libsuil_qt4_in_gtk2";
+		module_name = "suil_qt4_in_gtk2";
 	} else if (!strcmp(container_type_uri, GTK2_UI_URI)
 	           && !strcmp(ui_type_uri, X11_UI_URI)) {
-		module_name = "libsuil_x11_in_gtk2";
+		module_name = "suil_x11_in_gtk2";
+	} else if (!strcmp(container_type_uri, GTK2_UI_URI)
+	           && !strcmp(ui_type_uri, WIN_UI_URI)) {
+		module_name = "suil_win_in_gtk2";
 	} else if (!strcmp(container_type_uri, QT4_UI_URI)
 	           && !strcmp(ui_type_uri, X11_UI_URI)) {
-		module_name = "libsuil_x11_in_qt4";
+		module_name = "suil_x11_in_qt4";
 	}
 
 	if (!module_name) {
@@ -94,8 +100,9 @@ open_wrapper(SuilHost*      host,
 		+ 2;
 
 	char* const path = calloc(path_len, 1);
-	snprintf(path, path_len, "%s%s%s%s",
-	         mod_dir, SUIL_DIR_SEP, module_name, SUIL_MODULE_EXT);
+	snprintf(path, path_len, "%s%s%s%s%s",
+	         mod_dir, SUIL_DIR_SEP,
+	         SUIL_MODULE_PREFIX, module_name, SUIL_MODULE_EXT);
 
 	// Open wrap module
 	dlerror();
diff --git a/src/win_in_gtk2.c b/src/win_in_gtk2.c
new file mode 100644
index 0000000..8367996
--- /dev/null
+++ b/src/win_in_gtk2.c
@@ -0,0 +1,161 @@
+/*
+  Copyright 2011-2012 David Robillard <http://drobilla.net>
+
+  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 <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkwin32.h>
+
+#include "./suil_internal.h"
+
+#define SUIL_TYPE_WIN_WRAPPER (suil_win_wrapper_get_type())
+#define SUIL_WIN_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_WIN_WRAPPER, SuilWinWrapper))
+
+typedef struct _SuilWinWrapper      SuilWinWrapper;
+typedef struct _SuilWinWrapperClass SuilWinWrapperClass;
+
+struct _SuilWinWrapper {
+	GtkDrawingArea area;
+	SuilWrapper*   wrapper;
+	SuilInstance*  instance;
+};
+
+struct _SuilWinWrapperClass {
+	GtkDrawingAreaClass parent_class;
+};
+
+GType suil_win_wrapper_get_type(void);  // Accessor for SUIL_TYPE_WIN_WRAPPER
+
+G_DEFINE_TYPE(SuilWinWrapper, suil_win_wrapper, GTK_TYPE_DRAWING_AREA)
+
+static void
+suil_win_wrapper_finalize(GObject* gobject)
+{
+	SuilWinWrapper* const self = SUIL_WIN_WRAPPER(gobject);
+
+	self->wrapper->impl = NULL;
+
+	G_OBJECT_CLASS(suil_win_wrapper_parent_class)->finalize(gobject);
+}
+
+static void
+suil_win_wrapper_class_init(SuilWinWrapperClass* klass)
+{
+	GObjectClass* const gobject_class = G_OBJECT_CLASS(klass);
+
+	gobject_class->finalize = suil_win_wrapper_finalize;
+}
+
+static void
+suil_win_wrapper_init(SuilWinWrapper* self)
+{
+	self->instance = NULL;
+}
+
+GdkFilterReturn
+event_filter(GdkXEvent* xevent, GdkEvent* event, gpointer data)
+{
+	SuilWinWrapper* wrap = (SuilWinWrapper*)data;
+	MSG*            msg  = (MSG*)xevent;
+	if (msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
+		// Forward keyboard events to UI window
+		PostMessage((HWND)wrap->instance->ui_widget,
+		            msg->message, msg->wParam, msg->lParam);
+	}
+	return GDK_FILTER_CONTINUE;
+}
+
+static int
+wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
+{
+	gtk_drawing_area_size(GTK_DRAWING_AREA(handle), width, height);
+	return 0;
+}
+
+static int
+wrapper_wrap(SuilWrapper*  wrapper,
+             SuilInstance* instance)
+{
+	SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(wrapper->impl);
+
+	instance->host_widget = GTK_WIDGET(wrap);
+	wrap->wrapper         = wrapper;
+	wrap->instance        = instance;
+
+	return 0;
+}
+
+static void
+wrapper_free(SuilWrapper* wrapper)
+{
+	if (wrapper->impl) {
+		SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(wrapper->impl);
+		gtk_object_destroy(GTK_OBJECT(wrap));
+	}
+}
+
+SUIL_API
+SuilWrapper*
+suil_wrapper_new(SuilHost*      host,
+                 const char*    host_type_uri,
+                 const char*    ui_type_uri,
+                 LV2_Feature*** features,
+                 unsigned       n_features)
+{
+	GtkWidget* parent = NULL;
+	for (unsigned i = 0; i < n_features; ++i) {
+		if (!strcmp((*features)[i]->URI, LV2_UI__parent)) {
+			parent = (GtkWidget*)(*features)[i]->data;
+		}
+	}
+
+	if (!GTK_CONTAINER(parent)) {
+		SUIL_ERRORF("No GtkContainer parent given for %s UI\n",
+		            ui_type_uri);
+		return NULL;
+	}
+
+	SuilWrapper* wrapper = (SuilWrapper*)malloc(sizeof(SuilWrapper));
+	wrapper->wrap = wrapper_wrap;
+	wrapper->free = wrapper_free;
+
+	SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(
+		g_object_new(SUIL_TYPE_WIN_WRAPPER, NULL));
+
+	wrap->wrapper = NULL;
+
+	wrapper->impl             = wrap;
+	wrapper->resize.handle    = wrap;
+	wrapper->resize.ui_resize = wrapper_resize;
+
+	gtk_container_add(GTK_CONTAINER(parent), GTK_WIDGET(wrap));
+	gtk_widget_set_can_focus(GTK_WIDGET(wrap), TRUE);
+	gtk_widget_set_sensitive(GTK_WIDGET(wrap), TRUE);
+	gtk_widget_realize(GTK_WIDGET(wrap));
+
+	GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(wrap));
+	GdkWindow* parent_window = gtk_widget_get_window(parent);
+	gdk_window_add_filter(parent_window, event_filter, wrap);
+	gdk_window_add_filter(window, event_filter, wrap);
+
+	suil_add_feature(features, &n_features, LV2_UI__parent,
+	                 GDK_WINDOW_HWND(window));
+
+	suil_add_feature(features, &n_features, LV2_UI__resize,
+	                 &wrapper->resize);
+
+	return wrapper;
+}
diff --git a/wscript b/wscript
index 2b873dd..78b98af 100644
--- a/wscript
+++ b/wscript
@@ -65,8 +65,13 @@ def configure(conf):
     autowaf.define(conf, 'SUIL_MODULE_DIR',
                    conf.env['LIBDIR'] + '/suil-' + SUIL_MAJOR_VERSION)
     autowaf.define(conf, 'SUIL_DIR_SEP', '/')
-    autowaf.define(conf, 'SUIL_MODULE_EXT', '.so')
     autowaf.define(conf, 'SUIL_GTK2_LIB_NAME', Options.options.gtk2_lib_name);
+    if Options.platform == 'win32':
+        autowaf.define(conf, 'SUIL_MODULE_PREFIX', '')
+        autowaf.define(conf, 'SUIL_MODULE_EXT', '.dll')
+    else:
+        autowaf.define(conf, 'SUIL_MODULE_PREFIX', 'lib')
+        autowaf.define(conf, 'SUIL_MODULE_EXT', '.so')
 
     conf.env['LIB_SUIL'] = ['suil-%s' % SUIL_MAJOR_VERSION]
 
@@ -138,6 +143,16 @@ def build(bld):
                   linkflags    = bld.env['NODELETE_FLAGS'])
         autowaf.use_lib(bld, obj, 'GTK2 LV2')
 
+        if sys.platform == 'win32':
+            obj = bld(features     = 'cxx cxxshlib',
+                      source       = 'src/win_in_gtk2.c',
+                      target       = 'suil_win_in_gtk2',
+                      includes     = ['.'],
+                      install_path = module_dir,
+                      cflags       = cflags,
+                      linkflags    = bld.env['NODELETE_FLAGS'])
+            autowaf.use_lib(bld, obj, 'GTK2 LV2')
+
     if bld.is_defined('HAVE_QT4'):
         obj = bld(features     = 'cxx cxxshlib',
                   source       = 'src/x11_in_qt4.cpp',
-- 
cgit v1.2.1