@@ -1,62 +0,0 @@ | |||
#!/bin/bash | |||
set -e | |||
cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.cpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.hpp /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/distrho/*.h /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/include /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/src /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/AUTHORS /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v /Shared/Personal/FOSS/GIT/DISTRHO/DPF/dgl/src/pugl-upstream/COPYING /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/pugl-upstream/ | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Cairo.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/Vulkan.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Cairo.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Vulkan.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/dgl/src/Resources.{cpp,hpp} | |||
# rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/*.mm | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/jackbridge | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/travesty | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/vst | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/DistrhoInfo.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPlugin{JACK,LADSPA+DSSI,LV2,LV2export,VST2,VST3}.cpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoPluginVST3.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/src/DistrhoUI{DSSI,LV2,VST3}.cpp | |||
rm -r /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/sofd | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/FileBrowserDialog.{cpp,hpp} | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/LibraryUtils.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/RingBuffer.hpp | |||
rm /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/distrho/extra/ScopedSafeLocale.hpp | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandEQ/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandeq/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/3BandSplitter/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/3bandsplitter/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_mini-series/plugins/PingPongPan/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/pingpongpan/ | |||
# | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_nekobi/plugins/Nekobi/nekobee-src/{*.c,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/nekobi/nekobee-src/ | |||
# | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO_prom/plugins/ProM/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/prom/ | |||
# | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/GrooveJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/groovejuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/PowerJuiceX2/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/powerjuicex2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/SegmentJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/segmentjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/StutterJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/stutterjuice/ | |||
# # cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/TriggerJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/triggerjuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/VectorJuice/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/vectorjuice/ | |||
# cp -v /Shared/Personal/FOSS/GIT/DISTRHO/JuicePlugins/plugins/WobbleJuice/{*.cpp,*.hpp,*.hxx,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/wobblejuice/ | |||
# | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcomp/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamcompx2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamEQ2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zameq2/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamSynth/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamsynth/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZamTube/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamtube/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiComp/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticomp/ | |||
# cp -v /Shared/Personal/FOSS/GIT/zam-plugins-DPF/plugins/ZaMultiCompX2/{*.cpp,*.hpp,*.h} /Shared/Personal/FOSS/GIT/falkTX/Carla/source/modules/native-plugins/zamulticompx2/ |
@@ -0,0 +1,51 @@ | |||
#!/bin/bash | |||
set -e | |||
cd $(dirname ${0}) | |||
cd .. | |||
CARLA_DIR=$(pwd) | |||
DPF_DIR=/tmp/dpf-carla | |||
rm -rf ${DPF_DIR} | |||
git clone git@github.com:DISTRHO/DPF.git ${DPF_DIR} --depth=1 --recursive -b develop | |||
cp -v ${DPF_DIR}/dgl/*.hpp ${CARLA_DIR}/source/modules/dgl/ | |||
cp -v ${DPF_DIR}/dgl/src/*.cpp ${CARLA_DIR}/source/modules/dgl/src/ | |||
cp -v ${DPF_DIR}/dgl/src/*.hpp ${CARLA_DIR}/source/modules/dgl/src/ | |||
cp -v ${DPF_DIR}/distrho/*.cpp ${CARLA_DIR}/source/modules/distrho/ | |||
cp -v ${DPF_DIR}/distrho/*.hpp ${CARLA_DIR}/source/modules/distrho/ | |||
cp -v ${DPF_DIR}/distrho/src/*.cpp ${CARLA_DIR}/source/modules/distrho/src/ | |||
cp -v ${DPF_DIR}/distrho/src/*.hpp ${CARLA_DIR}/source/modules/distrho/src/ | |||
cp -v ${DPF_DIR}/distrho/extra/LeakDetector.hpp ${CARLA_DIR}/source/modules/distrho/extra/ | |||
cp -v ${DPF_DIR}/distrho/extra/ScopedPointer.hpp ${CARLA_DIR}/source/modules/distrho/extra/ | |||
cp -v ${DPF_DIR}/distrho/extra/ScopedSafeLocale.hpp ${CARLA_DIR}/source/modules/distrho/extra/ | |||
cp -v ${DPF_DIR}/distrho/extra/String.hpp ${CARLA_DIR}/source/modules/distrho/extra/ | |||
cp -r -v ${DPF_DIR}/dgl/src/pugl-upstream/include ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v ${DPF_DIR}/dgl/src/pugl-upstream/src ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v ${DPF_DIR}/dgl/src/pugl-upstream/AUTHORS ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/ | |||
cp -r -v ${DPF_DIR}/dgl/src/pugl-upstream/COPYING ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/ | |||
rm ${CARLA_DIR}/source/modules/dgl/Cairo.hpp | |||
rm ${CARLA_DIR}/source/modules/dgl/FileBrowserDialog.hpp | |||
rm ${CARLA_DIR}/source/modules/dgl/Vulkan.hpp | |||
rm ${CARLA_DIR}/source/modules/dgl/src/Cairo.cpp | |||
rm ${CARLA_DIR}/source/modules/dgl/src/Vulkan.cpp | |||
rm ${CARLA_DIR}/source/modules/dgl/src/Resources.{cpp,hpp} | |||
rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/src/.clang-tidy | |||
rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/include/.clang-tidy | |||
rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/include/meson.build | |||
rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/include/pugl/cairo.h | |||
rm ${CARLA_DIR}/source/modules/dgl/src/pugl-upstream/include/pugl/vulkan.h | |||
rm ${CARLA_DIR}/source/modules/distrho/DistrhoInfo.hpp | |||
rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPlugin{JACK,LADSPA+DSSI,LV2,LV2export,VST2,VST3}.cpp | |||
rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoPluginVST3.hpp | |||
rm ${CARLA_DIR}/source/modules/distrho/src/DistrhoUI{DSSI,LV2,VST3}.cpp | |||
rm ${CARLA_DIR}/source/modules/distrho/DistrhoStandaloneUtils.hpp | |||
rm -rf ${DPF_DIR} |
@@ -109,7 +109,7 @@ public: | |||
void removeIdleCallback(IdleCallback* callback); | |||
/** | |||
Set the class name of the application. | |||
Get the class name of the application. | |||
This is a stable identifier for the application, used as the window class/instance name on X11 and Windows. | |||
It is not displayed to the user, but can be used in scripts and by window managers, | |||
@@ -117,6 +117,12 @@ public: | |||
Plugins created with DPF have their class name automatically set based on DGL_NAMESPACE and plugin name. | |||
*/ | |||
const char* getClassName() const noexcept; | |||
/** | |||
Set the class name of the application. | |||
@see getClassName | |||
*/ | |||
void setClassName(const char* name); | |||
private: | |||
@@ -45,6 +45,13 @@ static bool isThisTheMainThread(const d_ThreadHandle mainThreadHandle) noexcept | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
const char* Application::getClassName() const noexcept | |||
{ | |||
return puglGetClassName(pData->world); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
Application::PrivateData::PrivateData(const bool standalone) | |||
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE, | |||
standalone ? PUGL_WORLD_THREADS : 0x0)), | |||
@@ -109,7 +109,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s) | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
waitingForClipboard(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
@@ -137,7 +138,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
waitingForClipboard(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
@@ -167,7 +169,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
waitingForClipboard(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
@@ -198,7 +201,8 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
ignoreIdleCallbacks(false), | |||
waitingForClipboard(false), | |||
waitingForClipboardData(false), | |||
waitingForClipboardEvents(false), | |||
clipboardTypeId(0), | |||
filenameToRenderInto(nullptr), | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
@@ -261,10 +265,18 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo | |||
puglSetViewHint(view, PUGL_DEPTH_BITS, 16); | |||
#endif | |||
puglSetViewHint(view, PUGL_STENCIL_BITS, 8); | |||
#ifdef DGL_USE_OPENGL3 | |||
#if defined(DGL_USE_OPENGL3) || defined(DGL_USE_GLES3) | |||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | |||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3); | |||
#elif defined(DGL_USE_GLES2) | |||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE); | |||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
#else | |||
puglSetViewHint(view, PUGL_USE_COMPAT_PROFILE, PUGL_TRUE); | |||
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2); | |||
#endif | |||
// PUGL_SAMPLES ?? | |||
puglSetEventFunc(view, puglEventCallback); | |||
@@ -602,7 +614,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh | |||
void Window::PrivateData::onPuglExpose() | |||
{ | |||
DGL_DBGp("PUGL: onPuglExpose\n"); | |||
DGL_DBG("PUGL: onPuglExpose\n"); | |||
puglOnDisplayPrepare(view); | |||
@@ -761,36 +773,52 @@ void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) | |||
const void* Window::PrivateData::getClipboard(size_t& dataSize) | |||
{ | |||
clipboardTypeId = 0; | |||
waitingForClipboard = true; | |||
waitingForClipboardData = true, | |||
waitingForClipboardEvents = true; | |||
// begin clipboard dance here | |||
if (puglPaste(view) != PUGL_SUCCESS) | |||
{ | |||
dataSize = 0; | |||
waitingForClipboard = false; | |||
waitingForClipboardEvents = false; | |||
return nullptr; | |||
} | |||
// wait for type request | |||
while (waitingForClipboard && clipboardTypeId == 0) | |||
puglUpdate(appData->world, 0.03); | |||
#ifdef DGL_USING_X11 | |||
// wait for type request, clipboardTypeId must be != 0 to be valid | |||
int retry = static_cast<int>(2 / 0.03); | |||
while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0) | |||
{ | |||
if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |||
break; | |||
} | |||
#endif | |||
if (clipboardTypeId == 0) | |||
{ | |||
dataSize = 0; | |||
waitingForClipboard = false; | |||
waitingForClipboardEvents = false; | |||
return nullptr; | |||
} | |||
// wait for actual data | |||
while (waitingForClipboard) | |||
puglUpdate(appData->world, 0.03); | |||
#ifdef DGL_USING_X11 | |||
// wait for actual data (assumes offer was accepted) | |||
retry = static_cast<int>(2 / 0.03); | |||
while (waitingForClipboardData && --retry >= 0) | |||
{ | |||
if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) | |||
break; | |||
} | |||
#endif | |||
if (clipboardTypeId == 0) | |||
{ | |||
dataSize = 0; | |||
waitingForClipboardEvents = false; | |||
return nullptr; | |||
} | |||
waitingForClipboardEvents = false; | |||
return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); | |||
} | |||
@@ -801,7 +829,8 @@ uint32_t Window::PrivateData::onClipboardDataOffer() | |||
if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) | |||
return clipboardTypeId; | |||
waitingForClipboard = false; | |||
// stop waiting for data, it was rejected | |||
waitingForClipboardData = false; | |||
return 0; | |||
} | |||
@@ -810,7 +839,7 @@ void Window::PrivateData::onClipboardData(const uint32_t typeId) | |||
if (clipboardTypeId != typeId) | |||
clipboardTypeId = 0; | |||
waitingForClipboard = false; | |||
waitingForClipboardData = false; | |||
} | |||
#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) | |||
@@ -826,7 +855,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
} | |||
#endif | |||
if (pData->waitingForClipboard) | |||
if (pData->waitingForClipboardEvents) | |||
{ | |||
switch (event->type) | |||
{ | |||
@@ -843,8 +872,15 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
case PUGL_BUTTON_RELEASE: | |||
case PUGL_MOTION: | |||
case PUGL_SCROLL: | |||
case PUGL_TIMER: | |||
case PUGL_LOOP_ENTER: | |||
case PUGL_LOOP_LEAVE: | |||
return PUGL_SUCCESS; | |||
case PUGL_DATA_OFFER: | |||
case PUGL_DATA: | |||
break; | |||
default: | |||
d_stdout("Got event %d while waitingForClipboardEvents", event->type); | |||
break; | |||
} | |||
} | |||
@@ -857,10 +893,10 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu | |||
///< View created, a #PuglEventCreate | |||
case PUGL_CREATE: | |||
#if defined(HAVE_X11) && !defined(DISTRHO_OS_MAC) && !defined(DISTRHO_OS_WINDOWS) | |||
#ifdef DGL_USING_X11 | |||
if (! pData->isEmbed) | |||
puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); | |||
#endif | |||
#endif | |||
break; | |||
///< View destroyed, a #PuglEventDestroy | |||
@@ -78,7 +78,8 @@ struct Window::PrivateData : IdleCallback { | |||
bool ignoreIdleCallbacks; | |||
/** Whether we are waiting to receive clipboard data, ignoring some events in the process. */ | |||
bool waitingForClipboard; | |||
bool waitingForClipboardData; | |||
bool waitingForClipboardEvents; | |||
/** The type id returned by the last onClipboardDataOffer call. */ | |||
uint32_t clipboardTypeId; | |||
@@ -49,6 +49,20 @@ puglInitViewInternals(PuglWorld* const world) | |||
return impl; | |||
} | |||
static PuglStatus | |||
puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event) | |||
{ | |||
PuglStatus st0 = PUGL_SUCCESS; | |||
PuglStatus st1 = PUGL_SUCCESS; | |||
if (!(st0 = view->backend->enter(view, NULL))) { | |||
st0 = view->eventFunc(view, event); | |||
st1 = view->backend->leave(view, NULL); | |||
} | |||
return st0 ? st0 : st1; | |||
} | |||
static PuglKey | |||
keyCodeToSpecial(const unsigned long code, const unsigned long location) | |||
{ | |||
@@ -125,8 +139,12 @@ puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEve | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
if (keyEvent->repeat && view->hints[PUGL_IGNORE_KEY_REPEAT]) | |||
if (!view->visible) { | |||
return EM_FALSE; | |||
} | |||
if (keyEvent->repeat && view->hints[PUGL_IGNORE_KEY_REPEAT]) | |||
return EM_TRUE; | |||
PuglStatus st0 = PUGL_SUCCESS; | |||
PuglStatus st1 = PUGL_SUCCESS; | |||
@@ -138,6 +156,10 @@ puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEve | |||
const PuglKey special = keyCodeToSpecial(keyEvent->keyCode, keyEvent->location); | |||
uint key = keyEvent->keyCode; | |||
if (key >= 'A' && key <= 'Z' && !keyEvent->shiftKey) | |||
key += 'a' - 'A'; | |||
PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
event.key.type = eventType == EMSCRIPTEN_EVENT_KEYDOWN ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; | |||
event.key.time = keyEvent->timestamp / 1000; | |||
@@ -145,16 +167,16 @@ puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEve | |||
// event.key.y = xevent.xkey.y; | |||
// event.key.xRoot = xevent.xkey.x_root; | |||
// event.key.yRoot = xevent.xkey.y_root; | |||
event.key.key = special ? special : keyEvent->keyCode; | |||
event.key.key = special ? special : key; | |||
event.key.keycode = keyEvent->keyCode; | |||
event.key.state = state; | |||
st0 = puglDispatchEvent(view, &event); | |||
st0 = puglDispatchEventWithContext(view, &event); | |||
d_debug("key event \n" | |||
"\tdown: %d\n" | |||
"\trepeat: %d\n" | |||
"\tlocation: %d\n" | |||
"\tflags: 0x%x\n" | |||
"\tstate: 0x%x\n" | |||
"\tkey[]: '%s'\n" | |||
"\tcode[]: '%s'\n" | |||
"\tlocale[]: '%s'\n" | |||
@@ -172,7 +194,7 @@ puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEve | |||
keyEvent->which, keyEvent->which >= ' ' && keyEvent->which <= '~' ? keyEvent->which : 0, | |||
special); | |||
if (event.type == PUGL_KEY_PRESS && !special) { | |||
if (event.type == PUGL_KEY_PRESS && !special && !(keyEvent->ctrlKey|keyEvent->altKey|keyEvent->metaKey)) { | |||
char str[8] = PUGL_INIT_STRUCT; | |||
if (decodeCharacterString(keyEvent->keyCode, keyEvent->key, str)) { | |||
@@ -181,11 +203,11 @@ puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEve | |||
event.text.type = PUGL_TEXT; | |||
event.text.character = event.key.key; | |||
memcpy(event.text.string, str, sizeof(event.text.string)); | |||
st1 = puglDispatchEvent(view, &event); | |||
st1 = puglDispatchEventWithContext(view, &event); | |||
} | |||
} | |||
return (st0 ? st0 : st1) == PUGL_SUCCESS ? EM_FALSE : EM_TRUE; | |||
return (st0 ? st0 : st1) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; | |||
} | |||
static EM_BOOL | |||
@@ -193,6 +215,10 @@ puglMouseCallback(const int eventType, const EmscriptenMouseEvent* const mouseEv | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
if (!view->visible) { | |||
return EM_FALSE; | |||
} | |||
PuglEvent event = {{PUGL_NOTHING, 0}}; | |||
const double time = mouseEvent->timestamp / 1000; | |||
@@ -201,50 +227,78 @@ puglMouseCallback(const int eventType, const EmscriptenMouseEvent* const mouseEv | |||
mouseEvent->altKey, | |||
mouseEvent->metaKey); | |||
const double scaleFactor = view->world->impl->scaleFactor; | |||
switch (eventType) { | |||
case EMSCRIPTEN_EVENT_MOUSEDOWN: | |||
case EMSCRIPTEN_EVENT_MOUSEUP: | |||
event.button.type = eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; | |||
event.button.time = time; | |||
event.button.x = mouseEvent->targetX; | |||
event.button.y = mouseEvent->targetY; | |||
event.button.xRoot = mouseEvent->screenX; | |||
event.button.yRoot = mouseEvent->screenY; | |||
event.button.x = mouseEvent->targetX * scaleFactor; | |||
event.button.y = mouseEvent->targetY * scaleFactor; | |||
event.button.xRoot = mouseEvent->screenX * scaleFactor; | |||
event.button.yRoot = mouseEvent->screenY * scaleFactor; | |||
event.button.state = state; | |||
switch (mouseEvent->button) { | |||
case 1: event.button.button = 2; | |||
case 2: event.button.button = 1; | |||
default: event.button.button = mouseEvent->button; | |||
break; | |||
case 1: | |||
event.button.button = 2; | |||
break; | |||
case 2: | |||
event.button.button = 1; | |||
break; | |||
default: | |||
event.button.button = mouseEvent->button; | |||
break; | |||
} | |||
break; | |||
case EMSCRIPTEN_EVENT_MOUSEMOVE: | |||
event.motion.type = PUGL_MOTION; | |||
event.motion.time = time; | |||
event.motion.x = mouseEvent->targetX; | |||
event.motion.y = mouseEvent->targetY; | |||
event.motion.xRoot = mouseEvent->screenX; | |||
event.motion.yRoot = mouseEvent->screenY; | |||
if (view->impl->lastX == mouseEvent->targetX && view->impl->lastY == mouseEvent->targetY) { | |||
// adjust local values for delta | |||
const double movementX = mouseEvent->movementX * scaleFactor; | |||
const double movementY = mouseEvent->movementY * scaleFactor; | |||
view->impl->lockedX += movementX; | |||
view->impl->lockedY += movementY; | |||
view->impl->lockedRootX += movementX; | |||
view->impl->lockedRootY += movementY; | |||
// now set x, y, xRoot and yRoot | |||
event.motion.x = view->impl->lockedX; | |||
event.motion.y = view->impl->lockedY; | |||
event.motion.xRoot = view->impl->lockedRootX; | |||
event.motion.yRoot = view->impl->lockedRootY; | |||
} else { | |||
// cache unmodified value first, for pointer lock detection | |||
view->impl->lastX = mouseEvent->targetX; | |||
view->impl->lastY = mouseEvent->targetY; | |||
// now set x, y, xRoot and yRoot | |||
view->impl->lockedX = event.motion.x = mouseEvent->targetX * scaleFactor; | |||
view->impl->lockedY = event.motion.y = mouseEvent->targetY * scaleFactor; | |||
view->impl->lockedRootX = event.motion.xRoot = mouseEvent->screenX * scaleFactor; | |||
view->impl->lockedRootY = event.motion.yRoot = mouseEvent->screenY * scaleFactor; | |||
} | |||
event.motion.state = state; | |||
break; | |||
case EMSCRIPTEN_EVENT_MOUSEENTER: | |||
case EMSCRIPTEN_EVENT_MOUSELEAVE: | |||
event.crossing.type = eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? PUGL_POINTER_IN : PUGL_POINTER_OUT; | |||
event.crossing.time = time; | |||
event.crossing.x = mouseEvent->targetX; | |||
event.crossing.y = mouseEvent->targetY; | |||
event.crossing.xRoot = mouseEvent->screenX; | |||
event.crossing.yRoot = mouseEvent->screenY; | |||
event.crossing.x = mouseEvent->targetX * scaleFactor; | |||
event.crossing.y = mouseEvent->targetY * scaleFactor; | |||
event.crossing.xRoot = mouseEvent->screenX * scaleFactor; | |||
event.crossing.yRoot = mouseEvent->screenY * scaleFactor; | |||
event.crossing.state = state; | |||
event.crossing.mode = PUGL_CROSSING_NORMAL; | |||
break; | |||
} | |||
if (event.type == PUGL_NOTHING) | |||
return EM_TRUE; | |||
return EM_FALSE; | |||
puglDispatchEventWithContext(view, &event); | |||
// FIXME must return false so keyboard events work, why? | |||
return puglDispatchEvent(view, &event) == PUGL_SUCCESS ? EM_FALSE : EM_TRUE; | |||
// note: we must always return false, otherwise canvas never gets keyboard input | |||
return EM_FALSE; | |||
} | |||
static EM_BOOL | |||
@@ -252,10 +306,27 @@ puglFocusCallback(const int eventType, const EmscriptenFocusEvent* /*const focus | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
if (!view->visible) { | |||
return EM_FALSE; | |||
} | |||
PuglEvent event = {{eventType == EMSCRIPTEN_EVENT_FOCUSIN ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT, 0}}; | |||
event.focus.mode = PUGL_CROSSING_NORMAL; | |||
return puglDispatchEvent(view, &event) == PUGL_SUCCESS ? EM_FALSE : EM_TRUE; | |||
puglDispatchEventWithContext(view, &event); | |||
// note: we must always return false, otherwise canvas never gets proper focus | |||
return EM_FALSE; | |||
} | |||
static EM_BOOL | |||
puglPointerLockChangeCallback(const int eventType, const EmscriptenPointerlockChangeEvent* event, void* const userData) | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
printf("puglPointerLockChangeCallback %d\n", event->isActive); | |||
view->impl->pointerLocked = event->isActive; | |||
return EM_TRUE; | |||
} | |||
static EM_BOOL | |||
@@ -263,6 +334,12 @@ puglWheelCallback(const int eventType, const EmscriptenWheelEvent* const wheelEv | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
if (!view->visible) { | |||
return EM_FALSE; | |||
} | |||
const double scaleFactor = view->world->impl->scaleFactor; | |||
PuglEvent event = {{PUGL_SCROLL, 0}}; | |||
event.scroll.time = wheelEvent->mouse.timestamp / 1000; | |||
event.scroll.x = wheelEvent->mouse.targetX; | |||
@@ -275,10 +352,35 @@ puglWheelCallback(const int eventType, const EmscriptenWheelEvent* const wheelEv | |||
wheelEvent->mouse.metaKey); | |||
event.scroll.direction = PUGL_SCROLL_SMOOTH; | |||
// FIXME handle wheelEvent->deltaMode | |||
event.scroll.dx = wheelEvent->deltaX * 0.01; | |||
event.scroll.dy = -wheelEvent->deltaY * 0.01; | |||
event.scroll.dx = wheelEvent->deltaX * 0.01 * scaleFactor; | |||
event.scroll.dy = -wheelEvent->deltaY * 0.01 * scaleFactor; | |||
return puglDispatchEvent(view, &event) == PUGL_SUCCESS ? EM_FALSE : EM_TRUE; | |||
return puglDispatchEventWithContext(view, &event) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; | |||
} | |||
static EM_BOOL | |||
puglUiCallback(const int eventType, const EmscriptenUiEvent* const uiEvent, void* const userData) | |||
{ | |||
PuglView* const view = (PuglView*)userData; | |||
// FIXME | |||
const int width = EM_ASM_INT({ return canvas.parentElement.clientWidth; }); | |||
const int height = EM_ASM_INT({ return canvas.parentElement.clientHeight; }); | |||
if (!width || !height) | |||
return EM_FALSE; | |||
const double scaleFactor = view->world->impl->scaleFactor = emscripten_get_device_pixel_ratio(); | |||
emscripten_set_canvas_element_size(view->world->className, width * scaleFactor, height * scaleFactor); | |||
PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |||
event.configure.x = view->frame.x; | |||
event.configure.y = view->frame.y; | |||
event.configure.width = width * scaleFactor; | |||
event.configure.height = height * scaleFactor; | |||
puglDispatchEvent(view, &event); | |||
return EM_TRUE; | |||
} | |||
PuglStatus | |||
@@ -298,11 +400,13 @@ puglRealize(PuglView* const view) | |||
return PUGL_BAD_BACKEND; | |||
} | |||
const char* const className = view->world->className; | |||
d_stdout("className is %s", className); | |||
// Set the size to the default if it has not already been set | |||
if (view->frame.width <= 0.0 && view->frame.height <= 0.0) { | |||
const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; | |||
if (!defaultSize.width || !defaultSize.height) { | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
return PUGL_BAD_CONFIGURATION; | |||
} | |||
@@ -310,26 +414,8 @@ puglRealize(PuglView* const view) | |||
view->frame.height = defaultSize.height; | |||
} | |||
// Center top-level windows if a position has not been set | |||
if (!view->frame.x && !view->frame.y) { | |||
int screenWidth, screenHeight; | |||
emscripten_get_screen_size(&screenWidth, &screenHeight); | |||
view->frame.x = (PuglCoord)((screenWidth - view->frame.width) / 2); | |||
view->frame.y = (PuglCoord)((screenHeight - view->frame.height) / 2); | |||
} | |||
// Configure the backend to get the visual info | |||
// impl->screen = screen; | |||
if ((st = view->backend->configure(view)) /*|| !impl->vi*/) { | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
view->backend->destroy(view); | |||
return st ? st : PUGL_BACKEND_FAILED; | |||
} | |||
// Create the backend drawing context/surface | |||
if ((st = view->backend->create(view))) { | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
// Configure and create the backend | |||
if ((st = view->backend->configure(view)) || (st = view->backend->create(view))) { | |||
view->backend->destroy(view); | |||
return st; | |||
} | |||
@@ -340,8 +426,13 @@ puglRealize(PuglView* const view) | |||
puglDispatchSimpleEvent(view, PUGL_CREATE); | |||
const char* const className = view->world->className; | |||
d_stdout("className is %s", className); | |||
PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |||
event.configure.x = view->frame.x; | |||
event.configure.y = view->frame.y; | |||
event.configure.width = view->frame.width; | |||
event.configure.height = view->frame.height; | |||
puglDispatchEvent(view, &event); | |||
emscripten_set_canvas_element_size(className, view->frame.width, view->frame.height); | |||
// emscripten_set_keypress_callback(className, view, false, puglKeyCallback); | |||
emscripten_set_keydown_callback(className, view, false, puglKeyCallback); | |||
@@ -353,7 +444,10 @@ puglRealize(PuglView* const view) | |||
emscripten_set_mouseleave_callback(className, view, false, puglMouseCallback); | |||
emscripten_set_focusin_callback(className, view, false, puglFocusCallback); | |||
emscripten_set_focusout_callback(className, view, false, puglFocusCallback); | |||
emscripten_set_pointerlockchange_callback(className, view, false, puglPointerLockChangeCallback); | |||
emscripten_set_wheel_callback(className, view, false, puglWheelCallback); | |||
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglUiCallback); | |||
view->impl->pointerLocked = true; | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
return PUGL_SUCCESS; | |||
@@ -363,20 +457,15 @@ PuglStatus | |||
puglShow(PuglView* const view) | |||
{ | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
PuglStatus st = PUGL_SUCCESS; | |||
if (!st) { | |||
// XMapRaised(view->world->impl->display, view->impl->win); | |||
st = puglPostRedisplay(view); | |||
} | |||
return st; | |||
view->visible = true; | |||
return puglPostRedisplay(view); | |||
} | |||
PuglStatus | |||
puglHide(PuglView* const view) | |||
{ | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
view->visible = false; | |||
return PUGL_FAILURE; | |||
} | |||
@@ -401,9 +490,8 @@ puglFreeWorldInternals(PuglWorld* const world) | |||
} | |||
PuglStatus | |||
puglGrabFocus(PuglView* const view) | |||
puglGrabFocus(PuglView*) | |||
{ | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
return PUGL_FAILURE; | |||
} | |||
@@ -448,31 +536,19 @@ double | |||
puglGetTime(const PuglWorld*) | |||
{ | |||
// d_stdout("DONE %s %d", __func__, __LINE__); | |||
return emscripten_get_now(); | |||
return emscripten_get_now() / 1000; | |||
} | |||
PuglStatus | |||
puglUpdate(PuglWorld* const world, const double timeout) | |||
{ | |||
// printf("TODO: %s %d\n", __func__, __LINE__); | |||
PuglEvent event = {{PUGL_EXPOSE, 0}}; | |||
for (size_t i = 0; i < world->numViews; ++i) { | |||
PuglView* const view = world->views[i]; | |||
static bool first = true; | |||
if (first) { | |||
first = false; | |||
PuglEvent event = {{PUGL_CONFIGURE, 0}}; | |||
event.configure.x = view->frame.x; | |||
event.configure.y = view->frame.y; | |||
event.configure.width = view->frame.width; | |||
event.configure.height = view->frame.height; | |||
d_stdout("configure at %d %d %u %u", | |||
(int)view->frame.x, (int)view->frame.y, | |||
(uint)view->frame.width, (uint)view->frame.height); | |||
puglDispatchEvent(view, &event); | |||
if (view->visible) { | |||
puglDispatchSimpleEvent(view, PUGL_UPDATE); | |||
} | |||
if (!view->impl->needsRepaint) { | |||
@@ -481,6 +557,7 @@ puglUpdate(PuglWorld* const world, const double timeout) | |||
view->impl->needsRepaint = false; | |||
PuglEvent event = {{PUGL_EXPOSE, 0}}; | |||
event.expose.x = view->frame.x; | |||
event.expose.y = view->frame.y; | |||
event.expose.width = view->frame.width; | |||
@@ -542,26 +619,28 @@ puglSetSizeHint(PuglView* const view, | |||
return PUGL_SUCCESS; | |||
} | |||
static void | |||
puglAsyncCallback(void* const arg) | |||
static EM_BOOL | |||
puglTimerLoopCallback(double timeout, void* const arg) | |||
{ | |||
PuglTimer* const timer = (PuglTimer*)arg; | |||
PuglInternals* const impl = timer->view->impl; | |||
// only handle async call if timer still present | |||
// only handle active timers | |||
for (uint32_t i=0; i<impl->numTimers; ++i) | |||
{ | |||
if (impl->timers[i].id == timer->id) | |||
{ | |||
PuglEvent event = {{PUGL_TIMER, 0}}; | |||
event.timer.id = timer->id; | |||
puglDispatchEvent(timer->view, &event); | |||
// re-run again, keeping timer active | |||
emscripten_async_call(puglAsyncCallback, timer, timer->timeout); | |||
break; | |||
puglDispatchEventWithContext(timer->view, &event); | |||
return EM_TRUE; | |||
} | |||
} | |||
return EM_FALSE; | |||
// unused | |||
(void)timeout; | |||
} | |||
PuglStatus | |||
@@ -579,9 +658,8 @@ puglStartTimer(PuglView* const view, const uintptr_t id, const double timeout) | |||
PuglTimer* const timer = &impl->timers[timerIndex]; | |||
timer->view = view; | |||
timer->id = id; | |||
timer->timeout = timeout * 1000; | |||
emscripten_async_call(puglAsyncCallback, timer, timer->timeout); | |||
emscripten_set_timeout_loop(puglTimerLoopCallback, timeout * 1000, timer); | |||
return PUGL_SUCCESS; | |||
} | |||
@@ -632,3 +710,25 @@ puglSetCursor(PuglView* const view, const PuglCursor cursor) | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
return PUGL_FAILURE; | |||
} | |||
PuglStatus | |||
puglSetTransientParent(PuglView* const view, const PuglNativeView parent) | |||
{ | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
view->transientParent = parent; | |||
return PUGL_FAILURE; | |||
} | |||
PuglStatus | |||
puglSetPosition(PuglView* const view, const int x, const int y) | |||
{ | |||
printf("TODO: %s %d\n", __func__, __LINE__); | |||
if (x > INT16_MAX || y > INT16_MAX) { | |||
return PUGL_BAD_PARAMETER; | |||
} | |||
view->frame.x = (PuglCoord)x; | |||
view->frame.y = (PuglCoord)y; | |||
return PUGL_FAILURE; | |||
} |
@@ -10,12 +10,9 @@ | |||
#include "pugl/pugl.h" | |||
#include <emscripten/emscripten.h> | |||
struct PuglTimer { | |||
PuglView* view; | |||
uintptr_t id; | |||
int timeout; | |||
}; | |||
struct PuglWorldInternalsImpl { | |||
@@ -25,7 +22,11 @@ struct PuglWorldInternalsImpl { | |||
struct PuglInternalsImpl { | |||
PuglSurface* surface; | |||
bool needsRepaint; | |||
bool pointerLocked; | |||
uint32_t numTimers; | |||
long lastX, lastY; | |||
double lockedX, lockedY; | |||
double lockedRootX, lockedRootY; | |||
struct PuglTimer* timers; | |||
}; | |||
@@ -116,7 +116,8 @@ puglWasmGlConfigure(PuglView* view) | |||
puglWasmGlGetAttrib(display, config, EGL_DEPTH_SIZE); | |||
view->hints[PUGL_STENCIL_BITS] = | |||
puglWasmGlGetAttrib(display, config, EGL_STENCIL_SIZE); | |||
view->hints[PUGL_SAMPLES] = puglWasmGlGetAttrib(display, config, EGL_SAMPLES); | |||
view->hints[PUGL_SAMPLES] = | |||
puglWasmGlGetAttrib(display, config, EGL_SAMPLES); | |||
// always enabled for EGL | |||
view->hints[PUGL_DOUBLE_BUFFER] = 1; | |||
@@ -136,6 +137,9 @@ puglWasmGlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) | |||
return PUGL_FAILURE; | |||
} | |||
// TESTING: is it faster if we never unset context? | |||
return PUGL_SUCCESS; | |||
return eglMakeCurrent(surface->display, surface->surface, surface->surface, surface->context) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
} | |||
@@ -150,6 +154,9 @@ puglWasmGlLeave(PuglView* view, const PuglExposeEvent* expose) | |||
eglSwapBuffers(surface->display, surface->surface); | |||
} | |||
// TESTING: is it faster if we never unset context? | |||
return PUGL_SUCCESS; | |||
return eglMakeCurrent(surface->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ? PUGL_SUCCESS : PUGL_FAILURE; | |||
} | |||
@@ -161,26 +168,26 @@ puglWasmGlCreate(PuglView* view) | |||
const EGLDisplay display = surface->display; | |||
const EGLConfig config = surface->config; | |||
/* | |||
const int ctx_attrs[] = { | |||
GLX_CONTEXT_MAJOR_VERSION_ARB, | |||
const EGLint attrs[] = { | |||
EGL_CONTEXT_CLIENT_VERSION, | |||
view->hints[PUGL_CONTEXT_VERSION_MAJOR], | |||
EGL_CONTEXT_MAJOR_VERSION, | |||
view->hints[PUGL_CONTEXT_VERSION_MAJOR], | |||
GLX_CONTEXT_MINOR_VERSION_ARB, | |||
/* | |||
EGL_CONTEXT_MINOR_VERSION, | |||
view->hints[PUGL_CONTEXT_VERSION_MINOR], | |||
GLX_CONTEXT_FLAGS_ARB, | |||
(view->hints[PUGL_USE_DEBUG_CONTEXT] ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), | |||
EGL_CONTEXT_OPENGL_DEBUG, | |||
(view->hints[PUGL_USE_DEBUG_CONTEXT] ? EGL_TRUE : EGL_FALSE), | |||
GLX_CONTEXT_PROFILE_MASK_ARB, | |||
EGL_CONTEXT_OPENGL_PROFILE_MASK, | |||
(view->hints[PUGL_USE_COMPAT_PROFILE] | |||
? GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB | |||
: GLX_CONTEXT_CORE_PROFILE_BIT_ARB), | |||
0}; | |||
? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT | |||
: EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT), | |||
*/ | |||
const EGLint attrs[] = { | |||
EGL_CONTEXT_CLIENT_VERSION, 2, | |||
EGL_NONE | |||
}; | |||
@@ -210,6 +217,9 @@ puglWasmGlCreate(PuglView* view) | |||
printf("TODO: %s %d | ok\n", __func__, __LINE__); | |||
// TESTING: is it faster if we never unset context? | |||
eglMakeCurrent(surface->display, surface->surface, surface->surface, surface->context); | |||
return PUGL_SUCCESS; | |||
} | |||
@@ -108,6 +108,7 @@ | |||
#ifndef DGL_FILE_BROWSER_DISABLED | |||
# define FILE_BROWSER_DIALOG_DGL_NAMESPACE | |||
# define FILE_BROWSER_DIALOG_NAMESPACE DGL_NAMESPACE | |||
# define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
START_NAMESPACE_DGL | |||
# include "../../distrho/extra/FileBrowserDialogImpl.hpp" | |||
@@ -225,6 +226,8 @@ void puglRaiseWindow(PuglView* const view) | |||
if (NSWindow* const window = view->impl->window ? view->impl->window | |||
: [view->impl->wrapperView window]) | |||
[window orderFrontRegardless]; | |||
#elif defined(DISTRHO_OS_WASM) | |||
// nothing | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
SetForegroundWindow(view->impl->hwnd); | |||
SetActiveWindow(view->impl->hwnd); | |||
@@ -289,6 +292,8 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co | |||
if (aspect && (status = updateSizeHint(view, PUGL_FIXED_ASPECT)) != PUGL_SUCCESS) | |||
return status; | |||
} | |||
#elif defined(DISTRHO_OS_WASM) | |||
// nothing | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
// nothing | |||
#elif defined(HAVE_X11) | |||
@@ -316,6 +321,8 @@ void puglSetResizable(PuglView* const view, const bool resizable) | |||
[window setStyleMask:style]; | |||
} | |||
// FIXME use [view setAutoresizingMask:NSViewNotSizable] ? | |||
#elif defined(DISTRHO_OS_WASM) | |||
// nothing | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
if (const HWND hwnd = view->impl->hwnd) | |||
{ | |||
@@ -432,6 +439,8 @@ void puglFallbackOnResize(PuglView* const view) | |||
#endif | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#if defined(DISTRHO_OS_MAC) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -569,8 +578,33 @@ void puglWin32ShowCentered(PuglView* const view) | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#elif defined(DISTRHO_OS_WASM) | |||
// nothing here yet | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#elif defined(HAVE_X11) | |||
PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world) | |||
{ | |||
const bool wasDispatchingEvents = world->impl->dispatchingEvents; | |||
world->impl->dispatchingEvents = true; | |||
PuglStatus st = PUGL_SUCCESS; | |||
const double startTime = puglGetTime(world); | |||
const double endTime = startTime + 0.03; | |||
for (double t = startTime; !st && t < endTime; t = puglGetTime(world)) | |||
{ | |||
pollX11Socket(world, endTime - t); | |||
st = dispatchX11Events(world); | |||
} | |||
world->impl->dispatchingEvents = wasDispatchingEvents; | |||
return st; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// X11 specific, set dialog window type and pid hints | |||
@@ -84,6 +84,10 @@ PuglStatus puglMacOSRemoveChildWindow(PuglView* view, PuglView* child); | |||
// macOS specific, center view based on parent coordinates (if there is one) | |||
void puglMacOSShowCentered(PuglView* view); | |||
#elif defined(DISTRHO_OS_WASM) | |||
// nothing here yet | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
// win32 specific, call ShowWindow with SW_RESTORE | |||
@@ -94,6 +98,11 @@ void puglWin32ShowCentered(PuglView* view); | |||
#elif defined(HAVE_X11) | |||
#define DGL_USING_X11 | |||
// X11 specific, update world without triggering exposure evente | |||
PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world); | |||
// X11 specific, set dialog window type and pid hints | |||
void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone); | |||
@@ -1,150 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#ifndef DISTRHO_BASE64_HPP_INCLUDED | |||
#define DISTRHO_BASE64_HPP_INCLUDED | |||
#include "../DistrhoUtils.hpp" | |||
#include <cctype> | |||
#include <vector> | |||
// ----------------------------------------------------------------------- | |||
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
/* | |||
Copyright (C) 2004-2008 René Nyffenegger | |||
This source code is provided 'as-is', without any express or implied | |||
warranty. In no event will the author be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this source code must not be misrepresented; you must not | |||
claim that you wrote the original source code. If you use this source code | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original source code. | |||
3. This notice may not be removed or altered from any source distribution. | |||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch | |||
*/ | |||
// ----------------------------------------------------------------------- | |||
// Helpers | |||
#ifndef DOXYGEN | |||
namespace DistrhoBase64Helpers { | |||
static const char* const kBase64Chars = | |||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
"abcdefghijklmnopqrstuvwxyz" | |||
"0123456789+/"; | |||
static inline | |||
uint8_t findBase64CharIndex(const char c) | |||
{ | |||
static const uint8_t kBase64CharsLen(static_cast<uint8_t>(std::strlen(kBase64Chars))); | |||
for (uint8_t i=0; i<kBase64CharsLen; ++i) | |||
{ | |||
if (kBase64Chars[i] == c) | |||
return i; | |||
} | |||
d_stderr2("findBase64CharIndex('%c') - failed", c); | |||
return 0; | |||
} | |||
static inline | |||
bool isBase64Char(const char c) | |||
{ | |||
return (std::isalnum(c) || (c == '+') || (c == '/')); | |||
} | |||
} // namespace DistrhoBase64Helpers | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
static inline | |||
std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(base64string != nullptr, std::vector<uint8_t>()); | |||
uint i=0, j=0; | |||
uint charArray3[3], charArray4[4]; | |||
std::vector<uint8_t> ret; | |||
ret.reserve(std::strlen(base64string)*3/4 + 4); | |||
for (std::size_t l=0, len=std::strlen(base64string); l<len; ++l) | |||
{ | |||
const char c = base64string[l]; | |||
if (c == '\0' || c == '=') | |||
break; | |||
if (c == ' ' || c == '\n') | |||
continue; | |||
DISTRHO_SAFE_ASSERT_CONTINUE(DistrhoBase64Helpers::isBase64Char(c)); | |||
charArray4[i++] = static_cast<uint>(c); | |||
if (i == 4) | |||
{ | |||
for (i=0; i<4; ++i) | |||
charArray4[i] = DistrhoBase64Helpers::findBase64CharIndex(static_cast<char>(charArray4[i])); | |||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
for (i=0; i<3; ++i) | |||
ret.push_back(static_cast<uint8_t>(charArray3[i])); | |||
i = 0; | |||
} | |||
} | |||
if (i != 0) | |||
{ | |||
for (j=0; j<i && j<4; ++j) | |||
charArray4[j] = DistrhoBase64Helpers::findBase64CharIndex(static_cast<char>(charArray4[j])); | |||
for (j=i; j<4; ++j) | |||
charArray4[j] = 0; | |||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
for (j=0; i>0 && j<i-1; j++) | |||
ret.push_back(static_cast<uint8_t>(charArray3[j])); | |||
} | |||
return ret; | |||
} | |||
// ----------------------------------------------------------------------- | |||
#endif // DISTRHO_BASE64_HPP_INCLUDED |
@@ -1,578 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#ifndef DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED | |||
#define DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED | |||
#include "String.hpp" | |||
#ifndef DISTRHO_OS_WINDOWS | |||
# include <cerrno> | |||
# include <signal.h> | |||
# include <sys/wait.h> | |||
# include <unistd.h> | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// ExternalWindow class | |||
/** | |||
External Window class. | |||
This is a standalone TopLevelWidget/Window-compatible class, but without any real event handling. | |||
Being compatible with TopLevelWidget/Window, it allows to be used as DPF UI target. | |||
It can be used to embed non-DPF things or to run a tool in a new process as the "UI". | |||
The uiIdle() function will be called at regular intervals to keep UI running. | |||
There are helper methods in place to launch external tools and keep track of its running state. | |||
External windows can be setup to run in 3 different modes: | |||
* Embed: | |||
Embed into the host UI, even-loop driven by the host. | |||
This is basically working as a regular plugin UI, as you typically expect them to. | |||
The plugin side does not get control over showing, hiding or closing the window (as usual for plugins). | |||
No restrictions on supported plugin format, everything should work. | |||
Requires DISTRHO_PLUGIN_HAS_EMBED_UI to be set to 1. | |||
* Semi-external: | |||
The UI is not embed into the host, but the even-loop is still driven by it. | |||
In this mode the host does not have control over the UI except for showing, hiding and setting transient parent. | |||
It is possible to close the window from the plugin, the host will be notified of such case. | |||
Host regularly calls isQuitting() to check if the UI got closed by the user or plugin side. | |||
This mode is only possible in LV2 plugin formats, using lv2ui:showInterface extension. | |||
* Standalone: | |||
The UI is not embed into the host or uses its event-loop, basically running as standalone. | |||
The host only has control over showing and hiding the window, nothing else. | |||
The UI is still free to close itself at any point. | |||
DPF will keep calling isRunning() to check if it should keep the event-loop running. | |||
Only possible in JACK and DSSI targets, as the UIs are literally standalone applications there. | |||
Please note that for non-embed windows, you cannot show the window yourself. | |||
The plugin window is only allowed to hide or close itself, a "show" action needs to come from the host. | |||
A few callbacks are provided so that implementations do not need to care about checking for state changes. | |||
They are not called on construction, but will be everytime something changes either by the host or the window itself. | |||
*/ | |||
class ExternalWindow | |||
{ | |||
struct PrivateData; | |||
public: | |||
/** | |||
Constructor. | |||
*/ | |||
explicit ExternalWindow() | |||
: pData() {} | |||
/** | |||
Constructor for DPF internal use. | |||
*/ | |||
explicit ExternalWindow(const PrivateData& data) | |||
: pData(data) {} | |||
/** | |||
Destructor. | |||
*/ | |||
virtual ~ExternalWindow() | |||
{ | |||
DISTRHO_SAFE_ASSERT(!pData.visible); | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow specific calls - Host side calls that you can reimplement for fine-grained funtionality */ | |||
/** | |||
Check if main-loop is running. | |||
This is used under standalone mode to check whether to keep things running. | |||
Returning false from this function will stop the event-loop and close the window. | |||
*/ | |||
virtual bool isRunning() const | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
if (ext.inUse) | |||
return ext.isRunning(); | |||
#endif | |||
return isVisible(); | |||
} | |||
/** | |||
Check if we are about to close. | |||
This is used when the event-loop is provided by the host to check if it should close the window. | |||
It is also used in standalone mode right after isRunning() returns false to verify if window needs to be closed. | |||
*/ | |||
virtual bool isQuitting() const | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
return ext.inUse ? ext.isQuitting : pData.isQuitting; | |||
#else | |||
return pData.isQuitting; | |||
#endif | |||
} | |||
/** | |||
Get the "native" window handle. | |||
This can be reimplemented in order to pass the native window to hosts that can use such informaton. | |||
Returned value type depends on the platform: | |||
- HaikuOS: This is a pointer to a `BView`. | |||
- MacOS: This is a pointer to an `NSView*`. | |||
- Windows: This is a `HWND`. | |||
- Everything else: This is an [X11] `Window`. | |||
@note Only available to override if DISTRHO_PLUGIN_HAS_EMBED_UI is set to 1. | |||
*/ | |||
virtual uintptr_t getNativeWindowHandle() const noexcept | |||
{ | |||
return 0; | |||
} | |||
/** | |||
Grab the keyboard input focus. | |||
Typically you would setup OS-native methods to bring the window to front and give it focus. | |||
Default implementation does nothing. | |||
*/ | |||
virtual void focus() {} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - Information, can be called by either host or plugin */ | |||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
/** | |||
Whether this Window is embed into another (usually not DGL-controlled) Window. | |||
*/ | |||
bool isEmbed() const noexcept | |||
{ | |||
return pData.parentWindowHandle != 0; | |||
} | |||
#endif | |||
/** | |||
Check if this window is visible. | |||
@see setVisible(bool) | |||
*/ | |||
bool isVisible() const noexcept | |||
{ | |||
return pData.visible; | |||
} | |||
/** | |||
Whether this Window is running as standalone, that is, without being coupled to a host event-loop. | |||
When in standalone mode, isRunning() is called to check if the event-loop should keep running. | |||
*/ | |||
bool isStandalone() const noexcept | |||
{ | |||
return pData.isStandalone; | |||
} | |||
/** | |||
Get width of this window. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
uint getWidth() const noexcept | |||
{ | |||
return pData.width; | |||
} | |||
/** | |||
Get height of this window. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
uint getHeight() const noexcept | |||
{ | |||
return pData.height; | |||
} | |||
/** | |||
Get the scale factor requested for this window. | |||
This is purely informational, and up to developers to choose what to do with it. | |||
*/ | |||
double getScaleFactor() const noexcept | |||
{ | |||
return pData.scaleFactor; | |||
} | |||
/** | |||
Get the title of the window previously set with setTitle(). | |||
This is typically displayed in the title bar or in window switchers. | |||
*/ | |||
const char* getTitle() const noexcept | |||
{ | |||
return pData.title; | |||
} | |||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | |||
/** | |||
Get the "native" window handle that this window should embed itself into. | |||
Returned value type depends on the platform: | |||
- HaikuOS: This is a pointer to a `BView`. | |||
- MacOS: This is a pointer to an `NSView*`. | |||
- Windows: This is a `HWND`. | |||
- Everything else: This is an [X11] `Window`. | |||
*/ | |||
uintptr_t getParentWindowHandle() const noexcept | |||
{ | |||
return pData.parentWindowHandle; | |||
} | |||
#endif | |||
/** | |||
Get the transient window that we should attach ourselves to. | |||
TODO what id? also NSView* on macOS, or NSWindow? | |||
*/ | |||
uintptr_t getTransientWindowId() const noexcept | |||
{ | |||
return pData.transientWinId; | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - actions called by either host or plugin */ | |||
/** | |||
Hide window. | |||
This is the same as calling setVisible(false). | |||
Embed windows should never call this! | |||
@see isVisible(), setVisible(bool) | |||
*/ | |||
void hide() | |||
{ | |||
setVisible(false); | |||
} | |||
/** | |||
Hide the UI and gracefully terminate. | |||
Embed windows should never call this! | |||
*/ | |||
virtual void close() | |||
{ | |||
pData.isQuitting = true; | |||
hide(); | |||
#ifndef DISTRHO_OS_WINDOWS | |||
if (ext.inUse) | |||
terminateAndWaitForExternalProcess(); | |||
#endif | |||
} | |||
/** | |||
Set width of this window. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setWidth(uint width) | |||
{ | |||
setSize(width, getHeight()); | |||
} | |||
/** | |||
Set height of this window. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setHeight(uint height) | |||
{ | |||
setSize(getWidth(), height); | |||
} | |||
/** | |||
Set size of this window using @a width and @a height values. | |||
Can trigger a sizeChanged callback. | |||
Only relevant to hosts when the UI is embedded. | |||
*/ | |||
void setSize(uint width, uint height) | |||
{ | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(width > 1, width,); | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(height > 1, height,); | |||
if (pData.width == width && pData.height == height) | |||
return; | |||
pData.width = width; | |||
pData.height = height; | |||
sizeChanged(width, height); | |||
} | |||
/** | |||
Set the title of the window, typically displayed in the title bar or in window switchers. | |||
Can trigger a titleChanged callback. | |||
Only relevant to hosts when the UI is not embedded. | |||
*/ | |||
void setTitle(const char* title) | |||
{ | |||
if (pData.title == title) | |||
return; | |||
pData.title = title; | |||
titleChanged(title); | |||
} | |||
/** | |||
Set geometry constraints for the Window when resized by the user. | |||
*/ | |||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, bool keepAspectRatio = false) | |||
{ | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumWidth > 0, minimumWidth,); | |||
DISTRHO_SAFE_ASSERT_UINT_RETURN(minimumHeight > 0, minimumHeight,); | |||
pData.minWidth = minimumWidth; | |||
pData.minHeight = minimumHeight; | |||
pData.keepAspectRatio = keepAspectRatio; | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* TopLevelWidget-like calls - actions called by the host */ | |||
/** | |||
Show window. | |||
This is the same as calling setVisible(true). | |||
@see isVisible(), setVisible(bool) | |||
*/ | |||
void show() | |||
{ | |||
setVisible(true); | |||
} | |||
/** | |||
Set window visible (or not) according to @a visible. | |||
@see isVisible(), hide(), show() | |||
*/ | |||
void setVisible(bool visible) | |||
{ | |||
if (pData.visible == visible) | |||
return; | |||
pData.visible = visible; | |||
visibilityChanged(visible); | |||
} | |||
/** | |||
Called by the host to set the transient parent window that we should attach ourselves to. | |||
TODO what id? also NSView* on macOS, or NSWindow? | |||
*/ | |||
void setTransientWindowId(uintptr_t winId) | |||
{ | |||
if (pData.transientWinId == winId) | |||
return; | |||
pData.transientWinId = winId; | |||
transientParentWindowChanged(winId); | |||
} | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow special calls for running externals tools */ | |||
bool startExternalProcess(const char* args[]) | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
ext.inUse = true; | |||
return ext.start(args); | |||
#else | |||
(void)args; | |||
return false; // TODO | |||
#endif | |||
} | |||
void terminateAndWaitForExternalProcess() | |||
{ | |||
#ifndef DISTRHO_OS_WINDOWS | |||
ext.isQuitting = true; | |||
ext.terminateAndWait(); | |||
#else | |||
// TODO | |||
#endif | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* ExternalWindow specific callbacks */ | |||
/** | |||
A callback for when the window size changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void sizeChanged(uint /* width */, uint /* height */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
/** | |||
A callback for when the window title changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void titleChanged(const char* /* title */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
/** | |||
A callback for when the window visibility changes. | |||
@note WIP this might need to get fed back into the host somehow. | |||
*/ | |||
virtual void visibilityChanged(bool /* visible */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
/** | |||
A callback for when the transient parent window changes. | |||
*/ | |||
virtual void transientParentWindowChanged(uintptr_t /* winId */) | |||
{ | |||
// unused, meant for custom implementations | |||
} | |||
private: | |||
friend class PluginWindow; | |||
friend class UI; | |||
#ifndef DISTRHO_OS_WINDOWS | |||
struct ExternalProcess { | |||
bool inUse; | |||
bool isQuitting; | |||
mutable pid_t pid; | |||
ExternalProcess() | |||
: inUse(false), | |||
isQuitting(false), | |||
pid(0) {} | |||
bool isRunning() const noexcept | |||
{ | |||
if (pid <= 0) | |||
return false; | |||
const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||
if (p == pid || (p == -1 && errno == ECHILD)) | |||
{ | |||
d_stdout("NOTICE: Child process exited while idle"); | |||
pid = 0; | |||
return false; | |||
} | |||
return true; | |||
} | |||
bool start(const char* args[]) | |||
{ | |||
terminateAndWait(); | |||
pid = vfork(); | |||
switch (pid) | |||
{ | |||
case 0: | |||
execvp(args[0], (char**)args); | |||
_exit(1); | |||
return false; | |||
case -1: | |||
d_stderr("Could not start external ui"); | |||
return false; | |||
default: | |||
return true; | |||
} | |||
} | |||
void terminateAndWait() | |||
{ | |||
if (pid <= 0) | |||
return; | |||
d_stdout("Waiting for external process to stop,,,"); | |||
bool sendTerm = true; | |||
for (pid_t p;;) | |||
{ | |||
p = ::waitpid(pid, nullptr, WNOHANG); | |||
switch (p) | |||
{ | |||
case 0: | |||
if (sendTerm) | |||
{ | |||
sendTerm = false; | |||
::kill(pid, SIGTERM); | |||
} | |||
break; | |||
case -1: | |||
if (errno == ECHILD) | |||
{ | |||
d_stdout("Done! (no such process)"); | |||
pid = 0; | |||
return; | |||
} | |||
break; | |||
default: | |||
if (p == pid) | |||
{ | |||
d_stdout("Done! (clean wait)"); | |||
pid = 0; | |||
return; | |||
} | |||
break; | |||
} | |||
// 5 msec | |||
usleep(5*1000); | |||
} | |||
} | |||
} ext; | |||
#endif | |||
struct PrivateData { | |||
uintptr_t parentWindowHandle; | |||
uintptr_t transientWinId; | |||
uint width; | |||
uint height; | |||
double scaleFactor; | |||
String title; | |||
uint minWidth; | |||
uint minHeight; | |||
bool keepAspectRatio; | |||
bool isQuitting; | |||
bool isStandalone; | |||
bool visible; | |||
PrivateData() | |||
: parentWindowHandle(0), | |||
transientWinId(0), | |||
width(1), | |||
height(1), | |||
scaleFactor(1.0), | |||
title(), | |||
minWidth(0), | |||
minHeight(0), | |||
keepAspectRatio(false), | |||
isQuitting(false), | |||
isStandalone(false), | |||
visible(false) {} | |||
} pData; | |||
DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED |
@@ -1,369 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#ifndef DISTRHO_MUTEX_HPP_INCLUDED | |||
#define DISTRHO_MUTEX_HPP_INCLUDED | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#endif | |||
#include <pthread.h> | |||
START_NAMESPACE_DISTRHO | |||
class Signal; | |||
// ----------------------------------------------------------------------- | |||
// Mutex class | |||
class Mutex | |||
{ | |||
public: | |||
/* | |||
* Constructor. | |||
*/ | |||
Mutex(const bool inheritPriority = true) noexcept | |||
: fMutex() | |||
{ | |||
pthread_mutexattr_t attr; | |||
pthread_mutexattr_init(&attr); | |||
pthread_mutexattr_setprotocol(&attr, inheritPriority ? PTHREAD_PRIO_INHERIT : PTHREAD_PRIO_NONE); | |||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); | |||
pthread_mutex_init(&fMutex, &attr); | |||
pthread_mutexattr_destroy(&attr); | |||
} | |||
/* | |||
* Destructor. | |||
*/ | |||
~Mutex() noexcept | |||
{ | |||
pthread_mutex_destroy(&fMutex); | |||
} | |||
/* | |||
* Lock the mutex. | |||
*/ | |||
bool lock() const noexcept | |||
{ | |||
return (pthread_mutex_lock(&fMutex) == 0); | |||
} | |||
/* | |||
* Try to lock the mutex. | |||
* Returns true if successful. | |||
*/ | |||
bool tryLock() const noexcept | |||
{ | |||
return (pthread_mutex_trylock(&fMutex) == 0); | |||
} | |||
/* | |||
* Unlock the mutex. | |||
*/ | |||
void unlock() const noexcept | |||
{ | |||
pthread_mutex_unlock(&fMutex); | |||
} | |||
private: | |||
mutable pthread_mutex_t fMutex; | |||
DISTRHO_DECLARE_NON_COPYABLE(Mutex) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// RecursiveMutex class | |||
class RecursiveMutex | |||
{ | |||
public: | |||
/* | |||
* Constructor. | |||
*/ | |||
RecursiveMutex() noexcept | |||
#ifdef DISTRHO_OS_WINDOWS | |||
: fSection() | |||
#else | |||
: fMutex() | |||
#endif | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
InitializeCriticalSection(&fSection); | |||
#else | |||
pthread_mutexattr_t attr; | |||
pthread_mutexattr_init(&attr); | |||
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); | |||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | |||
pthread_mutex_init(&fMutex, &attr); | |||
pthread_mutexattr_destroy(&attr); | |||
#endif | |||
} | |||
/* | |||
* Destructor. | |||
*/ | |||
~RecursiveMutex() noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
DeleteCriticalSection(&fSection); | |||
#else | |||
pthread_mutex_destroy(&fMutex); | |||
#endif | |||
} | |||
/* | |||
* Lock the mutex. | |||
*/ | |||
bool lock() const noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
EnterCriticalSection(&fSection); | |||
return true; | |||
#else | |||
return (pthread_mutex_lock(&fMutex) == 0); | |||
#endif | |||
} | |||
/* | |||
* Try to lock the mutex. | |||
* Returns true if successful. | |||
*/ | |||
bool tryLock() const noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
return (TryEnterCriticalSection(&fSection) != FALSE); | |||
#else | |||
return (pthread_mutex_trylock(&fMutex) == 0); | |||
#endif | |||
} | |||
/* | |||
* Unlock the mutex. | |||
*/ | |||
void unlock() const noexcept | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
LeaveCriticalSection(&fSection); | |||
#else | |||
pthread_mutex_unlock(&fMutex); | |||
#endif | |||
} | |||
private: | |||
#ifdef DISTRHO_OS_WINDOWS | |||
mutable CRITICAL_SECTION fSection; | |||
#else | |||
mutable pthread_mutex_t fMutex; | |||
#endif | |||
DISTRHO_DECLARE_NON_COPYABLE(RecursiveMutex) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Signal class | |||
class Signal | |||
{ | |||
public: | |||
/* | |||
* Constructor. | |||
*/ | |||
Signal() noexcept | |||
: fCondition(), | |||
fMutex(), | |||
fTriggered(false) | |||
{ | |||
pthread_condattr_t cattr; | |||
pthread_condattr_init(&cattr); | |||
pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE); | |||
pthread_cond_init(&fCondition, &cattr); | |||
pthread_condattr_destroy(&cattr); | |||
pthread_mutexattr_t mattr; | |||
pthread_mutexattr_init(&mattr); | |||
pthread_mutexattr_setprotocol(&mattr, PTHREAD_PRIO_INHERIT); | |||
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_NORMAL); | |||
pthread_mutex_init(&fMutex, &mattr); | |||
pthread_mutexattr_destroy(&mattr); | |||
} | |||
/* | |||
* Destructor. | |||
*/ | |||
~Signal() noexcept | |||
{ | |||
pthread_cond_destroy(&fCondition); | |||
pthread_mutex_destroy(&fMutex); | |||
} | |||
/* | |||
* Wait for a signal. | |||
*/ | |||
void wait() noexcept | |||
{ | |||
pthread_mutex_lock(&fMutex); | |||
while (! fTriggered) | |||
{ | |||
try { | |||
pthread_cond_wait(&fCondition, &fMutex); | |||
} DISTRHO_SAFE_EXCEPTION("pthread_cond_wait"); | |||
} | |||
fTriggered = false; | |||
pthread_mutex_unlock(&fMutex); | |||
} | |||
/* | |||
* Wake up all waiting threads. | |||
*/ | |||
void signal() noexcept | |||
{ | |||
pthread_mutex_lock(&fMutex); | |||
if (! fTriggered) | |||
{ | |||
fTriggered = true; | |||
pthread_cond_broadcast(&fCondition); | |||
} | |||
pthread_mutex_unlock(&fMutex); | |||
} | |||
private: | |||
pthread_cond_t fCondition; | |||
pthread_mutex_t fMutex; | |||
volatile bool fTriggered; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPYABLE(Signal) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Helper class to lock&unlock a mutex during a function scope. | |||
template <class Mutex> | |||
class ScopeLocker | |||
{ | |||
public: | |||
ScopeLocker(const Mutex& mutex) noexcept | |||
: fMutex(mutex) | |||
{ | |||
fMutex.lock(); | |||
} | |||
~ScopeLocker() noexcept | |||
{ | |||
fMutex.unlock(); | |||
} | |||
private: | |||
const Mutex& fMutex; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeLocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Helper class to try-lock&unlock a mutex during a function scope. | |||
template <class Mutex> | |||
class ScopeTryLocker | |||
{ | |||
public: | |||
ScopeTryLocker(const Mutex& mutex) noexcept | |||
: fMutex(mutex), | |||
fLocked(mutex.tryLock()) {} | |||
ScopeTryLocker(const Mutex& mutex, const bool forceLock) noexcept | |||
: fMutex(mutex), | |||
fLocked(forceLock ? mutex.lock() : mutex.tryLock()) {} | |||
~ScopeTryLocker() noexcept | |||
{ | |||
if (fLocked) | |||
fMutex.unlock(); | |||
} | |||
bool wasLocked() const noexcept | |||
{ | |||
return fLocked; | |||
} | |||
bool wasNotLocked() const noexcept | |||
{ | |||
return !fLocked; | |||
} | |||
private: | |||
const Mutex& fMutex; | |||
const bool fLocked; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeTryLocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Helper class to unlock&lock a mutex during a function scope. | |||
template <class Mutex> | |||
class ScopeUnlocker | |||
{ | |||
public: | |||
ScopeUnlocker(const Mutex& mutex) noexcept | |||
: fMutex(mutex) | |||
{ | |||
fMutex.unlock(); | |||
} | |||
~ScopeUnlocker() noexcept | |||
{ | |||
fMutex.lock(); | |||
} | |||
private: | |||
const Mutex& fMutex; | |||
DISTRHO_PREVENT_HEAP_ALLOCATION | |||
DISTRHO_DECLARE_NON_COPYABLE(ScopeUnlocker) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
// Define types | |||
typedef ScopeLocker<Mutex> MutexLocker; | |||
typedef ScopeLocker<RecursiveMutex> RecursiveMutexLocker; | |||
typedef ScopeTryLocker<Mutex> MutexTryLocker; | |||
typedef ScopeTryLocker<RecursiveMutex> RecursiveMutexTryLocker; | |||
typedef ScopeUnlocker<Mutex> MutexUnlocker; | |||
typedef ScopeUnlocker<RecursiveMutex> RecursiveMutexUnlocker; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_MUTEX_HPP_INCLUDED |
@@ -1,71 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#ifndef DISTRHO_SLEEP_HPP_INCLUDED | |||
#define DISTRHO_SLEEP_HPP_INCLUDED | |||
#include "../DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# ifndef NOMINMAX | |||
# define NOMINMAX | |||
# endif | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#else | |||
# include <unistd.h> | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// d_*sleep | |||
/* | |||
* Sleep for 'secs' seconds. | |||
*/ | |||
static inline | |||
void d_sleep(const uint secs) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(secs > 0,); | |||
try { | |||
#ifdef DISTRHO_OS_WINDOWS | |||
::Sleep(secs * 1000); | |||
#else | |||
::sleep(secs); | |||
#endif | |||
} DISTRHO_SAFE_EXCEPTION("d_sleep"); | |||
} | |||
/* | |||
* Sleep for 'msecs' milliseconds. | |||
*/ | |||
static inline | |||
void d_msleep(const uint msecs) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(msecs > 0,); | |||
try { | |||
#ifdef DISTRHO_OS_WINDOWS | |||
::Sleep(msecs); | |||
#else | |||
::usleep(msecs * 1000); | |||
#endif | |||
} DISTRHO_SAFE_EXCEPTION("d_msleep"); | |||
} | |||
// ----------------------------------------------------------------------- | |||
#endif // DISTRHO_SLEEP_HPP_INCLUDED |
@@ -1,334 +0,0 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
* | |||
* THE 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. | |||
*/ | |||
#ifndef DISTRHO_THREAD_HPP_INCLUDED | |||
#define DISTRHO_THREAD_HPP_INCLUDED | |||
#include "Mutex.hpp" | |||
#include "Sleep.hpp" | |||
#include "String.hpp" | |||
#ifdef DISTRHO_OS_LINUX | |||
# include <sys/prctl.h> | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------- | |||
// Thread class | |||
class Thread | |||
{ | |||
protected: | |||
/* | |||
* Constructor. | |||
*/ | |||
Thread(const char* const threadName = nullptr) noexcept | |||
: fLock(), | |||
fSignal(), | |||
fName(threadName), | |||
#ifdef PTW32_DLLPORT | |||
fHandle({nullptr, 0}), | |||
#else | |||
fHandle(0), | |||
#endif | |||
fShouldExit(false) {} | |||
/* | |||
* Destructor. | |||
*/ | |||
virtual ~Thread() /*noexcept*/ | |||
{ | |||
DISTRHO_SAFE_ASSERT(! isThreadRunning()); | |||
stopThread(-1); | |||
} | |||
/* | |||
* Virtual function to be implemented by the subclass. | |||
*/ | |||
virtual void run() = 0; | |||
// ------------------------------------------------------------------- | |||
public: | |||
/* | |||
* Check if the thread is running. | |||
*/ | |||
bool isThreadRunning() const noexcept | |||
{ | |||
#ifdef PTW32_DLLPORT | |||
return (fHandle.p != nullptr); | |||
#else | |||
return (fHandle != 0); | |||
#endif | |||
} | |||
/* | |||
* Check if the thread should exit. | |||
*/ | |||
bool shouldThreadExit() const noexcept | |||
{ | |||
return fShouldExit; | |||
} | |||
/* | |||
* Start the thread. | |||
*/ | |||
bool startThread(const bool withRealtimePriority = false) noexcept | |||
{ | |||
// check if already running | |||
DISTRHO_SAFE_ASSERT_RETURN(! isThreadRunning(), true); | |||
pthread_t handle; | |||
pthread_attr_t attr; | |||
pthread_attr_init(&attr); | |||
struct sched_param sched_param; | |||
std::memset(&sched_param, 0, sizeof(sched_param)); | |||
if (withRealtimePriority) | |||
{ | |||
sched_param.sched_priority = 80; | |||
#ifndef DISTRHO_OS_HAIKU | |||
if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) == 0 && | |||
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0 && | |||
# ifndef DISTRHO_OS_WINDOWS | |||
(pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0 || | |||
pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0) && | |||
# endif | |||
pthread_attr_setschedparam(&attr, &sched_param) == 0) | |||
{ | |||
d_stdout("Thread setup with realtime priority successful"); | |||
} | |||
else | |||
#endif | |||
{ | |||
d_stdout("Thread setup with realtime priority failed, going with normal priority instead"); | |||
pthread_attr_destroy(&attr); | |||
pthread_attr_init(&attr); | |||
} | |||
} | |||
const MutexLocker ml(fLock); | |||
fShouldExit = false; | |||
bool ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; | |||
pthread_attr_destroy(&attr); | |||
if (withRealtimePriority && !ok) | |||
{ | |||
d_stdout("Thread with realtime priority failed on creation, going with normal priority instead"); | |||
pthread_attr_init(&attr); | |||
ok = pthread_create(&handle, &attr, _entryPoint, this) == 0; | |||
pthread_attr_destroy(&attr); | |||
} | |||
DISTRHO_SAFE_ASSERT_RETURN(ok, false); | |||
#ifdef PTW32_DLLPORT | |||
DISTRHO_SAFE_ASSERT_RETURN(handle.p != nullptr, false); | |||
#else | |||
DISTRHO_SAFE_ASSERT_RETURN(handle != 0, false); | |||
#endif | |||
pthread_detach(handle); | |||
_copyFrom(handle); | |||
// wait for thread to start | |||
fSignal.wait(); | |||
return true; | |||
} | |||
/* | |||
* Stop the thread. | |||
* In the 'timeOutMilliseconds': | |||
* = 0 -> no wait | |||
* > 0 -> wait timeout value | |||
* < 0 -> wait forever | |||
*/ | |||
bool stopThread(const int timeOutMilliseconds) noexcept | |||
{ | |||
const MutexLocker ml(fLock); | |||
if (isThreadRunning()) | |||
{ | |||
signalThreadShouldExit(); | |||
if (timeOutMilliseconds != 0) | |||
{ | |||
// Wait for the thread to stop | |||
int timeOutCheck = (timeOutMilliseconds == 1 || timeOutMilliseconds == -1) ? timeOutMilliseconds : timeOutMilliseconds/2; | |||
for (; isThreadRunning();) | |||
{ | |||
d_msleep(2); | |||
if (timeOutCheck < 0) | |||
continue; | |||
if (timeOutCheck > 0) | |||
timeOutCheck -= 1; | |||
else | |||
break; | |||
} | |||
} | |||
if (isThreadRunning()) | |||
{ | |||
// should never happen! | |||
d_stderr2("assertion failure: \"! isThreadRunning()\" in file %s, line %i", __FILE__, __LINE__); | |||
// copy thread id so we can clear our one | |||
pthread_t threadId; | |||
_copyTo(threadId); | |||
_init(); | |||
pthread_detach(threadId); | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/* | |||
* Tell the thread to stop as soon as possible. | |||
*/ | |||
void signalThreadShouldExit() noexcept | |||
{ | |||
fShouldExit = true; | |||
} | |||
// ------------------------------------------------------------------- | |||
/* | |||
* Returns the name of the thread. | |||
* This is the name that gets set in the constructor. | |||
*/ | |||
const String& getThreadName() const noexcept | |||
{ | |||
return fName; | |||
} | |||
/* | |||
* Returns the Id/handle of the thread. | |||
*/ | |||
pthread_t getThreadId() const noexcept | |||
{ | |||
return fHandle; | |||
} | |||
/* | |||
* Changes the name of the caller thread. | |||
*/ | |||
static void setCurrentThreadName(const char* const name) noexcept | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',); | |||
#ifdef DISTRHO_OS_LINUX | |||
prctl(PR_SET_NAME, name, 0, 0, 0); | |||
#endif | |||
#if defined(__GLIBC__) && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 && !defined(DISTRHO_OS_GNU_HURD) | |||
pthread_setname_np(pthread_self(), name); | |||
#endif | |||
} | |||
// ------------------------------------------------------------------- | |||
private: | |||
Mutex fLock; // Thread lock | |||
Signal fSignal; // Thread start wait signal | |||
const String fName; // Thread name | |||
volatile pthread_t fHandle; // Handle for this thread | |||
volatile bool fShouldExit; // true if thread should exit | |||
/* | |||
* Init pthread type. | |||
*/ | |||
void _init() noexcept | |||
{ | |||
#ifdef PTW32_DLLPORT | |||
fHandle.p = nullptr; | |||
fHandle.x = 0; | |||
#else | |||
fHandle = 0; | |||
#endif | |||
} | |||
/* | |||
* Copy our pthread type from another var. | |||
*/ | |||
void _copyFrom(const pthread_t& handle) noexcept | |||
{ | |||
#ifdef PTW32_DLLPORT | |||
fHandle.p = handle.p; | |||
fHandle.x = handle.x; | |||
#else | |||
fHandle = handle; | |||
#endif | |||
} | |||
/* | |||
* Copy our pthread type to another var. | |||
*/ | |||
void _copyTo(volatile pthread_t& handle) const noexcept | |||
{ | |||
#ifdef PTW32_DLLPORT | |||
handle.p = fHandle.p; | |||
handle.x = fHandle.x; | |||
#else | |||
handle = fHandle; | |||
#endif | |||
} | |||
/* | |||
* Thread entry point. | |||
*/ | |||
void _runEntryPoint() noexcept | |||
{ | |||
if (fName.isNotEmpty()) | |||
setCurrentThreadName(fName); | |||
// report ready | |||
fSignal.signal(); | |||
try { | |||
run(); | |||
} catch(...) {} | |||
// done | |||
_init(); | |||
} | |||
/* | |||
* Thread entry point. | |||
*/ | |||
static void* _entryPoint(void* userData) noexcept | |||
{ | |||
static_cast<Thread*>(userData)->_runEntryPoint(); | |||
return nullptr; | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(Thread) | |||
}; | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_THREAD_HPP_INCLUDED |
@@ -44,6 +44,7 @@ | |||
# define x_fib_show DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_show) | |||
# define x_fib_status DISTRHO_PUGL_NAMESPACE_MACRO(plugin, x_fib_status) | |||
# define DISTRHO_FILE_BROWSER_DIALOG_HPP_INCLUDED | |||
# define FILE_BROWSER_DIALOG_NAMESPACE DISTRHO_NAMESPACE | |||
# define FILE_BROWSER_DIALOG_DISTRHO_NAMESPACE | |||
START_NAMESPACE_DISTRHO | |||
# include "../extra/FileBrowserDialogImpl.hpp" | |||
@@ -25,6 +25,7 @@ | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# include "../extra/Sleep.hpp" | |||
// TODO import and use file browser here | |||
#else | |||
# include "../../dgl/src/ApplicationPrivateData.hpp" | |||
# include "../../dgl/src/WindowPrivateData.hpp" | |||
@@ -319,7 +320,7 @@ struct UI::PrivateData { | |||
uint fgColor; | |||
double scaleFactor; | |||
uintptr_t winId; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
char* uiStateFileKeyRequest; | |||
#endif | |||
char* bundlePath; | |||
@@ -346,7 +347,7 @@ struct UI::PrivateData { | |||
fgColor(0xffffffff), | |||
scaleFactor(1.0), | |||
winId(0), | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
uiStateFileKeyRequest(nullptr), | |||
#endif | |||
bundlePath(nullptr), | |||
@@ -382,7 +383,7 @@ struct UI::PrivateData { | |||
~PrivateData() noexcept | |||
{ | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
std::free(uiStateFileKeyRequest); | |||
#endif | |||
std::free(bundlePath); | |||
@@ -437,7 +438,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
if (fileRequestCallbackFunc != nullptr) | |||
return fileRequestCallbackFunc(callbacksPtr, key); | |||
#if DISTRHO_PLUGIN_WANT_STATE && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !defined(DGL_FILE_BROWSER_DISABLED) | |||
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
std::free(uiStateFileKeyRequest); | |||
uiStateFileKeyRequest = strdup(key); | |||
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); | |||
@@ -457,7 +458,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||
// ----------------------------------------------------------------------- | |||
// PluginWindow onFileSelected that require UI::PrivateData definitions | |||
#if DISTRHO_UI_FILE_BROWSER | |||
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
inline void PluginWindow::onFileSelected(const char* const filename) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); | |||
@@ -481,7 +482,9 @@ inline void PluginWindow::onFileSelected(const char* const filename) | |||
} | |||
#endif | |||
puglBackendEnter(pData->view); | |||
ui->uiFileBrowserSelected(filename); | |||
puglBackendLeave(pData->view); | |||
} | |||
#endif | |||
@@ -78,7 +78,11 @@ const char* getPluginFormatName() noexcept | |||
#if defined(DISTRHO_PLUGIN_TARGET_CARLA) | |||
return "Carla"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_JACK) | |||
# ifdef DISTRHO_OS_WASM | |||
return "Wasm/Standalone"; | |||
# else | |||
return "JACK/Standalone"; | |||
# endif | |||
#elif defined(DISTRHO_PLUGIN_TARGET_LADSPA) | |||
return "LADSPA"; | |||
#elif defined(DISTRHO_PLUGIN_TARGET_DSSI) | |||