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