From 8fcbdf19755f37592c67deb2c61f5470a1852ea4 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 6 Jun 2019 21:03:16 +0200 Subject: Add support for Qt5 in Gtk3 --- NEWS | 3 +- src/instance.c | 8 ++ src/qt5_in_gtk.cpp | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/qt5_in_gtk2.cpp | 229 --------------------------------------------------- wscript | 18 +++- 5 files changed, 260 insertions(+), 231 deletions(-) create mode 100644 src/qt5_in_gtk.cpp delete mode 100644 src/qt5_in_gtk2.cpp diff --git a/NEWS b/NEWS index af06c39..83e23de 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,9 @@ suil (0.10.3) unstable; * Add support for min/base size hints for X11 in Gtk (thanks Hermann Meyer) + * Add support for Qt5 in Gtk3 - -- David Robillard Tue, 19 Feb 2019 08:34:31 +0100 + -- David Robillard Thu, 06 Jun 2019 21:02:47 +0200 suil (0.10.2) stable; diff --git a/src/instance.c b/src/instance.c index d365ca2..61037fc 100644 --- a/src/instance.c +++ b/src/instance.c @@ -54,6 +54,8 @@ suil_ui_supported(const char* host_type_uri, && !strcmp(ui_type_uri, X11_UI_URI)) || (!strcmp(host_type_uri, GTK3_UI_URI) && !strcmp(ui_type_uri, X11_UI_URI)) + || (!strcmp(host_type_uri, GTK3_UI_URI) + && !strcmp(ui_type_uri, QT5_UI_URI)) || (!strcmp(host_type_uri, GTK2_UI_URI) && !strcmp(ui_type_uri, WIN_UI_URI)) || (!strcmp(host_type_uri, GTK2_UI_URI) @@ -114,6 +116,12 @@ open_wrapper(SuilHost* host, module_name = "suil_x11_in_gtk3"; } #endif +#ifdef SUIL_WITH_QT5_IN_GTK3 + if (!strcmp(container_type_uri, GTK3_UI_URI) + && !strcmp(ui_type_uri, QT5_UI_URI)) { + module_name = "suil_qt5_in_gtk3"; + } +#endif #ifdef SUIL_WITH_WIN_IN_GTK2 if (!strcmp(container_type_uri, GTK2_UI_URI) && !strcmp(ui_type_uri, WIN_UI_URI)) { diff --git a/src/qt5_in_gtk.cpp b/src/qt5_in_gtk.cpp new file mode 100644 index 0000000..e74ab5d --- /dev/null +++ b/src/qt5_in_gtk.cpp @@ -0,0 +1,233 @@ +/* + Copyright 2011-2017 David Robillard + Copyright 2018 Rui Nuno Capela + + 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 + +#include +#include +#include + +#if GTK_MAJOR_VERSION == 3 +#include +#endif + +#include "lv2/options/options.h" +#include "lv2/urid/urid.h" + +#include "./suil_internal.h" + +extern "C" { + +#define SUIL_TYPE_QT_WRAPPER (suil_qt_wrapper_get_type()) +#define SUIL_QT_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_QT_WRAPPER, SuilQtWrapper)) + +typedef struct _SuilQtWrapper SuilQtWrapper; +typedef struct _SuilQtWrapperClass SuilQtWrapperClass; + +struct _SuilQtWrapper { + GtkSocket socket; + QWidget* qembed; + SuilWrapper* wrapper; + SuilInstance* instance; + const LV2UI_Idle_Interface* idle_iface; + guint idle_id; + guint idle_ms; +}; + +struct _SuilQtWrapperClass { + GtkSocketClass parent_class; +}; + +GType suil_qt_wrapper_get_type(void); // Accessor for SUIL_TYPE_QT_WRAPPER + +G_DEFINE_TYPE(SuilQtWrapper, suil_qt_wrapper, GTK_TYPE_SOCKET) + +static void +suil_qt_wrapper_finalize(GObject* gobject) +{ + SuilQtWrapper* const self = SUIL_QT_WRAPPER(gobject); + + if (self->idle_id) { + g_source_remove(self->idle_id); + self->idle_id = 0; + } + + if (self->instance->handle) { + self->instance->descriptor->cleanup(self->instance->handle); + self->instance->handle = NULL; + } + + if (self->qembed) { + self->qembed->deleteLater(); + } + + self->qembed = NULL; + self->idle_iface = NULL; + self->wrapper->impl = NULL; + + G_OBJECT_CLASS(suil_qt_wrapper_parent_class)->finalize(gobject); +} + +static void +suil_qt_wrapper_class_init(SuilQtWrapperClass* klass) +{ + GObjectClass* const gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = suil_qt_wrapper_finalize; +} + +static void +suil_qt_wrapper_init(SuilQtWrapper* self) +{ + self->qembed = NULL; + self->wrapper = NULL; + self->instance = NULL; + self->idle_iface = NULL; + self->idle_id = 0; + self->idle_ms = 1000 / 30; // 30 Hz default +} + +static void +suil_qt_wrapper_realize(GtkWidget* w, gpointer data) +{ + SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(w); + GtkSocket* const s = GTK_SOCKET(w); + const WId id = (WId)gtk_socket_get_id(s); + + wrap->qembed->winId(); + wrap->qembed->windowHandle()->setParent(QWindow::fromWinId(id)); + wrap->qembed->show(); +} + +static int +suil_qt_wrapper_resize(LV2UI_Feature_Handle handle, int width, int height) +{ + gtk_widget_set_size_request(GTK_WIDGET(handle), width, height); + + return 0; +} + +static gboolean +suil_qt_wrapper_idle(void* data) +{ + SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(data); + + if (wrap->idle_iface) { + wrap->idle_iface->idle(wrap->instance->handle); + return TRUE; // Continue calling + } + + return FALSE; +} + +static int +wrapper_wrap(SuilWrapper* wrapper, + SuilInstance* instance) +{ + SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl); + + wrap->qembed = new QWidget(); + wrap->wrapper = wrapper; + wrap->instance = instance; + + QWidget* qwidget = (QWidget*)instance->ui_widget; + QVBoxLayout* layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(qwidget); + + wrap->qembed->setLayout(layout); + + g_signal_connect_after(G_OBJECT(wrap), "realize", + G_CALLBACK(suil_qt_wrapper_realize), NULL); + + instance->host_widget = GTK_WIDGET(wrap); + + const LV2UI_Idle_Interface* idle_iface = NULL; + if (instance->descriptor->extension_data) { + idle_iface = (const LV2UI_Idle_Interface*) + instance->descriptor->extension_data(LV2_UI__idleInterface); + } + + if (idle_iface) { + wrap->idle_iface = idle_iface; + wrap->idle_id = g_timeout_add( + wrap->idle_ms, suil_qt_wrapper_idle, wrap); + } + + return 0; +} + +static void +wrapper_free(SuilWrapper* wrapper) +{ + if (wrapper->impl) { + SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl); + gtk_widget_destroy(GTK_WIDGET(wrap)); + } +} + +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) +{ + SuilWrapper* wrapper = (SuilWrapper*)calloc(1, sizeof(SuilWrapper)); + wrapper->wrap = wrapper_wrap; + wrapper->free = wrapper_free; + + SuilQtWrapper* const wrap = SUIL_QT_WRAPPER( + g_object_new(SUIL_TYPE_QT_WRAPPER, NULL)); + + wrap->wrapper = NULL; + wrapper->impl = wrap; + + wrapper->resize.handle = wrap; + wrapper->resize.ui_resize = suil_qt_wrapper_resize; + + suil_add_feature(features, &n_features, LV2_UI__resize, &wrapper->resize); + suil_add_feature(features, &n_features, LV2_UI__idleInterface, NULL); + + // Scan for URID map and options + LV2_URID_Map* map = NULL; + LV2_Options_Option* options = NULL; + for (LV2_Feature** f = *features; *f && (!map || !options); ++f) { + if (!strcmp((*f)->URI, LV2_OPTIONS__options)) { + options = (LV2_Options_Option*)(*f)->data; + } else if (!strcmp((*f)->URI, LV2_URID__map)) { + map = (LV2_URID_Map*)(*f)->data; + } + } + + if (map && options) { + // Set UI update rate if given + LV2_URID ui_updateRate = map->map(map->handle, LV2_UI__updateRate); + for (LV2_Options_Option* o = options; o->key; ++o) { + if (o->key == ui_updateRate) { + wrap->idle_ms = 1000.0f / *(const float*)o->value; + break; + } + } + } + + return wrapper; +} + +} // extern "C" diff --git a/src/qt5_in_gtk2.cpp b/src/qt5_in_gtk2.cpp deleted file mode 100644 index eac44af..0000000 --- a/src/qt5_in_gtk2.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - Copyright 2011-2017 David Robillard - Copyright 2018 Rui Nuno Capela - - 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 - -#include -#include -#include - -#include "lv2/options/options.h" -#include "lv2/urid/urid.h" - -#include "./suil_internal.h" - -extern "C" { - -#define SUIL_TYPE_QT_WRAPPER (suil_qt_wrapper_get_type()) -#define SUIL_QT_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_QT_WRAPPER, SuilQtWrapper)) - -typedef struct _SuilQtWrapper SuilQtWrapper; -typedef struct _SuilQtWrapperClass SuilQtWrapperClass; - -struct _SuilQtWrapper { - GtkSocket socket; - QWidget* qembed; - SuilWrapper* wrapper; - SuilInstance* instance; - const LV2UI_Idle_Interface* idle_iface; - guint idle_id; - guint idle_ms; -}; - -struct _SuilQtWrapperClass { - GtkSocketClass parent_class; -}; - -GType suil_qt_wrapper_get_type(void); // Accessor for SUIL_TYPE_QT_WRAPPER - -G_DEFINE_TYPE(SuilQtWrapper, suil_qt_wrapper, GTK_TYPE_SOCKET) - -static void -suil_qt_wrapper_finalize(GObject* gobject) -{ - SuilQtWrapper* const self = SUIL_QT_WRAPPER(gobject); - - if (self->idle_id) { - g_source_remove(self->idle_id); - self->idle_id = 0; - } - - if (self->instance->handle) { - self->instance->descriptor->cleanup(self->instance->handle); - self->instance->handle = NULL; - } - - if (self->qembed) { - self->qembed->deleteLater(); - } - - self->qembed = NULL; - self->idle_iface = NULL; - self->wrapper->impl = NULL; - - G_OBJECT_CLASS(suil_qt_wrapper_parent_class)->finalize(gobject); -} - -static void -suil_qt_wrapper_class_init(SuilQtWrapperClass* klass) -{ - GObjectClass* const gobject_class = G_OBJECT_CLASS(klass); - - gobject_class->finalize = suil_qt_wrapper_finalize; -} - -static void -suil_qt_wrapper_init(SuilQtWrapper* self) -{ - self->qembed = NULL; - self->wrapper = NULL; - self->instance = NULL; - self->idle_iface = NULL; - self->idle_id = 0; - self->idle_ms = 1000 / 30; // 30 Hz default -} - -static void -suil_qt_wrapper_realize(GtkWidget* w, gpointer data) -{ - SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(w); - GtkSocket* const s = GTK_SOCKET(w); - const WId id = (WId)gtk_socket_get_id(s); - - wrap->qembed->winId(); - wrap->qembed->windowHandle()->setParent(QWindow::fromWinId(id)); - wrap->qembed->show(); -} - -static int -suil_qt_wrapper_resize(LV2UI_Feature_Handle handle, int width, int height) -{ - gtk_widget_set_size_request(GTK_WIDGET(handle), width, height); - - return 0; -} - -static gboolean -suil_qt_wrapper_idle(void* data) -{ - SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(data); - - if (wrap->idle_iface) { - wrap->idle_iface->idle(wrap->instance->handle); - return TRUE; // Continue calling - } - - return FALSE; -} - -static int -wrapper_wrap(SuilWrapper* wrapper, - SuilInstance* instance) -{ - SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl); - - wrap->qembed = new QWidget(); - wrap->wrapper = wrapper; - wrap->instance = instance; - - QWidget* qwidget = (QWidget*)instance->ui_widget; - QVBoxLayout* layout = new QVBoxLayout(); - layout->setMargin(0); - layout->setSpacing(0); - layout->addWidget(qwidget); - - wrap->qembed->setLayout(layout); - - g_signal_connect_after(G_OBJECT(wrap), "realize", - G_CALLBACK(suil_qt_wrapper_realize), NULL); - - instance->host_widget = GTK_WIDGET(wrap); - - const LV2UI_Idle_Interface* idle_iface = NULL; - if (instance->descriptor->extension_data) { - idle_iface = (const LV2UI_Idle_Interface*) - instance->descriptor->extension_data(LV2_UI__idleInterface); - } - - if (idle_iface) { - wrap->idle_iface = idle_iface; - wrap->idle_id = g_timeout_add( - wrap->idle_ms, suil_qt_wrapper_idle, wrap); - } - - return 0; -} - -static void -wrapper_free(SuilWrapper* wrapper) -{ - if (wrapper->impl) { - SuilQtWrapper* const wrap = SUIL_QT_WRAPPER(wrapper->impl); - gtk_object_destroy(GTK_OBJECT(wrap)); - } -} - -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) -{ - SuilWrapper* wrapper = (SuilWrapper*)calloc(1, sizeof(SuilWrapper)); - wrapper->wrap = wrapper_wrap; - wrapper->free = wrapper_free; - - SuilQtWrapper* const wrap = SUIL_QT_WRAPPER( - g_object_new(SUIL_TYPE_QT_WRAPPER, NULL)); - - wrap->wrapper = NULL; - wrapper->impl = wrap; - - wrapper->resize.handle = wrap; - wrapper->resize.ui_resize = suil_qt_wrapper_resize; - - suil_add_feature(features, &n_features, LV2_UI__resize, &wrapper->resize); - suil_add_feature(features, &n_features, LV2_UI__idleInterface, NULL); - - // Scan for URID map and options - LV2_URID_Map* map = NULL; - LV2_Options_Option* options = NULL; - for (LV2_Feature** f = *features; *f && (!map || !options); ++f) { - if (!strcmp((*f)->URI, LV2_OPTIONS__options)) { - options = (LV2_Options_Option*)(*f)->data; - } else if (!strcmp((*f)->URI, LV2_URID__map)) { - map = (LV2_URID_Map*)(*f)->data; - } - } - - if (map && options) { - // Set UI update rate if given - LV2_URID ui_updateRate = map->map(map->handle, LV2_UI__updateRate); - for (LV2_Options_Option* o = options; o->key; ++o) { - if (o->key == ui_updateRate) { - wrap->idle_ms = 1000.0f / *(const float*)o->value; - break; - } - } - } - - return wrapper; -} - -} // extern "C" diff --git a/wscript b/wscript index 0823980..f02a181 100644 --- a/wscript +++ b/wscript @@ -118,6 +118,9 @@ def configure(conf): if conf.env.HAVE_GTK3 and conf.env.HAVE_GTK3_X11: autowaf.define(conf, 'SUIL_WITH_X11_IN_GTK3', 1) + if conf.env.HAVE_GTK3 and conf.env.HAVE_QT5: + autowaf.define(conf, 'SUIL_WITH_QT5_IN_GTK3', 1) + if conf.env.HAVE_GTK2 and conf.env.HAVE_GTK2_QUARTZ: autowaf.define(conf, 'SUIL_WITH_COCOA_IN_GTK2', 1) @@ -174,6 +177,7 @@ def configure(conf): ('win', 'gtk2'), ('x11', 'gtk2'), ('x11', 'gtk3'), + ('qt5', 'gtk3'), ('x11', 'qt4'), ('x11', 'qt5'), ('cocoa', 'qt5')] @@ -271,7 +275,7 @@ def build(bld): if bld.env.SUIL_WITH_QT5_IN_GTK2: obj = bld(features = 'cxx cxxshlib', - source = 'src/qt5_in_gtk2.cpp', + source = 'src/qt5_in_gtk.cpp', target = 'suil_qt5_in_gtk2', includes = ['.'], defines = ['SUIL_SHARED', 'SUIL_INTERNAL'], @@ -305,6 +309,18 @@ def build(bld): uselib = 'GTK3 GTK3_X11 LV2', linkflags = bld.env.NODELETE_FLAGS) + if bld.env.SUIL_WITH_QT5_IN_GTK3: + obj = bld(features = 'cxx cxxshlib', + source = 'src/qt5_in_gtk.cpp', + target = 'suil_qt5_in_gtk3', + includes = ['.'], + defines = ['SUIL_SHARED', 'SUIL_INTERNAL'], + install_path = module_dir, + cflags = cflags, + lib = modlib, + uselib = 'GTK3 QT5 LV2', + linkflags = bld.env.NODELETE_FLAGS) + if bld.env.SUIL_WITH_COCOA_IN_GTK2: obj = bld(features = 'cxx cshlib', source = 'src/cocoa_in_gtk2.mm', -- cgit v1.2.1