From 9e9cf2fbcafc4fe673e7bc65418b401671a4be88 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 3 Oct 2017 17:36:10 +0200 Subject: Add support for Cocoa in Qt5 --- NEWS | 6 ++ src/cocoa_in_qt5.mm | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/instance.c | 10 +++- wscript | 19 +++++- 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/cocoa_in_qt5.mm diff --git a/NEWS b/NEWS index 5ce7774..1e34284 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +suil (0.10.1) unstable; + + * Add support for Cocoa in Qt5 + + -- David Robillard Tue, 03 Oct 2017 22:11:49 +0200 + suil (0.10.0) stable; * Add support for X11 in Gtk3 diff --git a/src/cocoa_in_qt5.mm b/src/cocoa_in_qt5.mm new file mode 100644 index 0000000..4259075 --- /dev/null +++ b/src/cocoa_in_qt5.mm @@ -0,0 +1,166 @@ +/* + Copyright 2017 David Robillard + + 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. +*/ + +#import + +#include +#include +#include +#include + +#undef signals + +#include "./suil_config.h" +#include "./suil_internal.h" + +extern "C" { + +typedef struct { + QWidget* host_widget; + QWidget* parent; +} SuilCocoaInQt5Wrapper; + +class SuilQCocoaWidget : public QMacCocoaViewContainer +{ +public: + SuilQCocoaWidget(NSView* view, QWidget* parent) + : QMacCocoaViewContainer(view, parent) + , _instance(NULL) + , _idle_iface(NULL) + , _ui_timer(0) + { + } + + void start_idle(SuilInstance* instance, + const LV2UI_Idle_Interface* idle_iface) { + _instance = instance; + _idle_iface = idle_iface; + NSView* view = (NSView*)instance->ui_widget; + setCocoaView((NSView*)instance->ui_widget); + setMinimumWidth([view fittingSize].width); + setMinimumHeight([view fittingSize].height); + 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); + } + + void closeEvent(QCloseEvent* event) { + if (_ui_timer && _idle_iface) { + this->killTimer(_ui_timer); + _ui_timer = 0; + } + QWidget::closeEvent(event); + } + +private: + SuilInstance* _instance; + const LV2UI_Idle_Interface* _idle_iface; + int _ui_timer; +}; + +static void +wrapper_free(SuilWrapper* wrapper) +{ + SuilCocoaInQt5Wrapper* impl = (SuilCocoaInQt5Wrapper*)wrapper->impl; + + if (impl->host_widget) { + delete impl->host_widget; + } + + free(impl); +} + +static int +wrapper_wrap(SuilWrapper* wrapper, + SuilInstance* instance) +{ + SuilCocoaInQt5Wrapper* const impl = (SuilCocoaInQt5Wrapper*)wrapper->impl; + SuilQCocoaWidget* const ew = (SuilQCocoaWidget*)impl->parent; + + 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); + } + + impl->host_widget = ew; + + instance->host_widget = impl->host_widget; + + return 0; +} + +static int +wrapper_resize(LV2UI_Feature_Handle handle, int width, int height) +{ + SuilQCocoaWidget* const ew = (SuilQCocoaWidget*)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) +{ + QWidget* parent = NULL; + for (unsigned i = 0; i < n_features; ++i) { + if (!strcmp((*features)[i]->URI, LV2_UI__parent)) { + parent = (QWidget*)(*features)[i]->data; + } + } + + if (!parent) { + SUIL_ERRORF("No QWidget parent given for %s UI\n", ui_type_uri); + return NULL; + } + + SuilCocoaInQt5Wrapper* const impl = (SuilCocoaInQt5Wrapper*) + calloc(1, sizeof(SuilCocoaInQt5Wrapper)); + + SuilWrapper* wrapper = (SuilWrapper*)malloc(sizeof(SuilWrapper)); + wrapper->wrap = wrapper_wrap; + wrapper->free = wrapper_free; + NSView* view = [NSView new]; + + SuilQCocoaWidget* const ew = new SuilQCocoaWidget(view, parent); + + 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, ew->cocoaView()); + suil_add_feature(features, &n_features, LV2_UI__resize, &wrapper->resize); + suil_add_feature(features, &n_features, LV2_UI__idleInterface, NULL); + + return wrapper; +} + +} // extern "C" diff --git a/src/instance.c b/src/instance.c index 42ea7b8..d365ca2 100644 --- a/src/instance.c +++ b/src/instance.c @@ -61,7 +61,9 @@ suil_ui_supported(const char* host_type_uri, || (!strcmp(host_type_uri, QT4_UI_URI) && !strcmp(ui_type_uri, X11_UI_URI)) || (!strcmp(host_type_uri, QT5_UI_URI) - && !strcmp(ui_type_uri, X11_UI_URI))) { + && !strcmp(ui_type_uri, X11_UI_URI)) + || (!strcmp(host_type_uri, QT5_UI_URI) + && !strcmp(ui_type_uri, COCOA_UI_URI))) { return SUIL_WRAPPING_EMBEDDED; } else { return SUIL_WRAPPING_UNSUPPORTED; @@ -136,6 +138,12 @@ open_wrapper(SuilHost* host, module_name = "suil_x11_in_qt5"; } #endif +#ifdef SUIL_WITH_COCOA_IN_QT5 + if (!strcmp(container_type_uri, QT5_UI_URI) + && !strcmp(ui_type_uri, COCOA_UI_URI)) { + module_name = "suil_cocoa_in_qt5"; + } +#endif if (!module_name) { SUIL_ERRORF("Unable to wrap UI type <%s> as type <%s>\n", diff --git a/wscript b/wscript index 5420333..279265b 100644 --- a/wscript +++ b/wscript @@ -92,6 +92,10 @@ def configure(conf): if not conf.options.no_qt5: autowaf.check_pkg(conf, 'Qt5Widgets', uselib_store='QT5', atleast_version='5.1.0', mandatory=False) + if conf.check_cxx(header_name = 'QMacCocoaViewContainer', + uselib = 'QT5', + mandatory = False): + autowaf.define(conf, 'SUIL_WITH_COCOA_IN_QT5', 1) conf.check_cc(define_name = 'HAVE_LIBDL', lib = 'dl', @@ -174,7 +178,8 @@ def configure(conf): ('x11', 'gtk2'), ('x11', 'gtk3'), ('x11', 'qt4'), - ('x11', 'qt5')] + ('x11', 'qt5'), + ('cocoa', 'qt5')] for w in wrappers: var = 'SUIL_WITH_%s_IN_%s' % (w[0].upper(), w[1].upper()) autowaf.display_msg(conf, 'Support for %s in %s' % (w[0], w[1]), @@ -351,6 +356,18 @@ def build(bld): lib = modlib) autowaf.use_lib(bld, obj, 'QT5 LV2') + if bld.env.SUIL_WITH_COCOA_IN_QT5: + obj = bld(features = 'cxx cxxshlib', + source = 'src/cocoa_in_qt5.mm', + target = 'suil_cocoa_in_qt5', + includes = ['.'], + defines = ['SUIL_SHARED', 'SUIL_INTERNAL'], + install_path = module_dir, + cflags = cflags, + lib = modlib, + linkflags = ['-framework', 'Cocoa']) + autowaf.use_lib(bld, obj, 'QT5 QT5_MAC_EXTRAS LV2') + if bld.env.SUIL_WITH_X11: obj = bld(features = 'c cshlib', source = 'src/x11.c', -- cgit v1.2.1