|
|
@@ -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<Fl_Double_Window*>::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<Fl_Double_Window*> 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<Fl_Double_Window*> 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<Fl_Double_Window*>::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 |
|
|
|