Signed-off-by: falkTX <falktx@gmail.com>vstgui
@@ -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 | |||
@@ -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,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) | |||
}; | |||
@@ -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 | |||
@@ -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 | |||
// ----------------------------------------------------------------------------------------------------------- | |||
@@ -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"); | |||
} | |||
// ------------------------------------------------------------------- | |||
@@ -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 | |||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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) | |||
# -------------------------------------------------------------- |
@@ -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/> |