Browse Source

Adjust things for better external ui support, add example plugin

Signed-off-by: falkTX <falktx@gmail.com>
vstgui
falkTX 4 years ago
parent
commit
209c6c2be6
Signed by: falkTX <falktx@gmail.com> GPG Key ID: 2D3445A829213837
13 changed files with 696 additions and 100 deletions
  1. +9
    -0
      Makefile
  2. +8
    -7
      distrho/DistrhoUI.hpp
  3. +40
    -10
      distrho/extra/ExternalWindow.hpp
  4. +0
    -5
      distrho/src/DistrhoPluginJack.cpp
  5. +16
    -17
      distrho/src/DistrhoUI.cpp
  6. +2
    -0
      distrho/src/DistrhoUIDSSI.cpp
  7. +119
    -61
      distrho/src/DistrhoUIInternal.hpp
  8. +36
    -0
      examples/ExternalUI/DistrhoPluginInfo.h
  9. +188
    -0
      examples/ExternalUI/ExternalExamplePlugin.cpp
  10. +187
    -0
      examples/ExternalUI/ExternalExampleUI.cpp
  11. +40
    -0
      examples/ExternalUI/ExternalLauncher.sh
  12. +40
    -0
      examples/ExternalUI/Makefile
  13. +11
    -0
      examples/ExternalUI/README.md

+ 9
- 0
Makefile View File

@@ -16,6 +16,7 @@ ifeq ($(HAVE_DGL),true)
endif

examples: dgl
$(MAKE) all -C examples/ExternalUI
$(MAKE) all -C examples/Info
$(MAKE) all -C examples/Latency
$(MAKE) all -C examples/Meters
@@ -23,6 +24,13 @@ examples: dgl
$(MAKE) all -C examples/Parameters
$(MAKE) all -C examples/States

# ExternalUI launcher
install -d bin/d_extui-dssi
install -d bin/d_extui.lv2
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.sh
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui-dssi/d_extui.sh
install -m 755 examples/ExternalUI/ExternalLauncher.sh bin/d_extui.lv2/d_extui.sh

ifneq ($(CROSS_COMPILING),true)
gen: examples utils/lv2_ttl_generator
@$(CURDIR)/utils/generate-ttl.sh
@@ -40,6 +48,7 @@ endif

clean:
$(MAKE) clean -C dgl
$(MAKE) clean -C examples/ExternalUI
$(MAKE) clean -C examples/Info
$(MAKE) clean -C examples/Latency
$(MAKE) clean -C examples/Meters


+ 8
- 7
distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2018 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
@@ -20,7 +20,8 @@
#include "extra/LeakDetector.hpp"
#include "src/DistrhoPluginChecks.h"

#ifndef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../dgl/Base.hpp"
# include "extra/ExternalWindow.hpp"
typedef DISTRHO_NAMESPACE::ExternalWindow UIWidget;
#elif DISTRHO_UI_USE_NANOVG
@@ -67,7 +68,7 @@ public:
*/
bool isUserResizable() const noexcept;

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/**
Set geometry constraints for the UI when resized by the user, and optionally scale UI automatically.
@see Window::setGeometryConstraints(uint,uint,bool)
@@ -181,7 +182,7 @@ protected:
*/
virtual void sampleRateChanged(double newSampleRate);

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/* --------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */

@@ -191,13 +192,13 @@ protected:
*/
virtual void uiIdle() {}

#ifndef DGL_FILE_BROWSER_DISABLED
# ifndef DGL_FILE_BROWSER_DISABLED
/**
File browser selected function.
@see Window::fileBrowserSelected(const char*)
*/
virtual void uiFileBrowserSelected(const char* filename);
#endif
# endif

/**
OpenGL window reshape function, called when parent window is resized.
@@ -225,7 +226,7 @@ private:
friend class UIExporter;
friend class UIExporterWindow;

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// these should not be used
void setAbsoluteX(int) const noexcept {}
void setAbsoluteY(int) const noexcept {}


+ 40
- 10
distrho/extra/ExternalWindow.hpp View File

@@ -40,6 +40,8 @@ public:
: width(w),
height(h),
title(t),
transientWinId(0),
visible(false),
pid(0) {}

virtual ~ExternalWindow()
@@ -62,9 +64,14 @@ public:
return title;
}

void setTitle(const char* const t) noexcept
uintptr_t getTransientWinId() const noexcept
{
title = t;
return transientWinId;
}

bool isVisible() const noexcept
{
return visible;
}

bool isRunning() noexcept
@@ -84,6 +91,27 @@ public:
return true;
}

virtual void setSize(uint w, uint h)
{
width = w;
height = h;
}

virtual void setTitle(const char* const t)
{
title = t;
}

virtual void setTransientWinId(const uintptr_t winId)
{
transientWinId = winId;
}

virtual void setVisible(const bool yesNo)
{
visible = yesNo;
}

protected:
bool startExternalProcess(const char* args[])
{
@@ -107,14 +135,6 @@ protected:
}
}

private:
uint width;
uint height;
String title;
pid_t pid;

friend class UIExporter;

void terminateAndWaitForProcess()
{
if (pid <= 0)
@@ -162,6 +182,16 @@ private:
}
}

private:
uint width;
uint height;
String title;
uintptr_t transientWinId;
bool visible;
pid_t pid;

friend class UIExporter;

DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow)
};



+ 0
- 5
distrho/src/DistrhoPluginJack.cpp View File

@@ -16,11 +16,6 @@

#include "DistrhoPluginInternal.hpp"

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
# undef DISTRHO_PLUGIN_HAS_UI
# define DISTRHO_PLUGIN_HAS_UI 0
#endif

#if DISTRHO_PLUGIN_HAS_UI
# include "DistrhoUIInternal.hpp"
#else


+ 16
- 17
distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2018 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
@@ -15,8 +15,7 @@
*/

#include "DistrhoUIInternal.hpp"

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "src/WidgetPrivateData.hpp"
#endif

@@ -27,16 +26,21 @@ START_NAMESPACE_DISTRHO

double d_lastUiSampleRate = 0.0;
void* d_lastUiDspPtr = nullptr;
#ifdef HAVE_DGL
Window* d_lastUiWindow = nullptr;
#endif
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uintptr_t g_nextWindowId = 0;
const char* g_nextBundlePath = nullptr;
#else
Window* d_lastUiWindow = nullptr;
#endif

/* ------------------------------------------------------------------------------------------------------------
* UI */

#ifdef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
UI::UI(uint width, uint height, bool userResizable)
: UIWidget(width, height),
pData(new PrivateData(userResizable)) {}
#else
UI::UI(uint width, uint height, bool userResizable)
: UIWidget(*d_lastUiWindow),
pData(new PrivateData(userResizable))
@@ -46,10 +50,6 @@ UI::UI(uint width, uint height, bool userResizable)
if (width > 0 && height > 0)
setSize(width, height);
}
#else
UI::UI(uint width, uint height, bool userResizable)
: UIWidget(width, height),
pData(new PrivateData(userResizable)) {}
#endif

UI::~UI()
@@ -62,7 +62,7 @@ bool UI::isUserResizable() const noexcept
return pData->userResizable;
}

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRatio, bool automaticallyScale)
{
DISTRHO_SAFE_ASSERT_RETURN(minWidth > 0,);
@@ -73,7 +73,6 @@ void UI::setGeometryConstraints(uint minWidth, uint minHeight, bool keepAspectRa
pData->minHeight = minHeight;

getParentWindow().setGeometryConstraints(minWidth, minHeight, keepAspectRatio);

}
#endif

@@ -141,15 +140,15 @@ uintptr_t UI::getNextWindowId() noexcept

void UI::sampleRateChanged(double) {}

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
/* ------------------------------------------------------------------------------------------------------------
* UI Callbacks (optional) */

#ifndef DGL_FILE_BROWSER_DISABLED
# ifndef DGL_FILE_BROWSER_DISABLED
void UI::uiFileBrowserSelected(const char*)
{
}
#endif
# endif

void UI::uiReshape(uint width, uint height)
{
@@ -173,7 +172,7 @@ void UI::onResize(const ResizeEvent& ev)

pData->setSizeCallback(ev.size.getWidth(), ev.size.getHeight());
}
#endif
#endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI

// -----------------------------------------------------------------------------------------------------------



+ 2
- 0
distrho/src/DistrhoUIDSSI.cpp View File

@@ -112,6 +112,7 @@ public:

void exec()
{
d_stdout("exec 1");
for (;;)
{
fOscData.idle();
@@ -121,6 +122,7 @@ public:

d_msleep(30);
}
d_stdout("exec 3");
}

// -------------------------------------------------------------------


+ 119
- 61
distrho/src/DistrhoUIInternal.hpp View File

@@ -19,7 +19,10 @@

#include "../DistrhoUI.hpp"

#ifdef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include "../extra/Sleep.hpp"
using DGL_NAMESPACE::IdleCallback;
#else
# include "../../dgl/Application.hpp"
# include "../../dgl/Window.hpp"
using DGL_NAMESPACE::Application;
@@ -34,7 +37,7 @@ START_NAMESPACE_DISTRHO

extern double d_lastUiSampleRate;
extern void* d_lastUiDspPtr;
#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
extern Window* d_lastUiWindow;
#endif
extern uintptr_t g_nextWindowId;
@@ -146,7 +149,20 @@ struct UI::PrivateData {
// -----------------------------------------------------------------------
// Plugin Window, needed to take care of resize properly

#ifdef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
static inline
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextBundlePath = nullptr;
return ret;
}
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
static inline
UI* createUiWrapper(void* const dspPtr, Window* const window)
{
@@ -212,7 +228,7 @@ protected:
fIsReady = true;
}

#ifndef DGL_FILE_BROWSER_DISABLED
# ifndef DGL_FILE_BROWSER_DISABLED
// custom file-browser selected
void fileBrowserSelected(const char* filename) override
{
@@ -220,26 +236,13 @@ protected:

fUI->uiFileBrowserSelected(filename);
}
#endif
# endif

private:
UI* const fUI;
bool fIsReady;
};
#else
static inline
UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const char* const bundlePath)
{
d_lastUiDspPtr = dspPtr;
g_nextWindowId = winId;
g_nextBundlePath = bundlePath;
UI* const ret = createUI();
d_lastUiDspPtr = nullptr;
g_nextWindowId = 0;
g_nextBundlePath = nullptr;
return ret;
}
#endif
#endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI

// -----------------------------------------------------------------------
// UI exporter class
@@ -256,13 +259,13 @@ public:
const setSizeFunc setSizeCall,
void* const dspPtr = nullptr,
const char* const bundlePath = nullptr)
#ifdef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
: fUI(createUiWrapper(dspPtr, winId, bundlePath)),
#else
: glApp(),
glWindow(glApp, winId, dspPtr),
fChangingSize(false),
fUI(glWindow.getUI()),
#else
: fUI(createUiWrapper(dspPtr, winId, bundlePath)),
#endif
fData((fUI != nullptr) ? fUI->pData : nullptr)
{
@@ -276,54 +279,65 @@ public:
fData->sendNoteCallbackFunc = sendNoteCall;
fData->setSizeCallbackFunc = setSizeCall;

#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused
return; (void)bundlePath;
#endif
}

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
~UIExporter()
{
delete fUI;
}
#endif

// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uint getWidth() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getWidth();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getWidth();
#endif
}

uint getHeight() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getHeight();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
return fUI->getHeight();
#endif
}

bool isVisible() const noexcept
{
#ifdef HAVE_DGL
return glWindow.isVisible();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
return fUI->isRunning();
#endif
}

// -------------------------------------------------------------------
intptr_t getWindowId() const noexcept
{
return 0;
}
#else
uint getWidth() const noexcept
{
return glWindow.getWidth();
}

uint getHeight() const noexcept
{
return glWindow.getHeight();
}

bool isVisible() const noexcept
{
return glWindow.isVisible();
}

intptr_t getWindowId() const noexcept
{
#ifdef HAVE_DGL
return glWindow.getWindowId();
#else
return 0;
#endif
}
#endif

// -------------------------------------------------------------------

@@ -365,7 +379,39 @@ public:

// -------------------------------------------------------------------

#ifdef HAVE_DGL
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void exec(IdleCallback* const cb)
{
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->setVisible(true);
cb->idleCallback();

while (fUI->isRunning())
{
d_msleep(10);
cb->idleCallback();
}
}

void exec_idle()
{
}

bool idle()
{
return true;
}

void quit()
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->setVisible(false);
fUI->terminateAndWaitForProcess();
}
#else
void exec(IdleCallback* const cb)
{
DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
@@ -381,48 +427,64 @@ public:
if (glWindow.isReady())
fUI->uiIdle();
}
#endif

bool idle()
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);

#ifdef HAVE_DGL
glApp.idle();

if (glWindow.isReady())
fUI->uiIdle();

return ! glApp.isQuiting();
#else
return fUI->isRunning();
#endif
}

void quit()
{
#ifdef HAVE_DGL
glWindow.close();
glApp.quit();
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
fUI->terminateAndWaitForProcess();
#endif
}

#endif
// -------------------------------------------------------------------

#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
void setWindowTitle(const char* const uiTitle)
{
#ifdef HAVE_DGL
glWindow.setTitle(uiTitle);
#else
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->setTitle(uiTitle);
#endif
}

#ifdef HAVE_DGL
void setWindowSize(const uint width, const uint height, const bool = false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->setSize(width, height);
}

void setWindowTransientWinId(const uintptr_t winId)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);

fUI->setTransientWinId(winId);
}

bool setWindowVisible(const bool yesNo)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);

fUI->setVisible(yesNo);

return fUI->isRunning();
}
#else
void setWindowTitle(const char* const uiTitle)
{
glWindow.setTitle(uiTitle);
}

void setWindowSize(const uint width, const uint height, const bool updateUI = false)
{
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
@@ -459,10 +521,6 @@ public:
{
return glWindow.handlePluginSpecial(press, key);
}
#else
void setWindowSize(const uint, const uint, const bool = false) {}
void setWindowTransientWinId(const uintptr_t) {}
bool setWindowVisible(const bool) { return true; }
#endif

// -------------------------------------------------------------------
@@ -483,7 +541,7 @@ public:
}

private:
#ifdef HAVE_DGL
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// -------------------------------------------------------------------
// DGL Application and Window for this widget



+ 36
- 0
examples/ExternalUI/DistrhoPluginInfo.h View File

@@ -0,0 +1,36 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 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_PLUGIN_INFO_H_INCLUDED
#define DISTRHO_PLUGIN_INFO_H_INCLUDED

#define DISTRHO_PLUGIN_BRAND "DISTRHO"
#define DISTRHO_PLUGIN_NAME "ExternalUI"
#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/ExternalUI"

#define DISTRHO_PLUGIN_HAS_UI 1
#define DISTRHO_PLUGIN_HAS_EMBED_UI 0
#define DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_NUM_INPUTS 1
#define DISTRHO_PLUGIN_NUM_OUTPUTS 1

enum Parameters {
kParameterLevel = 0,
kParameterCount
};

#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED

+ 188
- 0
examples/ExternalUI/ExternalExamplePlugin.cpp View File

@@ -0,0 +1,188 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 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.
*/
#include "DistrhoPlugin.hpp"
START_NAMESPACE_DISTRHO
// -----------------------------------------------------------------------------------------------------------
/**
Plugin to show how to get some basic information sent to the UI.
*/
class ExternalExamplePlugin : public Plugin
{
public:
ExternalExamplePlugin()
: Plugin(kParameterCount, 0, 0),
fValue(0.0f)
{
}
protected:
/* --------------------------------------------------------------------------------------------------------
* Information */
/**
Get the plugin label.
This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters.
*/
const char* getLabel() const override
{
return "ExternalUI";
}
/**
Get an extensive comment/description about the plugin.
*/
const char* getDescription() const override
{
return "Plugin to show how to use an external / remote UI.";
}
/**
Get the plugin author/maker.
*/
const char* getMaker() const override
{
return "DISTRHO";
}
/**
Get the plugin homepage.
*/
const char* getHomePage() const override
{
return "https://github.com/DISTRHO/DPF";
}
/**
Get the plugin license name (a single line of text).
For commercial plugins this should return some short copyright information.
*/
const char* getLicense() const override
{
return "ISC";
}
/**
Get the plugin version, in hexadecimal.
*/
uint32_t getVersion() const override
{
return d_version(1, 0, 0);
}
/**
Get the plugin unique Id.
This value is used by LADSPA, DSSI and VST plugin formats.
*/
int64_t getUniqueId() const override
{
return d_cconst('d', 'E', 'x', 't');
}
/* --------------------------------------------------------------------------------------------------------
* Init */
/**
Initialize the parameter @a index.
This function will be called once, shortly after the plugin is created.
*/
void initParameter(uint32_t index, Parameter& parameter) override
{
if (index != 0)
return;
parameter.hints = kParameterIsAutomable|kParameterIsInteger;
parameter.ranges.def = 0.0f;
parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f;
parameter.name = "Value";
parameter.symbol = "value";
}
/* --------------------------------------------------------------------------------------------------------
* Internal data */
/**
Get the current value of a parameter.
The host may call this function from any context, including realtime processing.
*/
float getParameterValue(uint32_t index) const override
{
if (index != 0)
return 0.0f;
return fValue;
}
/**
Change a parameter value.
The host may call this function from any context, including realtime processing.
When a parameter is marked as automable, you must ensure no non-realtime operations are performed.
@note This function will only be called for parameter inputs.
*/
void setParameterValue(uint32_t index, float value) override
{
if (index != 0)
return;
fValue = value;
}
/* --------------------------------------------------------------------------------------------------------
* Audio/MIDI Processing */
/**
Run/process function for plugins without MIDI input.
@note Some parameters might be null if there are no audio inputs or outputs.
*/
void run(const float** inputs, float** outputs, uint32_t frames) override
{
/**
This plugin does nothing, it just demonstrates information usage.
So here we directly copy inputs over outputs, leaving the audio untouched.
We need to be careful in case the host re-uses the same buffer for both ins and outs.
*/
if (outputs[0] != inputs[0])
std::memcpy(outputs[0], inputs[0], sizeof(float)*frames);
}
// -------------------------------------------------------------------------------------------------------
private:
// Parameters
float fValue;
/**
Set our plugin class as non-copyable and add a leak detector just in case.
*/
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExamplePlugin)
};
/* ------------------------------------------------------------------------------------------------------------
* Plugin entry point, called by DPF to create a new plugin instance. */
Plugin* createPlugin()
{
return new ExternalExamplePlugin();
}
// -----------------------------------------------------------------------------------------------------------
END_NAMESPACE_DISTRHO

+ 187
- 0
examples/ExternalUI/ExternalExampleUI.cpp View File

@@ -0,0 +1,187 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2018 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.
*/

#include "DistrhoUI.hpp"

// Extra includes for current path and fifo stuff
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

START_NAMESPACE_DISTRHO

// TODO: generate a random, not-yet-existing, filename
const char* const kFifoFilename = "/tmp/dpf-fifo-test";

// Helper to get current path of this plugin
static const char* getCurrentPluginFilename()
{
Dl_info exeInfo;
void* localSymbol = (void*)kFifoFilename;
dladdr(localSymbol, &exeInfo);
return exeInfo.dli_fname;
}

// Helper to check if a file exists
static bool fileExists(const char* const filename)
{
return access(filename, F_OK) != -1;
}

// Helper function to keep trying to write until it succeeds or really errors out
static ssize_t
writeRetry(int fd, const void* src, size_t size)
{
ssize_t error;

do {
error = write(fd, src, size);
} while (error == -1 && (errno == EINTR || errno == EPIPE));

return error;
}

// -----------------------------------------------------------------------------------------------------------

class ExternalExampleUI : public UI
{
public:
ExternalExampleUI()
: UI(405, 256, true),
fFifo(-1),
fValue(0.0f),
fExternalScript(getNextBundlePath())
{
if (fExternalScript.isEmpty())
{
fExternalScript = getCurrentPluginFilename();
fExternalScript.truncate(fExternalScript.rfind('/'));
}

fExternalScript += "/d_extui.sh";
d_stdout("External script = %s", fExternalScript.buffer());
}

protected:
/* --------------------------------------------------------------------------------------------------------
* DSP/Plugin Callbacks */

/**
A parameter has changed on the plugin side.
This is called by the host to inform the UI about parameter changes.
*/
void parameterChanged(uint32_t index, float value) override
{
if (index != 0)
return;

fValue = value;

if (fFifo == -1)
return;

// NOTE: This is a terrible way to pass values, also locale might get in the way...
char valueStr[24];
std::memset(valueStr, 0, sizeof(valueStr));
std::snprintf(valueStr, 23, "%i\n", static_cast<int>(value + 0.5f));

DISTRHO_SAFE_ASSERT(writeRetry(fFifo, valueStr, 24) == sizeof(valueStr));
}

/* --------------------------------------------------------------------------------------------------------
* External Window overrides */

/**
Manage external process and IPC when UI is requested to be visible.
*/
void setVisible(const bool yesNo) override
{
if (yesNo)
{
DISTRHO_SAFE_ASSERT_RETURN(fileExists(fExternalScript),);

mkfifo(kFifoFilename, 0666);
sync();

char winIdStr[24];
std::memset(winIdStr, 0, sizeof(winIdStr));
std::snprintf(winIdStr, 23, "%lu", getTransientWinId());

const char* args[] = {
fExternalScript.buffer(),
kFifoFilename,
"--progressbar", "External UI example",
"--title", getTitle(),
nullptr,
};
DISTRHO_SAFE_ASSERT_RETURN(startExternalProcess(args),);

// NOTE: this can lockup the current thread if the other side does not read the file!
fFifo = open(kFifoFilename, O_WRONLY);
DISTRHO_SAFE_ASSERT_RETURN(fFifo != -1,);

parameterChanged(0, fValue);
}
else
{
if (fFifo != -1)
{
if (isRunning())
{
DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5);
fsync(fFifo);
}
close(fFifo);
fFifo = -1;
}

unlink(kFifoFilename);
terminateAndWaitForProcess();
}

UI::setVisible(yesNo);
}

// -------------------------------------------------------------------------------------------------------

private:
// IPC Stuff
int fFifo;

// Current value, cached for when UI becomes visible
float fValue;

// Path to external ui script
String fExternalScript;

/**
Set our UI class as non-copyable and add a leak detector just in case.
*/
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExampleUI)
};

/* ------------------------------------------------------------------------------------------------------------
* UI entry point, called by DPF to create a new UI instance. */

UI* createUI()
{
return new ExternalExampleUI();
}

// -----------------------------------------------------------------------------------------------------------

END_NAMESPACE_DISTRHO

+ 40
- 0
examples/ExternalUI/ExternalLauncher.sh View File

@@ -0,0 +1,40 @@
#!/bin/bash

# Read FIFO argument from CLI
FIFO=${1}
shift

if [ ! -e "${FIFO}" ]; then
echo "Fifo file ${FIFO} does not exist, cannot run"
exit 1
fi

# Start kdialog with all other arguments and get dbus reference
dbusRef=$(kdialog "$@" 100)

if [ $? -ne 0 ] || [ -z "${dbusRef}" ]; then
echo "Failed to start kdialog"
exit 1
fi

# Setup cancellation point for this script
quitfn() {
qdbus ${dbusRef} close 2>/dev/null
}

trap quitfn SIGINT
trap quitfn SIGTERM

# Read Fifo for new values or a quit message
while read line <"${FIFO}"; do
if echo "${line}" | grep -q "quit"; then
break
fi
if ! qdbus ${dbusRef} Set "" value "${line}"; then
break
fi
done

# Cleanup
rm -f "${FIFO}"
quitfn

+ 40
- 0
examples/ExternalUI/Makefile View File

@@ -0,0 +1,40 @@
#!/usr/bin/make -f
# Makefile for DISTRHO Plugins #
# ---------------------------- #
# Created by falkTX
#

# --------------------------------------------------------------
# Project name, used for binaries

NAME = d_extui

# --------------------------------------------------------------
# Files to build

FILES_DSP = \
ExternalExamplePlugin.cpp

FILES_UI = \
ExternalExampleUI.cpp

# --------------------------------------------------------------
# Do some magic

include ../../Makefile.plugins.mk

LINK_FLAGS += -ldl

# --------------------------------------------------------------
# Enable all possible plugin types

ifeq ($(HAVE_JACK),true)
TARGETS += jack
endif

TARGETS += dssi
TARGETS += lv2_sep

all: $(TARGETS)

# --------------------------------------------------------------

+ 11
- 0
examples/ExternalUI/README.md View File

@@ -0,0 +1,11 @@
# External UI example

This example will show how to use an external / remote UI together with DPF.<br/>

The Plugin has a shell script that calls kdialog and uses qdbus for sending values to it.<br/>
It is a very ugly way to show a remote UI (using a shell script!), but it is only to prove the point.<br/>

Note that everything regarding external UIs is still a bit experimental in DPF.<br/>
There is Unix-specific code in there.<br/>

If this is something you are interested on using and contributing to, please let us know.<br/>

Loading…
Cancel
Save