diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | src/instance.c | 19 | ||||
-rw-r--r-- | src/win_in_gtk2.c | 161 | ||||
-rw-r--r-- | wscript | 17 |
4 files changed, 191 insertions, 7 deletions
@@ -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; +} @@ -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', |