diff options
author | David Robillard <d@drobilla.net> | 2015-09-12 16:02:02 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2015-09-12 16:02:02 +0000 |
commit | efa94682fd223a084687cdcf0f626d56325de0e0 (patch) | |
tree | 6da00f1bd9a861679aca9a53cc702d1505d536b5 | |
parent | f18371240cf3d32940e9a67844505b46006e0788 (diff) | |
download | suil-efa94682fd223a084687cdcf0f626d56325de0e0.tar.gz suil-efa94682fd223a084687cdcf0f626d56325de0e0.tar.bz2 suil-efa94682fd223a084687cdcf0f626d56325de0e0.zip |
Add Gtk2 and X11 in Qt5 wrappers.
git-svn-id: http://svn.drobilla.net/lad/trunk/suil@5725 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | src/gtk2_in_qt4.cpp | 4 | ||||
-rw-r--r-- | src/gtk2_in_qt5.cpp | 168 | ||||
-rw-r--r-- | src/host.c | 4 | ||||
-rw-r--r-- | src/instance.c | 27 | ||||
-rw-r--r-- | src/x11_in_qt5.cpp | 165 | ||||
-rw-r--r-- | wscript | 44 |
8 files changed, 403 insertions, 15 deletions
@@ -13,4 +13,5 @@ Contributors: * Cocoa in Gtk wrapper * Numerous Windows fixes * Rui Nuno Capela - * Fixes for X11 in Qt4
\ No newline at end of file + * Fixes for X11 in Qt4 + * Qt5 wrappers
\ No newline at end of file @@ -3,12 +3,13 @@ suil (0.8.3) unstable; * Configure based on compiler target OS for cross-compilation * Add Cocoa in Gtk wrapper (patch from Robin Gareus) * Various Windows fixes (patches from Robin Gareus) + * Add Gtk2 and X11 in Qt5 wrappers (patch from Rui Nuno Capela) * Fix compilation with -Wl,--no-undefined * Fix a few minor/unlikely memory errors * Gracefully handle failure to open wrapper * Only report suil_ui_supported() if necessary wrapper is compiled in - -- David Robillard <d@drobilla.net> Fri, 11 Sep 2015 17:38:26 -0400 + -- David Robillard <d@drobilla.net> Sat, 12 Sep 2015 10:54:09 -0400 suil (0.8.2) stable; diff --git a/src/gtk2_in_qt4.cpp b/src/gtk2_in_qt4.cpp index 641d1ba..ab474be 100644 --- a/src/gtk2_in_qt4.cpp +++ b/src/gtk2_in_qt4.cpp @@ -118,8 +118,8 @@ suil_wrapper_new(SuilHost* host, 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()); + SUIL_ERRORF("Failed to open %s (%s)\n", + SUIL_GTK2_LIB_NAME, dlerror()); return NULL; } gtk_init(NULL, NULL); diff --git a/src/gtk2_in_qt5.cpp b/src/gtk2_in_qt5.cpp new file mode 100644 index 0000000..0d42ffd --- /dev/null +++ b/src/gtk2_in_qt5.cpp @@ -0,0 +1,168 @@ +/* + Copyright 2011-2015 David Robillard <http://drobilla.net> + Copyright 2015 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_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 = 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) { + SUIL_ERRORF("Failed to open %s (%s)\n", + SUIL_GTK2_LIB_NAME, dlerror()); + return NULL; + } + gtk_init(NULL, NULL); + } + + /* Create wrapper implementation. */ + SuilGtk2InQt5Wrapper* const impl = (SuilGtk2InQt5Wrapper*) + calloc(1, sizeof(SuilGtk2InQt5Wrapper)); + + /* 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*)calloc(1, sizeof(SuilWrapper)); + wrapper->wrap = wrapper_wrap; + wrapper->free = wrapper_free; + wrapper->impl = impl; + + return wrapper; +} + +} // extern "C" @@ -23,13 +23,11 @@ suil_host_new(SuilPortWriteFunc write_func, SuilPortSubscribeFunc subscribe_func, SuilPortUnsubscribeFunc unsubscribe_func) { - SuilHost* host = (SuilHost*)malloc(sizeof(struct SuilHostImpl)); + SuilHost* host = (SuilHost*)calloc(1, sizeof(struct SuilHostImpl)); host->write_func = write_func; host->index_func = index_func; host->subscribe_func = subscribe_func; host->unsubscribe_func = unsubscribe_func; - host->touch_func = NULL; - host->gtk_lib = NULL; return host; } diff --git a/src/instance.c b/src/instance.c index a336dcb..1b26b96 100644 --- a/src/instance.c +++ b/src/instance.c @@ -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 @@ suil_ui_supported(const char* container_type_uri, && !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 @@ suil_ui_supported(const char* container_type_uri, || (!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 { @@ -72,36 +77,48 @@ open_wrapper(SuilHost* host, 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)) { + && !strcmp(ui_type_uri, QT4_UI_URI)) { module_name = "suil_qt4_in_gtk2"; } #endif #ifdef SUIL_WITH_X11_IN_GTK2 if (!strcmp(container_type_uri, GTK2_UI_URI) - && !strcmp(ui_type_uri, X11_UI_URI)) { + && !strcmp(ui_type_uri, X11_UI_URI)) { module_name = "suil_x11_in_gtk2"; } #endif #ifdef SUIL_WITH_WIN_IN_GTK2 if (!strcmp(container_type_uri, GTK2_UI_URI) - && !strcmp(ui_type_uri, WIN_UI_URI)) { + && !strcmp(ui_type_uri, WIN_UI_URI)) { module_name = "suil_win_in_gtk2"; } #endif #ifdef SUIL_WITH_COCOA_IN_GTK2 if (!strcmp(container_type_uri, GTK2_UI_URI) - && !strcmp(ui_type_uri, COCOA_UI_URI)) { + && !strcmp(ui_type_uri, COCOA_UI_URI)) { module_name = "suil_cocoa_in_gtk2"; } #endif #ifdef SUIL_WITH_X11_IN_QT4 if (!strcmp(container_type_uri, QT4_UI_URI) - && !strcmp(ui_type_uri, X11_UI_URI)) { + && !strcmp(ui_type_uri, X11_UI_URI)) { 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", diff --git a/src/x11_in_qt5.cpp b/src/x11_in_qt5.cpp new file mode 100644 index 0000000..1f62796 --- /dev/null +++ b/src/x11_in_qt5.cpp @@ -0,0 +1,165 @@ +/* + Copyright 2011-2015 David Robillard <http://drobilla.net> + Copyright 2015 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*) + calloc(1, sizeof(SuilX11InQt5Wrapper)); + + 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" @@ -30,7 +30,11 @@ def options(opt): 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') + help='Do not build support for Qt (any version)') + 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]") @@ -76,8 +80,13 @@ def configure(conf): atleast_version='2.0.0', mandatory=False) if not Options.options.no_qt: - autowaf.check_pkg(conf, 'QtGui', uselib_store='QT4', - atleast_version='4.0.0', mandatory=False) + if not Options.options.no_qt4: + autowaf.check_pkg(conf, 'QtGui', uselib_store='QT4', + 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', @@ -93,6 +102,9 @@ def configure(conf): 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 +117,9 @@ def configure(conf): 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 +144,7 @@ def configure(conf): 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 +211,17 @@ def build(bld): 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 +281,17 @@ def build(bld): 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) |