diff --git a/source/Makefile.mk b/source/Makefile.mk index c8abb88a2..6a7a7d7c6 100644 --- a/source/Makefile.mk +++ b/source/Makefile.mk @@ -133,6 +133,7 @@ endif ifeq ($(MACOS),true) CFLAGS += -isystem /opt/kxstudio/include CXXFLAGS += -isystem /opt/kxstudio/include +CXXFLAGS += -isystem /System/Library/Frameworks endif ifeq ($(WIN64),true) CFLAGS += -isystem /opt/mingw64/include diff --git a/source/bridges-ui/CarlaBridgeToolkitPlugin.cpp b/source/bridges-ui/CarlaBridgeToolkitPlugin.cpp index 65621a2e9..da16d3313 100644 --- a/source/bridges-ui/CarlaBridgeToolkitPlugin.cpp +++ b/source/bridges-ui/CarlaBridgeToolkitPlugin.cpp @@ -20,6 +20,12 @@ #include "CarlaPluginUI.hpp" +#if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC) +# include "juce_events.h" +using juce::MessageManager; +using juce::ScopedJuceInitialiser_GUI; +#endif + CARLA_BRIDGE_START_NAMESPACE // ------------------------------------------------------------------------- @@ -79,7 +85,12 @@ public: if (! kClient->oscIdle()) break; +#if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC) + if (MessageManager* const msgMgr = MessageManager::getInstance()) + msgMgr->runDispatchLoopUntil(20); +#else carla_msleep(20); +#endif } } @@ -154,6 +165,10 @@ private: CarlaPluginUI* fUI; bool fIdling; +#if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC) + const ScopedJuceInitialiser_GUI kJuceInit; +#endif + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaBridgeToolkitPlugin) }; @@ -168,6 +183,7 @@ CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const clien CARLA_BRIDGE_END_NAMESPACE +#define CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS #include "CarlaPluginUI.cpp" // ------------------------------------------------------------------------- diff --git a/source/bridges-ui/CarlaBridgeUI-LV2.cpp b/source/bridges-ui/CarlaBridgeUI-LV2.cpp index 7c7df9fc5..94de404a2 100644 --- a/source/bridges-ui/CarlaBridgeUI-LV2.cpp +++ b/source/bridges-ui/CarlaBridgeUI-LV2.cpp @@ -1236,7 +1236,7 @@ int main(int argc, char* argv[]) if (argc != 6) { - carla_stderr("usage: %s ", argv[0]); + carla_stderr("usage: %s ", argv[0]); return 1; } diff --git a/source/bridges-ui/Makefile b/source/bridges-ui/Makefile index 69bab8da6..bbbad8178 100644 --- a/source/bridges-ui/Makefile +++ b/source/bridges-ui/Makefile @@ -40,7 +40,7 @@ BUILD_LV2_X11_FLAGS = $(BUILD_LV2_FLAGS) -DBRIDGE_X11 -DBRIDGE_LV2_X11 $(X11 LINK_LV2_X11_FLAGS = $(LINK_FLAGS) $(X11_LIBS) -ldl BUILD_LV2_COCOA_FLAGS = $(BUILD_LV2_FLAGS) -DBRIDGE_COCOA -DBRIDGE_LV2_COCOA -LINK_LV2_COCOA_FLAGS = $(LINK_FLAGS) -ldl +LINK_LV2_COCOA_FLAGS = $(LINK_FLAGS) -framework Cocoa -framework IOKit -framework QuartzCore -ldl BUILD_LV2_WINDOWS_FLAGS = $(BUILD_LV2_FLAGS) -DBRIDGE_HWND -DBRIDGE_LV2_HWND LINK_LV2_WINDOWS_FLAGS = $(LINK_FLAGS) -static -mwindows @@ -72,13 +72,11 @@ TARGETS += ui_vst-x11 endif ifeq ($(MACOS),true) -# TODO -# TARGETS += ui_lv2-cocoa +TARGETS += ui_lv2-cocoa endif ifeq ($(WIN32),true) -# TODO -# TARGETS += ui_lv2-windows +TARGETS += ui_lv2-windows endif # -------------------------------------------------------------- @@ -101,6 +99,18 @@ OBJS_LV2_LIBS = \ ../modules/juce_core.a \ ../modules/lilv.a +OBJS_LV2_LIBS_JUCE_UI = \ + $(OBJS_LV2_LIBS) \ + ../modules/juce_data_structures.a \ + ../modules/juce_events.a \ + ../modules/juce_graphics.a \ + ../modules/juce_gui_basics.a + +ifeq ($(MACOS),true) +OBJS_LV2_LIBS_JUCE_UI += \ + ../modules/juce_gui_extra.a +endif + OBJS_VST_LIBS = \ ../modules/juce_core.a @@ -206,11 +216,11 @@ OBJS_LV2_COCOA = \ CarlaBridgeToolkitPlugin__lv2-cocoa.o \ CarlaBridgeUI-LV2__lv2-cocoa.o -../../bin/carla-bridge-lv2-cocoa: $(OBJS_LV2_COCOA) $(OBJS_LV2_LIBS) +../../bin/carla-bridge-lv2-cocoa: $(OBJS_LV2_COCOA) $(OBJS_LV2_LIBS_JUCE_UI) $(CXX) $^ $(LINK_LV2_COCOA_FLAGS) -o $@ %__lv2-cocoa.o: %.cpp - $(CXX) $< $(BUILD_LV2_COCOA_FLAGS) -c -o $@ + $(CXX) $< $(BUILD_LV2_COCOA_FLAGS) -ObjC++ -c -o $@ # -------------------------------------------------------------- # ui_lv2-windows @@ -221,7 +231,7 @@ OBJS_LV2_WINDOWS = \ CarlaBridgeToolkitPlugin__lv2-windows.o \ CarlaBridgeUI-LV2__lv2-windows.o -../../bin/carla-bridge-lv2-windows.exe: $(OBJS_LV2_WINDOWS) $(OBJS_LV2_LIBS) +../../bin/carla-bridge-lv2-windows.exe: $(OBJS_LV2_WINDOWS) $(OBJS_LV2_LIBS_JUCE_UI) $(CXX) $^ $(LINK_LV2_WINDOWS_FLAGS) -o $@ %__lv2-windows.o: %.cpp diff --git a/source/utils/CarlaPluginUI.cpp b/source/utils/CarlaPluginUI.cpp index 3d2205428..2c85e9b1a 100644 --- a/source/utils/CarlaPluginUI.cpp +++ b/source/utils/CarlaPluginUI.cpp @@ -21,8 +21,10 @@ #if defined(CARLA_OS_WIN) || defined(CARLA_OS_MAC) # include "juce_gui_basics.h" using juce::Colour; +using juce::Colours; using juce::ComponentPeer; using juce::DocumentWindow; +using juce::Graphics; #endif #ifdef HAVE_X11 @@ -32,6 +34,92 @@ using juce::DocumentWindow; # include #endif +// ----------------------------------------------------- +// AutoResizingNSViewComponentWithParent, see juce_audio_processors.cpp + +#ifdef CARLA_OS_MAC +# include "juce_gui_extra.h" + +# ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS +# include "juce_core/native/juce_BasicNativeHeaders.h" +# else +struct NSView; +# endif + +namespace juce { +//============================================================================== +struct AutoResizingNSViewComponent : public NSViewComponent, + private AsyncUpdater { + AutoResizingNSViewComponent(); + void childBoundsChanged(Component*) override; + void handleAsyncUpdate() override; + bool recursive; +}; + +struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, + private Timer { + AutoResizingNSViewComponentWithParent(); + NSView* getChildView() const; + void timerCallback() override; +}; + +//============================================================================== +# ifdef CARLA_PLUGIN_UI_WITHOUT_JUCE_PROCESSORS +# include "juce_core/native/juce_BasicNativeHeaders.h" + +AutoResizingNSViewComponent::AutoResizingNSViewComponent() + : recursive (false) {} + +void AutoResizingNSViewComponent::childBoundsChanged(Component*) override +{ + if (recursive) + { + triggerAsyncUpdate(); + } + else + { + recursive = true; + resizeToFitView(); + recursive = true; + } +} + +void AutoResizingNSViewComponent::handleAsyncUpdate() override +{ + resizeToFitView(); +} + +AutoResizingNSViewComponentWithParent::AutoResizingNSViewComponentWithParent() +{ + NSView* v = [[NSView alloc] init]; + setView (v); + [v release]; + + startTimer(500); +} + +NSView* AutoResizingNSViewComponentWithParent::getChildView() const +{ + if (NSView* parent = (NSView*)getView()) + if ([[parent subviews] count] > 0) + return [[parent subviews] objectAtIndex: 0]; + + return nil; +} + +void AutoResizingNSViewComponentWithParent::timerCallback() override +{ + if (NSView* child = getChildView()) + { + stopTimer(); + setView(child); + } +} +#endif +} // namespace juce +using juce::AutoResizingNSViewComponentWithParent; +#endif + // ----------------------------------------------------- // JUCE @@ -44,6 +132,9 @@ public: : CarlaPluginUI(cb, false), DocumentWindow("JucePluginUI", Colour(50, 50, 200), DocumentWindow::closeButton, false), fClosed(false), +#ifdef CARLA_OS_MAC + fCocoaWrapper(), +#endif leakDetector_JucePluginUI() { setVisible(false); @@ -52,10 +143,23 @@ public: setResizable(false, false); setUsingNativeTitleBar(true); +#ifdef CARLA_OS_MAC + addAndMakeVisible(fCocoaWrapper = new AutoResizingNSViewComponentWithParent()); +#endif + addToDesktop(); } + ~JucePluginUI() override + { +#ifdef CARLA_OS_MAC + // deleted before window + fCocoaWrapper = nullptr; +#endif + } + protected: + // CarlaPluginUI calls void closeButtonPressed() override { fClosed = true; @@ -104,16 +208,64 @@ protected: void* getPtr() const noexcept override { +#ifdef CARLA_OS_MAC + return fCocoaWrapper->getView(); +#else if (ComponentPeer* const peer = getPeer()) return peer->getNativeHandle(); carla_stdout("getPtr() failed"); return nullptr; +#endif + } + +#ifdef CARLA_OS_MAC + // JUCE MacOS calls + void childBoundsChanged(Component*) override + { + if (fCocoaWrapper != nullptr) + { + const int w = fCocoaWrapper->getWidth(); + const int h = fCocoaWrapper->getHeight(); + + if (w != DocumentWindow::getWidth() || h != DocumentWindow::getHeight()) + DocumentWindow::setSize(w, h); + } } + void resized() override + { + if (fCocoaWrapper != nullptr) + fCocoaWrapper->setSize(DocumentWindow::getWidth(), DocumentWindow::getHeight()); + } +#endif + +#ifdef CARLA_OS_WINDOWS + // JUCE Windows calls + void mouseDown(const MouseEvent&) override + { + DocumentWindow::toFront(true); + } +#endif + + void paint(Graphics& g) override + { + g.fillAll(Colours::black); + } + +#if 0 + //============================================================================== + bool keyStateChanged (bool) override { return pluginWantsKeys; } + bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; } +#endif + private: volatile bool fClosed; +#ifdef CARLA_OS_MAC + juce::ScopedPointer fCocoaWrapper; +#endif + CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePluginUI) }; #endif