Browse Source

Update included DPF

Signed-off-by: falkTX <falktx@falktx.com>
tags/v2.5.0
falkTX 2 years ago
parent
commit
3d3c4d8933
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
19 changed files with 395 additions and 1696 deletions
  1. +0
    -62
      data/copy-dpf-stuff
  2. +51
    -0
      data/update-dpf
  3. +7
    -1
      source/modules/dgl/Application.hpp
  4. +7
    -0
      source/modules/dgl/src/ApplicationPrivateData.cpp
  5. +56
    -20
      source/modules/dgl/src/WindowPrivateData.cpp
  6. +2
    -1
      source/modules/dgl/src/WindowPrivateData.hpp
  7. +189
    -89
      source/modules/dgl/src/pugl-upstream/src/wasm.c
  8. +4
    -3
      source/modules/dgl/src/pugl-upstream/src/wasm.h
  9. +23
    -13
      source/modules/dgl/src/pugl-upstream/src/wasm_gl.c
  10. +34
    -0
      source/modules/dgl/src/pugl.cpp
  11. +9
    -0
      source/modules/dgl/src/pugl.hpp
  12. +0
    -150
      source/modules/distrho/extra/Base64.hpp
  13. +0
    -578
      source/modules/distrho/extra/ExternalWindow.hpp
  14. +0
    -369
      source/modules/distrho/extra/Mutex.hpp
  15. +0
    -71
      source/modules/distrho/extra/Sleep.hpp
  16. +0
    -334
      source/modules/distrho/extra/Thread.hpp
  17. +1
    -0
      source/modules/distrho/src/DistrhoUI.cpp
  18. +8
    -5
      source/modules/distrho/src/DistrhoUIPrivateData.hpp
  19. +4
    -0
      source/modules/distrho/src/DistrhoUtils.cpp

+ 0
- 62
data/copy-dpf-stuff View File

@@ -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/

+ 51
- 0
data/update-dpf View File

@@ -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}

+ 7
- 1
source/modules/dgl/Application.hpp View File

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


+ 7
- 0
source/modules/dgl/src/ApplicationPrivateData.cpp View File

@@ -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)),


+ 56
- 20
source/modules/dgl/src/WindowPrivateData.cpp View File

@@ -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


+ 2
- 1
source/modules/dgl/src/WindowPrivateData.hpp View File

@@ -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;


+ 189
- 89
source/modules/dgl/src/pugl-upstream/src/wasm.c View File

@@ -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;
}

+ 4
- 3
source/modules/dgl/src/pugl-upstream/src/wasm.h View File

@@ -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;
};



+ 23
- 13
source/modules/dgl/src/pugl-upstream/src/wasm_gl.c View File

@@ -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;
}



+ 34
- 0
source/modules/dgl/src/pugl.cpp View File

@@ -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



+ 9
- 0
source/modules/dgl/src/pugl.hpp View File

@@ -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);



+ 0
- 150
source/modules/distrho/extra/Base64.hpp View File

@@ -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

+ 0
- 578
source/modules/distrho/extra/ExternalWindow.hpp View File

@@ -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

+ 0
- 369
source/modules/distrho/extra/Mutex.hpp View File

@@ -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

+ 0
- 71
source/modules/distrho/extra/Sleep.hpp View File

@@ -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

+ 0
- 334
source/modules/distrho/extra/Thread.hpp View File

@@ -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

+ 1
- 0
source/modules/distrho/src/DistrhoUI.cpp View File

@@ -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"


+ 8
- 5
source/modules/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -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



+ 4
- 0
source/modules/distrho/src/DistrhoUtils.cpp View File

@@ -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)


Loading…
Cancel
Save