diff --git a/dgl/ntk/NtkApp.hpp b/dgl/ntk/NtkApp.hpp index 5dc7b2d4..2c32d56b 100644 --- a/dgl/ntk/NtkApp.hpp +++ b/dgl/ntk/NtkApp.hpp @@ -18,6 +18,8 @@ #define DGL_NTK_APP_HPP_INCLUDED #include "../Base.hpp" +#include "../../distrho/DistrhoUI.hpp" +#include "../../distrho/extra/d_thread.hpp" #ifdef override # define override_defined @@ -35,36 +37,65 @@ # undef override_defined #endif +struct ScopedDisplayLock { + ScopedDisplayLock() + { +#ifdef DISTRHO_OS_LINUX + XLockDisplay(fl_display); +#endif + } + + ~ScopedDisplayLock() + { +#ifdef DISTRHO_OS_LINUX + XUnlockDisplay(fl_display); +#endif + } +}; + +// ----------------------------------------------------------------------- + +namespace DISTRHO_NAMESPACE { + class UI; +} + START_NAMESPACE_DGL class NtkWindow; +typedef DISTRHO_NAMESPACE::Mutex d_Mutex; +typedef DISTRHO_NAMESPACE::MutexLocker d_MutexLocker; +typedef DISTRHO_NAMESPACE::Thread d_Thread; +typedef DISTRHO_NAMESPACE::UI d_UI; + // ----------------------------------------------------------------------- /** DGL compatible App class that uses NTK instead of OpenGL. @see App */ -class NtkApp +class NtkApp : d_Thread { public: /** Constructor. */ NtkApp() - : fIsRunning(true), - fWindows() + : d_Thread("NtkApp"), + fWindows(), + fWindowMutex(), + fNextUI(), + fDoNextUI(false), + fInitialized(false) { - static bool initialized = false; - - if (! initialized) - { - initialized = true; - fl_register_images(); #ifdef DISTRHO_OS_LINUX - fl_open_display(); + //XInitThreads(); #endif - } + + startThread(); + + for (; ! fInitialized;) + d_msleep(10); } /** @@ -72,20 +103,15 @@ public: */ ~NtkApp() { - DISTRHO_SAFE_ASSERT(! fIsRunning); - + stopThread(-1); fWindows.clear(); } /** Idle function. - This calls the NTK event-loop once (and all idle callbacks). + This calls does nothing. */ - void idle() - { - Fl::check(); - Fl::flush(); - } + void idle() {} /** Run the application event-loop until all Windows are closed. @@ -93,9 +119,8 @@ public: */ void exec() { - fIsRunning = true; - Fl::run(); - fIsRunning = false; + while (isThreadRunning() && ! shouldThreadExit()) + d_sleep(1); } /** @@ -104,13 +129,7 @@ public: */ void quit() { - fIsRunning = false; - - for (std::list::reverse_iterator rit = fWindows.rbegin(), rite = fWindows.rend(); rit != rite; ++rit) - { - Fl_Double_Window* const window(*rit); - window->hide(); - } + signalThreadShouldExit(); } /** @@ -119,23 +138,91 @@ public: */ bool isQuiting() const noexcept { - return !fIsRunning; + if (isThreadRunning() && ! shouldThreadExit()) + return false; + return true; + } + + // ------------------------------------------------------------------- + + /** + Create UI on our separate thread. + Blocks until the UI is created and returns it. + */ + d_UI* createUI(void* const func) + { + DISTRHO_SAFE_ASSERT_RETURN(isThreadRunning(), nullptr); + DISTRHO_SAFE_ASSERT_RETURN(! fDoNextUI, nullptr); + + fNextUI.create = true; + fNextUI.func = (NextUI::UiFunc)func; + fDoNextUI = true; + + for (; fDoNextUI;) + d_msleep(10); + + return fNextUI.ui; + } + + /** + Delete UI on our separate thread. + Blocks until the UI is deleted. + */ + void deleteUI(d_UI* const ui) + { + DISTRHO_SAFE_ASSERT_RETURN(! fDoNextUI,); + + fNextUI.create = false; + fNextUI.ui = ui; + fDoNextUI = true; + + if (isThreadRunning()) + { + for (; fDoNextUI;) + d_msleep(10); + } + else + { + fNextUI.run(); + fDoNextUI = false; + } } + // ------------------------------------------------------------------- + private: - bool fIsRunning; - std::list fWindows; + struct NextUI { + typedef d_UI* (*UiFunc)(); - friend class NtkWindow; + bool create; + + union { + UiFunc func; + d_UI* ui; + }; + + NextUI() + : create(false), + func(nullptr) {} + + void run(); + }; + + std::list fWindows; + d_Mutex fWindowMutex; + NextUI fNextUI; + volatile bool fDoNextUI; + volatile bool fInitialized; /** @internal used by NtkWindow. */ void addWindow(Fl_Double_Window* const window) { DISTRHO_SAFE_ASSERT_RETURN(window != nullptr,); - if (fWindows.size() == 0) - fIsRunning = true; + if (fWindows.size() == 0 && ! isThreadRunning()) + startThread(); + const d_MutexLocker sl(fWindowMutex); fWindows.push_back(window); } @@ -144,15 +231,59 @@ private: { DISTRHO_SAFE_ASSERT_RETURN(window != nullptr,); + const d_MutexLocker sl(fWindowMutex); fWindows.remove(window); if (fWindows.size() == 0) - fIsRunning = false; + signalThreadShouldExit(); + } + + /** @internal */ + void run() override + { + static bool initialized = false; + + if (! initialized) + { + initialized = true; + fl_register_images(); +#ifdef DISTRHO_OS_LINUX + fl_open_display(); +#endif + } + + fInitialized = true; + + for (; ! shouldThreadExit();) + { + if (fDoNextUI) + { + const ScopedDisplayLock csdl; + fNextUI.run(); + fDoNextUI = false; + } + + const ScopedDisplayLock csdl; + Fl::check(); + Fl::flush(); + + d_msleep(20); + } + + const d_MutexLocker sl(fWindowMutex); + const ScopedDisplayLock csdl; + + for (std::list::reverse_iterator rit = fWindows.rbegin(), rite = fWindows.rend(); rit != rite; ++rit) + { + Fl_Double_Window* const window(*rit); + window->hide(); + } } + friend class NtkWindow; + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NtkApp) }; - // ----------------------------------------------------------------------- END_NAMESPACE_DGL diff --git a/distrho/DistrhoUIMain.cpp b/distrho/DistrhoUIMain.cpp index cb1cb6e7..3bc09713 100644 --- a/distrho/DistrhoUIMain.cpp +++ b/distrho/DistrhoUIMain.cpp @@ -27,3 +27,28 @@ #elif defined(DISTRHO_PLUGIN_TARGET_VST) // nothing #endif + +#ifdef DGL_NTK_APP_HPP_INCLUDED + +START_NAMESPACE_DGL + +void NtkApp::NextUI::run() +{ + if (create) + { + d_stdout("Creating NTK UI in separate thread..."); + d_UI* const ui2 = (func)(); + ui = ui2; + } + else + { + d_stdout("Destroying NTK UI in separate thread..."); + d_UI* const ui2 = ui; + ui = nullptr; + delete ui2; + } +} + +END_NAMESPACE_DGL + +#endif diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp index e5b53762..6bd421c0 100644 --- a/distrho/src/DistrhoUIInternal.hpp +++ b/distrho/src/DistrhoUIInternal.hpp @@ -141,7 +141,11 @@ UI* createUiWrapper(void* const dspPtr, UIWindow* const window) { d_lastUiDspPtr = dspPtr; d_lastUiWindow = window; +#if DISTRHO_UI_USE_NTK + UI* const ret = window->getApp().createUI((void*)createUI); +#else UI* const ret = createUI(); +#endif d_lastUiDspPtr = nullptr; d_lastUiWindow = nullptr; return ret; @@ -164,7 +168,11 @@ public: ~UIExporterWindow() { +#if DISTRHO_UI_USE_NTK + getApp().deleteUI(fUI); +#else delete fUI; +#endif } UI* getUI() const noexcept