From a85679758c34a958418d58c6763659f769764ea0 Mon Sep 17 00:00:00 2001 From: falkTX Date: Mon, 26 Dec 2022 09:10:52 +0000 Subject: [PATCH] Setup mini variant UI, move around more code to accomodate this Signed-off-by: falkTX --- dpf | 2 +- plugins/Makefile | 6 +- plugins/plugins-mini.cpp | 1 + src/CardinalCommon.cpp | 300 +++++++++++++++++++++++++ src/CardinalCommon.hpp | 45 +++- src/CardinalMini/DistrhoPluginInfo.h | 4 +- src/CardinalPlugin.cpp | 314 +-------------------------- src/CardinalUI.cpp | 111 +++++++++- src/Makefile.cardinal.mk | 29 ++- src/PluginContext.hpp | 3 - src/extra/SharedResourcePointer.hpp | 19 ++ src/override/Window.cpp | 34 ++- 12 files changed, 534 insertions(+), 334 deletions(-) diff --git a/dpf b/dpf index 564f651..7994806 160000 --- a/dpf +++ b/dpf @@ -1 +1 @@ -Subproject commit 564f6519b4bbf1c6cc8791a9adbb377f6cfd4984 +Subproject commit 79948069d581c64bd19bc454234550de88b459a2 diff --git a/plugins/Makefile b/plugins/Makefile index e9bfc20..d8d7ac7 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -293,6 +293,10 @@ PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp PLUGIN_FILES += Cardinal/src/SassyScope.cpp PLUGIN_FILES += Cardinal/src/DearImGui.cpp PLUGIN_FILES += Cardinal/src/DearImGuiColorTextEditor.cpp +MINIPLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp +MINIPLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp +MINIPLUGIN_FILES += Cardinal/src/DearImGui.cpp +MINIPLUGIN_FILES += Cardinal/src/DearImGuiColorTextEditor.cpp endif ifeq ($(shell $(PKG_CONFIG) --exists fftw3f && echo true),true) @@ -1290,7 +1294,7 @@ endif ifeq ($(NOPLUGINS),true) TARGETS = noplugins$(TARGET_SUFFIX).a else -TARGETS = plugins$(TARGET_SUFFIX).a plugins-mini-headless.a +TARGETS = plugins$(TARGET_SUFFIX).a plugins-mini.a endif all: $(TARGETS) diff --git a/plugins/plugins-mini.cpp b/plugins/plugins-mini.cpp index 3c50e38..afdd096 100644 --- a/plugins/plugins-mini.cpp +++ b/plugins/plugins-mini.cpp @@ -189,6 +189,7 @@ void destroyStaticPlugins() void updateStaticPluginsDarkMode() { + d_stdout("TODO"); } } diff --git a/src/CardinalCommon.cpp b/src/CardinalCommon.cpp index 9a71895..ea61c0f 100644 --- a/src/CardinalCommon.cpp +++ b/src/CardinalCommon.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -65,6 +66,27 @@ # define HEADLESS #endif +#if CARDINAL_VARIANT_FX +# define CARDINAL_TEMPLATE_NAME "init/fx.vcv" +#elif CARDINAL_VARIANT_NATIVE +# define CARDINAL_TEMPLATE_NAME "init/native.vcv" +#elif CARDINAL_VARIANT_SYNTH +# define CARDINAL_TEMPLATE_NAME "init/synth.vcv" +#else +# define CARDINAL_TEMPLATE_NAME "init/main.vcv" +#endif + +namespace rack { +namespace asset { +std::string patchesPath(); +void destroy(); +} +namespace plugin { +void initStaticPlugins(); +void destroyStaticPlugins(); +} +} + const std::string CARDINAL_VERSION = "22.12"; START_NAMESPACE_DISTRHO @@ -172,6 +194,284 @@ void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message, plugin->writeMidiEvent(event); } +// ----------------------------------------------------------------------------------------------------------- + +#ifdef CARDINAL_INIT_OSC_THREAD +static void osc_error_handler(int num, const char* msg, const char* path) +{ + d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path); +} + +static int osc_fallback_handler(const char* const path, const char* const types, lo_arg**, int, lo_message, void*) +{ + d_stderr("Cardinal OSC unhandled message \"%s\" with types \"%s\"", path, types); + return 0; +} + +static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self) +{ + d_stdout("osc_hello_handler()"); + const lo_address source = lo_message_get_source(m); + lo_send_from(source, static_cast(self)->oscServer, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok"); + return 0; +} + +static int osc_load_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self) +{ + d_stdout("osc_load_handler()"); + DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0); + DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0); + + const int32_t size = argv[0]->blob.size; + DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0); + + const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data); + DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0); + + bool ok = false; + + if (CardinalBasePlugin* const plugin = static_cast(self)->oscPlugin) + { + CardinalPluginContext* const context = plugin->context; + std::vector data(size); + std::memcpy(data.data(), blob, size); + + rack::contextSet(context); + rack::system::removeRecursively(context->patch->autosavePath); + rack::system::createDirectories(context->patch->autosavePath); + try { + rack::system::unarchiveToDirectory(data, context->patch->autosavePath); + context->patch->loadAutosave(); + ok = true; + } + catch (rack::Exception& e) { + WARN("%s", e.what()); + } + rack::contextSet(nullptr); + } + + const lo_address source = lo_message_get_source(m); + lo_send_from(source, static_cast(self)->oscServer, + LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail"); + return 0; +} + +static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self) +{ + d_stdout("osc_screenshot_handler()"); + DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0); + DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0); + + const int32_t size = argv[0]->blob.size; + DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0); + + const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data); + DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0); + + bool ok = false; + + if (CardinalBasePlugin* const plugin = static_cast(self)->oscPlugin) + ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer()); + + const lo_address source = lo_message_get_source(m); + lo_send_from(source, static_cast(self)->oscServer, + LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail"); + return 0; +} +#endif + +Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalBaseUI* const ui) +{ + using namespace rack; + +#ifdef DISTRHO_OS_WASM + settings::allowCursorLock = true; +#else + settings::allowCursorLock = false; +#endif + settings::autoCheckUpdates = false; + settings::autosaveInterval = 0; + settings::devMode = true; + settings::isPlugin = true; + settings::skipLoadOnLaunch = true; + settings::showTipsOnLaunch = false; + settings::windowPos = math::Vec(0, 0); +#ifdef HEADLESS + settings::headless = true; +#endif + + // copied from https://community.vcvrack.com/t/16-colour-cable-palette/15951 + settings::cableColors = { + color::fromHexString("#ff5252"), + color::fromHexString("#ff9352"), + color::fromHexString("#ffd452"), + color::fromHexString("#e8ff52"), + color::fromHexString("#a8ff52"), + color::fromHexString("#67ff52"), + color::fromHexString("#52ff7d"), + color::fromHexString("#52ffbe"), + color::fromHexString("#52ffff"), + color::fromHexString("#52beff"), + color::fromHexString("#527dff"), + color::fromHexString("#6752ff"), + color::fromHexString("#a852ff"), + color::fromHexString("#e952ff"), + color::fromHexString("#ff52d4"), + color::fromHexString("#ff5293"), + }; + + system::init(); + logger::init(); + random::init(); + ui::init(); + + if (asset::systemDir.empty()) + { + if (const char* const bundlePath = (plugin != nullptr ? plugin->getBundlePath() : + ui != nullptr ? ui->getBundlePath() : nullptr)) + { + if (const char* const resourcePath = getResourcePath(bundlePath)) + { + asset::systemDir = resourcePath; + asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); + } + } + + if (asset::systemDir.empty() || ! system::exists(asset::systemDir) || ! system::exists(asset::bundlePath)) + { + #ifdef CARDINAL_PLUGIN_SOURCE_DIR + // Make system dir point to source code location as fallback + asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack"; + asset::bundlePath.clear(); + + // If source code dir does not exist use install target prefix as system dir + if (!system::exists(system::join(asset::systemDir, "res"))) + #endif + { + #if defined(DISTRHO_OS_WASM) + asset::systemDir = "/resources"; + #elif defined(ARCH_MAC) + asset::systemDir = "/Library/Application Support/Cardinal"; + #elif defined(ARCH_WIN) + const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles); + if (! commonprogfiles.empty()) + asset::systemDir = system::join(commonprogfiles, "Cardinal"); + #else + asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal"; + #endif + + asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); + } + } + + asset::userDir = asset::systemDir; + } + + const std::string patchesPath = asset::patchesPath(); + #ifdef DISTRHO_OS_WASM + templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME); + #else + templatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); + #endif + factoryTemplatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); + + // Log environment + INFO("%s %s %s, compatible with Rack version %s", APP_NAME.c_str(), APP_EDITION.c_str(), CARDINAL_VERSION.c_str(), APP_VERSION.c_str()); + INFO("%s", system::getOperatingSystemInfo().c_str()); + INFO("Binary filename: %s", getBinaryFilename()); + if (plugin != nullptr) { + INFO("Bundle path: %s", plugin->getBundlePath()); + } else if (ui != nullptr) { + INFO("Bundle path: %s", ui->getBundlePath()); + } + INFO("System directory: %s", asset::systemDir.c_str()); + INFO("User directory: %s", asset::userDir.c_str()); + INFO("Template patch: %s", templatePath.c_str()); + INFO("System template patch: %s", factoryTemplatePath.c_str()); + + // Report to user if something is wrong with the installation + if (asset::systemDir.empty()) + { + d_stderr2("Failed to locate Cardinal plugin bundle.\n" + "Install Cardinal with its bundle folder intact and try again."); + } + else if (! system::exists(asset::systemDir)) + { + d_stderr2("System directory \"%s\" does not exist.\n" + "Make sure Cardinal was downloaded and installed correctly.", asset::systemDir.c_str()); + } + + INFO("Initializing plugins"); + plugin::initStaticPlugins(); + + INFO("Initializing plugin browser DB"); + app::browserInit(); + +#ifdef CARDINAL_INIT_OSC_THREAD + INFO("Initializing OSC Remote control"); + oscServer = lo_server_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler); + DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,); + + lo_server_add_method(oscServer, "/hello", "", osc_hello_handler, this); + lo_server_add_method(oscServer, "/load", "b", osc_load_handler, this); + lo_server_add_method(oscServer, "/screenshot", "b", osc_screenshot_handler, this); + lo_server_add_method(oscServer, nullptr, nullptr, osc_fallback_handler, nullptr); + + startThread(); +#else + INFO("OSC Remote control is not enabled in this build"); +#endif +} + +Initializer::~Initializer() +{ + using namespace rack; + +#ifdef CARDINAL_INIT_OSC_THREAD + if (oscServer != nullptr) + { + stopThread(5000); + lo_server_del_method(oscServer, nullptr, nullptr); + lo_server_free(oscServer); + oscServer = nullptr; + } +#endif + + INFO("Clearing asset paths"); + asset::bundlePath.clear(); + asset::systemDir.clear(); + asset::userDir.clear(); + + INFO("Destroying plugins"); + plugin::destroyStaticPlugins(); + + INFO("Destroying colourized assets"); + asset::destroy(); + + INFO("Destroying settings"); + settings::destroy(); + + INFO("Destroying logger"); + logger::destroy(); +} + +#ifdef CARDINAL_INIT_OSC_THREAD +void Initializer::run() +{ + INFO("OSC Thread Listening for remote commands"); + + while (! shouldThreadExit()) + { + d_msleep(200); + while (lo_server_recv_noblock(oscServer, 0) != 0) {} + } + + INFO("OSC Thread Closed"); +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + END_NAMESPACE_DISTRHO // -------------------------------------------------------------------------------------------------------------------- diff --git a/src/CardinalCommon.hpp b/src/CardinalCommon.hpp index 188a454..601fad4 100644 --- a/src/CardinalCommon.hpp +++ b/src/CardinalCommon.hpp @@ -15,14 +15,17 @@ * For a full copy of the GNU General Public License see the LICENSE file. */ -#include - #pragma once +#include "DistrhoUtils.hpp" + +#include + #ifdef HAVE_LIBLO // # define REMOTE_HOST "localhost" # define REMOTE_HOST "192.168.51.1" # define REMOTE_HOST_PORT "2228" +# include "extra/Thread.hpp" #endif #ifdef DISTRHO_OS_WASM @@ -35,6 +38,8 @@ extern const std::string CARDINAL_VERSION; +// ----------------------------------------------------------------------------------------------------------- + namespace rack { namespace ui { @@ -65,6 +70,8 @@ extern char* patchStorageSlug; } // namespace rack +// ----------------------------------------------------------------------------------------------------------- + namespace patchUtils { void loadDialog(); @@ -86,3 +93,37 @@ void deployToRemote(); void sendScreenshotToRemote(const char* screenshot); } // namespace patchUtils + +// ----------------------------------------------------------------------------------------------------------- + +#if defined(HAVE_LIBLO) && defined(HEADLESS) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +# define CARDINAL_INIT_OSC_THREAD +#endif + +START_NAMESPACE_DISTRHO + +class CardinalBasePlugin; +class CardinalBaseUI; + +struct Initializer +#ifdef CARDINAL_INIT_OSC_THREAD + : public Thread +#endif +{ +#ifdef CARDINAL_INIT_OSC_THREAD + lo_server oscServer = nullptr; + CardinalBasePlugin* oscPlugin = nullptr; +#endif + std::string templatePath; + std::string factoryTemplatePath; + + Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui); + ~Initializer(); +#ifdef CARDINAL_INIT_OSC_THREAD + void run() override; +#endif +}; + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- diff --git a/src/CardinalMini/DistrhoPluginInfo.h b/src/CardinalMini/DistrhoPluginInfo.h index a27e82e..ac6a942 100644 --- a/src/CardinalMini/DistrhoPluginInfo.h +++ b/src/CardinalMini/DistrhoPluginInfo.h @@ -31,8 +31,8 @@ #define DISTRHO_PLUGIN_URI "https://distrho.kx.studio/plugins/cardinal#mini" #define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.cardinal#mini" -#define DISTRHO_PLUGIN_NAME "Cardinal FX" -#define DISTRHO_PLUGIN_LABEL "CardinalFX" +#define DISTRHO_PLUGIN_NAME "Cardinal Mini" +#define DISTRHO_PLUGIN_LABEL "CardinalMini" #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0 diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index f883949..7ec6012 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -15,7 +15,6 @@ * For a full copy of the GNU General Public License see the LICENSE file. */ -#include #include #include #include @@ -24,7 +23,6 @@ #include #include -#include #include #include #include @@ -52,16 +50,6 @@ # include "extra/SharedResourcePointer.hpp" #endif -#if CARDINAL_VARIANT_FX -# define CARDINAL_TEMPLATE_NAME "init/fx.vcv" -#elif CARDINAL_VARIANT_NATIVE -# define CARDINAL_TEMPLATE_NAME "init/native.vcv" -#elif CARDINAL_VARIANT_SYNTH -# define CARDINAL_TEMPLATE_NAME "init/synth.vcv" -#else -# define CARDINAL_TEMPLATE_NAME "init/main.vcv" -#endif - static const constexpr uint kCardinalStateBaseCount = 3; // patch, screenshot, comment #ifndef HEADLESS @@ -76,17 +64,9 @@ static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount; extern const std::string CARDINAL_VERSION; namespace rack { -namespace asset { -std::string patchesPath(); -void destroy(); -} namespace engine { void Engine_setAboutToClose(Engine*); } -namespace plugin { -void initStaticPlugins(); -void destroyStaticPlugins(); -} } START_NAMESPACE_DISTRHO @@ -98,6 +78,10 @@ bool d_isDiffHigherThanLimit(const T& v1, const T& v2, const T& limit) return v1 != v2 ? (v1 > v2 ? v1 - v2 : v2 - v1) > limit : false; } +#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +const char* UI::getBundlePath() const noexcept { return nullptr; } +#endif + // ----------------------------------------------------------------------------------------------------------- #ifdef DISTRHO_OS_WASM @@ -143,288 +127,6 @@ static char* getPatchStorageSlug() { // ----------------------------------------------------------------------------------------------------------- -struct Initializer -#if defined(HAVE_LIBLO) && defined(HEADLESS) -: public Thread -#endif -{ -#if defined(HAVE_LIBLO) && defined(HEADLESS) - lo_server oscServer = nullptr; - CardinalBasePlugin* oscPlugin = nullptr; -#endif - std::string templatePath; - std::string factoryTemplatePath; - - Initializer(const CardinalBasePlugin* const plugin) - { - using namespace rack; - -#ifdef DISTRHO_OS_WASM - settings::allowCursorLock = true; -#else - settings::allowCursorLock = false; -#endif - settings::autoCheckUpdates = false; - settings::autosaveInterval = 0; - settings::devMode = true; - settings::isPlugin = true; - settings::skipLoadOnLaunch = true; - settings::showTipsOnLaunch = false; - settings::windowPos = math::Vec(0, 0); -#ifdef HEADLESS - settings::headless = true; -#endif - - // copied from https://community.vcvrack.com/t/16-colour-cable-palette/15951 - settings::cableColors = { - color::fromHexString("#ff5252"), - color::fromHexString("#ff9352"), - color::fromHexString("#ffd452"), - color::fromHexString("#e8ff52"), - color::fromHexString("#a8ff52"), - color::fromHexString("#67ff52"), - color::fromHexString("#52ff7d"), - color::fromHexString("#52ffbe"), - color::fromHexString("#52ffff"), - color::fromHexString("#52beff"), - color::fromHexString("#527dff"), - color::fromHexString("#6752ff"), - color::fromHexString("#a852ff"), - color::fromHexString("#e952ff"), - color::fromHexString("#ff52d4"), - color::fromHexString("#ff5293"), - }; - - system::init(); - logger::init(); - random::init(); - ui::init(); - - if (asset::systemDir.empty()) - { - if (const char* const bundlePath = plugin->getBundlePath()) - { - if (const char* const resourcePath = getResourcePath(bundlePath)) - { - asset::systemDir = resourcePath; - asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); - } - } - - if (asset::systemDir.empty() || ! system::exists(asset::systemDir) || ! system::exists(asset::bundlePath)) - { - #ifdef CARDINAL_PLUGIN_SOURCE_DIR - // Make system dir point to source code location as fallback - asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack"; - asset::bundlePath.clear(); - - // If source code dir does not exist use install target prefix as system dir - if (!system::exists(system::join(asset::systemDir, "res"))) - #endif - { - #if defined(DISTRHO_OS_WASM) - asset::systemDir = "/resources"; - #elif defined(ARCH_MAC) - asset::systemDir = "/Library/Application Support/Cardinal"; - #elif defined(ARCH_WIN) - const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles); - if (! commonprogfiles.empty()) - asset::systemDir = system::join(commonprogfiles, "Cardinal"); - #else - asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal"; - #endif - - asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); - } - } - - asset::userDir = asset::systemDir; - } - - const std::string patchesPath = asset::patchesPath(); - #ifdef DISTRHO_OS_WASM - templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME); - #else - templatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); - #endif - factoryTemplatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); - - // Log environment - INFO("%s %s %s, compatible with Rack version %s", APP_NAME.c_str(), APP_EDITION.c_str(), CARDINAL_VERSION.c_str(), APP_VERSION.c_str()); - INFO("%s", system::getOperatingSystemInfo().c_str()); - INFO("Binary filename: %s", getBinaryFilename()); - INFO("Bundle path: %s", plugin->getBundlePath()); - INFO("System directory: %s", asset::systemDir.c_str()); - INFO("User directory: %s", asset::userDir.c_str()); - INFO("Template patch: %s", templatePath.c_str()); - INFO("System template patch: %s", factoryTemplatePath.c_str()); - - // Report to user if something is wrong with the installation - if (asset::systemDir.empty()) - { - d_stderr2("Failed to locate Cardinal plugin bundle.\n" - "Install Cardinal with its bundle folder intact and try again."); - } - else if (! system::exists(asset::systemDir)) - { - d_stderr2("System directory \"%s\" does not exist.\n" - "Make sure Cardinal was downloaded and installed correctly.", asset::systemDir.c_str()); - } - - INFO("Initializing plugins"); - plugin::initStaticPlugins(); - - INFO("Initializing plugin browser DB"); - app::browserInit(); - -#if defined(HAVE_LIBLO) && defined(HEADLESS) - INFO("Initializing OSC Remote control"); - oscServer = lo_server_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler); - DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,); - - lo_server_add_method(oscServer, "/hello", "", osc_hello_handler, this); - lo_server_add_method(oscServer, "/load", "b", osc_load_handler, this); - lo_server_add_method(oscServer, "/screenshot", "b", osc_screenshot_handler, this); - lo_server_add_method(oscServer, nullptr, nullptr, osc_fallback_handler, nullptr); - - startThread(); -#elif defined(HEADLESS) - INFO("OSC Remote control is not enabled in this build"); -#endif - } - - ~Initializer() - { - using namespace rack; - -#if defined(HAVE_LIBLO) && defined(HEADLESS) - if (oscServer != nullptr) - { - stopThread(5000); - lo_server_del_method(oscServer, nullptr, nullptr); - lo_server_free(oscServer); - oscServer = nullptr; - } -#endif - - INFO("Clearing asset paths"); - asset::bundlePath.clear(); - asset::systemDir.clear(); - asset::userDir.clear(); - - INFO("Destroying plugins"); - plugin::destroyStaticPlugins(); - - INFO("Destroying colourized assets"); - asset::destroy(); - - INFO("Destroying settings"); - settings::destroy(); - - INFO("Destroying logger"); - logger::destroy(); - } - -#if defined(HAVE_LIBLO) && defined(HEADLESS) - void run() override - { - INFO("OSC Thread Listening for remote commands"); - - while (! shouldThreadExit()) - { - d_msleep(200); - while (lo_server_recv_noblock(oscServer, 0) != 0) {} - } - - INFO("OSC Thread Closed"); - } - - static void osc_error_handler(int num, const char* msg, const char* path) - { - d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path); - } - - static int osc_fallback_handler(const char* const path, const char* const types, lo_arg**, int, lo_message, void*) - { - d_stderr("Cardinal OSC unhandled message \"%s\" with types \"%s\"", path, types); - return 0; - } - - static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self) - { - d_stdout("osc_hello_handler()"); - const lo_address source = lo_message_get_source(m); - lo_send_from(source, static_cast(self)->oscServer, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok"); - return 0; - } - - static int osc_load_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self) - { - d_stdout("osc_load_handler()"); - DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0); - DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0); - - const int32_t size = argv[0]->blob.size; - DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0); - - const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data); - DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0); - - bool ok = false; - - if (CardinalBasePlugin* const plugin = static_cast(self)->oscPlugin) - { - CardinalPluginContext* const context = plugin->context; - std::vector data(size); - std::memcpy(data.data(), blob, size); - - rack::contextSet(context); - rack::system::removeRecursively(context->patch->autosavePath); - rack::system::createDirectories(context->patch->autosavePath); - try { - rack::system::unarchiveToDirectory(data, context->patch->autosavePath); - context->patch->loadAutosave(); - ok = true; - } - catch (rack::Exception& e) { - WARN("%s", e.what()); - } - rack::contextSet(nullptr); - } - - const lo_address source = lo_message_get_source(m); - lo_send_from(source, static_cast(self)->oscServer, - LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail"); - return 0; - } - - static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self) - { - d_stdout("osc_screenshot_handler()"); - DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0); - DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0); - - const int32_t size = argv[0]->blob.size; - DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0); - - const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data); - DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0); - - bool ok = false; - - if (CardinalBasePlugin* const plugin = static_cast(self)->oscPlugin) - ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer()); - - const lo_address source = lo_message_get_source(m); - lo_send_from(source, static_cast(self)->oscServer, - LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail"); - return 0; - } -#endif -}; - -// ----------------------------------------------------------------------------------------------------------- - struct ScopedContext { ScopedContext(const CardinalBasePlugin* const plugin) { @@ -479,9 +181,9 @@ public: CardinalPlugin() : CardinalBasePlugin(kModuleParameters + kWindowParameterCount + 1, 0, kCardinalStateCount), #ifdef DISTRHO_OS_WASM - fInitializer(new Initializer(this)), + fInitializer(new Initializer(this, static_cast(nullptr))), #else - fInitializer(this), + fInitializer(this, static_cast(nullptr)), #endif #if DISTRHO_PLUGIN_NUM_INPUTS != 0 fAudioBufferCopy(nullptr), @@ -571,14 +273,14 @@ public: context->patch->templatePath = context->patch->factoryTemplatePath; } - #if defined(HAVE_LIBLO) && defined(HEADLESS) + #ifdef CARDINAL_INIT_OSC_THREAD fInitializer->oscPlugin = this; #endif } ~CardinalPlugin() override { - #if defined(HAVE_LIBLO) && defined(HEADLESS) + #ifdef CARDINAL_INIT_OSC_THREAD fInitializer->oscPlugin = nullptr; #endif diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index 49caa38..451dd9b 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -39,7 +39,6 @@ # include # include # include -# include "CardinalCommon.hpp" # include #endif @@ -47,15 +46,27 @@ # undef DEBUG #endif -#include +#include "Application.hpp" #include "AsyncDialog.hpp" +#include "CardinalCommon.hpp" #include "PluginContext.hpp" #include "WindowParameters.hpp" +#ifndef DISTRHO_OS_WASM +# include "extra/SharedResourcePointer.hpp" +#endif + +#ifndef HEADLESS +# include "extra/ScopedValueSetter.hpp" +#endif + namespace rack { namespace app { widget::Widget* createMenuBar(bool isStandalone); } +namespace engine { +void Engine_setAboutToClose(Engine*); +} namespace window { void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui); void WindowSetMods(Window* window, int mods); @@ -65,7 +76,14 @@ namespace window { START_NAMESPACE_DISTRHO -// ----------------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- + +#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS +const char* Plugin::getBundlePath() const noexcept { return nullptr; } +bool Plugin::writeMidiEvent(const MidiEvent&) noexcept { return false; } +#endif + +// -------------------------------------------------------------------------------------------------------------------- #ifdef DISTRHO_OS_WASM struct WasmWelcomeDialog : rack::widget::OpaqueWidget @@ -268,6 +286,15 @@ static void downloadRemotePatchSucceeded(const char* const filename) class CardinalUI : public CardinalBaseUI, public WindowParametersCallback { + #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + #ifdef DISTRHO_OS_WASM + ScopedPointer fInitializer; + #else + SharedResourcePointer fInitializer; + #endif + std::string fAutosavePath; + #endif + rack::math::Vec lastMousePos; WindowParameters windowParameters; int rateLimitStep = 0; @@ -305,8 +332,65 @@ class CardinalUI : public CardinalBaseUI, public: CardinalUI() - : CardinalBaseUI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT) + : CardinalBaseUI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT), + #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + #ifdef DISTRHO_OS_WASM + fInitializer(new Initializer(static_cast(nullptr), this)), + #else + fInitializer(static_cast(nullptr), this), + #endif + #endif + lastMousePos() { + rack::contextSet(context); + + #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + // create unique temporary path for this instance + try { + char uidBuf[24]; + const std::string tmp = rack::system::getTempDirectory(); + + for (int i=1;; ++i) + { + std::snprintf(uidBuf, sizeof(uidBuf), "Cardinal.%04d", i); + const std::string trypath = rack::system::join(tmp, uidBuf); + + if (! rack::system::exists(trypath)) + { + if (rack::system::createDirectories(trypath)) + fAutosavePath = trypath; + break; + } + } + } DISTRHO_SAFE_EXCEPTION("create unique temporary path"); + + const float sampleRate = getSampleRate(); + rack::settings::sampleRate = sampleRate; + + context->bufferSize = 128; + context->sampleRate = sampleRate; + + context->engine = new rack::engine::Engine; + context->engine->setSampleRate(sampleRate); + + context->history = new rack::history::State; + context->patch = new rack::patch::Manager; + context->patch->autosavePath = fAutosavePath; + context->patch->templatePath = fInitializer->templatePath; + context->patch->factoryTemplatePath = fInitializer->factoryTemplatePath; + + context->event = new rack::widget::EventState; + context->scene = new rack::app::Scene; + context->event->rootWidget = context->scene; + + context->window = new rack::window::Window; + + context->patch->loadTemplate(); + context->scene->rackScroll->reset(); + // swap to factory template after first load + context->patch->templatePath = context->patch->factoryTemplatePath; + #endif + Window& window(getWindow()); window.setIgnoringKeyRepeat(true); @@ -319,8 +403,6 @@ public: if (scaleFactor != 1.0) setSize(DISTRHO_UI_DEFAULT_WIDTH * scaleFactor, DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor); - rack::contextSet(context); - rack::window::WindowSetPluginUI(context->window, this); if (rack::widget::Widget* const menuBar = context->scene->menuBar) @@ -408,6 +490,23 @@ public: rack::window::WindowSetPluginUI(context->window, nullptr); + #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + { + const ScopedContext sc(this); + context->patch->clear(); + + // do a little dance to prevent context scene deletion from saving to temp dir + #ifndef HEADLESS + const ScopedValueSetter svs(rack::settings::headless, true); + #endif + Engine_setAboutToClose(context->engine); + delete context; + } + + if (! fAutosavePath.empty()) + rack::system::removeRecursively(fAutosavePath); + #endif + rack::contextSet(nullptr); } diff --git a/src/Makefile.cardinal.mk b/src/Makefile.cardinal.mk index af12cb2..baa9f9d 100644 --- a/src/Makefile.cardinal.mk +++ b/src/Makefile.cardinal.mk @@ -201,8 +201,10 @@ endif # -------------------------------------------------------------- # Setup resources -CORE_RESOURCES = patches -CORE_RESOURCES += $(subst ../Rack/res/,,$(wildcard ../Rack/res/ComponentLibrary/*.svg ../Rack/res/fonts/*.ttf)) +CORE_RESOURCES = $(subst ../Rack/res/,,$(wildcard ../Rack/res/ComponentLibrary/*.svg ../Rack/res/fonts/*.ttf)) +# ifneq ($(CARDINAL_VARIANT),mini) +CORE_RESOURCES += patches +# endif LV2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).lv2/resources/%) VST3_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst3/Contents/Resources/%) @@ -243,15 +245,19 @@ endif # -------------------------------------------------------------- # mini variant UI -# ifeq ($(CARDINAL_VARIANT),mini) -# ifneq ($(HEADLESS)$(MOD_BUILD),true) -# FILES_UI = CardinalUI.cpp -# FILES_UI += glfw.cpp -# FILES_UI += Window.cpp -# EXTRA_UI_DEPENDENCIES = $(subst -headless,,$(EXTRA_DSP_DEPENDENCIES)) -# EXTRA_UI_LIBS = $(subst -headless,,$(EXTRA_DSP_LIBS)) -# endif -# endif +ifeq ($(CARDINAL_VARIANT),mini) +ifneq ($(HEADLESS)$(MOD_BUILD),true) +FILES_UI = CardinalUI.cpp +FILES_UI += CardinalCommon.cpp +FILES_UI += common.cpp +FILES_UI += glfw.cpp +FILES_UI += Window.cpp +EXTRA_UI_DEPENDENCIES = $(subst -headless,,$(EXTRA_DSP_DEPENDENCIES)) +EXTRA_UI_LIBS = -Wl,--start-group +EXTRA_UI_LIBS += $(subst -headless,,$(EXTRA_DSP_LIBS)) +EXTRA_UI_LIBS += -Wl,--end-group +endif +endif # -------------------------------------------------------------- # Do some magic @@ -467,6 +473,7 @@ endif all: $(TARGETS) lv2: $(LV2_RESOURCES) +lv2_sep: $(LV2_RESOURCES) vst2: $(VST2_RESOURCES) vst3: $(VST3_RESOURCES) clap: $(CLAP_RESOURCES) diff --git a/src/PluginContext.hpp b/src/PluginContext.hpp index 1b51333..2e8a612 100644 --- a/src/PluginContext.hpp +++ b/src/PluginContext.hpp @@ -189,9 +189,6 @@ public: context->tlw = nullptr; context->ui = nullptr; - #if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS - delete context; - #endif } }; #endif diff --git a/src/extra/SharedResourcePointer.hpp b/src/extra/SharedResourcePointer.hpp index 005c42e..4688ade 100644 --- a/src/extra/SharedResourcePointer.hpp +++ b/src/extra/SharedResourcePointer.hpp @@ -107,6 +107,13 @@ public: initialise_variant(variant); } + template + SharedResourcePointer(const T1* const v1, const T2* const v2) + : sharedObject(nullptr) + { + initialise_variant2(v1, v2); + } + SharedResourcePointer (const SharedResourcePointer&) : sharedObject(nullptr) { @@ -179,6 +186,18 @@ private: sharedObject = holder.sharedInstance; } + template + void initialise_variant2(const T1* const v1, const T2* const v2) + { + SharedObjectHolder& holder = getSharedObjectHolder(); + const SpinLock::ScopedLockType sl (holder.lock); + + if (++(holder.refCount) == 1) + holder.sharedInstance = new SharedObjectType(v1, v2); + + sharedObject = holder.sharedInstance; + } + // There's no need to assign to a SharedResourcePointer because every // instance of the class is exactly the same! SharedResourcePointer& operator= (const SharedResourcePointer&) = delete; diff --git a/src/override/Window.cpp b/src/override/Window.cpp index 32fddbb..12c52d9 100644 --- a/src/override/Window.cpp +++ b/src/override/Window.cpp @@ -56,6 +56,7 @@ #include "Application.hpp" #include "extra/String.hpp" #include "../CardinalCommon.hpp" +#include "../PluginContext.hpp" #include "../WindowParameters.hpp" #ifndef DGL_NO_SHARED_RESOURCES @@ -148,12 +149,14 @@ struct Window::Internal { DGL_NAMESPACE::NanoTopLevelWidget* tlw = nullptr; DISTRHO_NAMESPACE::WindowParameters params; DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr; +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS DGL_NAMESPACE::Application hiddenApp; DGL_NAMESPACE::Window hiddenWindow; NVGcontext* r_vg = nullptr; NVGcontext* r_fbVg = nullptr; NVGcontext* o_vg = nullptr; NVGcontext* o_fbVg = nullptr; +#endif math::Vec size = WINDOW_SIZE_MIN; @@ -176,12 +179,16 @@ struct Window::Internal { int fbCount = 0; Internal() +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS : hiddenApp(false), hiddenWindow(hiddenApp) { hiddenWindow.setIgnoringKeyRepeat(true); hiddenApp.idle(); } +#else + {} +#endif }; @@ -203,12 +210,17 @@ static int loadFallbackFont(NVGcontext* const vg) Window::Window() { internal = new Internal; - DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow); - // Set up NanoVG const int nvgFlags = NVG_ANTIALIAS; + +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS + DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow); vg = nvgCreateGL(nvgFlags); +#else + vg = static_cast(APP)->tlw->getContext(); +#endif DISTRHO_SAFE_ASSERT_RETURN(vg != nullptr,); + #ifdef NANOVG_GLES2 fbVg = nvgCreateSharedGLES2(vg, nvgFlags); #else @@ -268,6 +280,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw) window->internal->tlw = tlw; window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight()); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS // Set up NanoVG window->internal->r_vg = tlw->getContext(); #ifdef NANOVG_GLES2 @@ -299,6 +312,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw) image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); } +#endif // Init settings WindowParametersRestore(window); @@ -311,6 +325,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw) widget::Widget::ContextDestroyEvent e; APP->scene->onContextDestroy(e); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS // swap contexts window->uiFont->vg = window->internal->o_vg; window->vg = window->internal->o_vg; @@ -338,6 +353,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw) nvgDeleteGLES2(window->internal->r_fbVg); #else nvgDeleteGL2(window->internal->r_fbVg); +#endif #endif window->internal->tlw = nullptr; @@ -375,6 +391,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) window->internal->ui = ui; window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight()); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS // Set up NanoVG window->internal->r_vg = ui->getContext(); #ifdef NANOVG_GLES2 @@ -406,6 +423,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); } +#endif // Init settings WindowParametersRestore(window); @@ -418,6 +436,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) widget::Widget::ContextDestroyEvent e; APP->scene->onContextDestroy(e); +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS // swap contexts window->uiFont->vg = window->internal->o_vg; window->vg = window->internal->o_vg; @@ -445,6 +464,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) nvgDeleteGLES2(window->internal->r_fbVg); #else nvgDeleteGL2(window->internal->r_fbVg); +#endif #endif window->internal->tlw = nullptr; @@ -460,9 +480,11 @@ void WindowSetMods(Window* const window, const int mods) Window::~Window() { { +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow); internal->hiddenWindow.close(); internal->hiddenApp.idle(); +#endif // Fonts and Images in the cache must be deleted before the NanoVG context is deleted internal->fontCache.clear(); @@ -470,12 +492,20 @@ Window::~Window() { if (vg != nullptr) { +#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #if defined NANOVG_GLES2 nvgDeleteGLES2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg); nvgDeleteGLES2(internal->o_vg != nullptr ? internal->o_vg : vg); #else nvgDeleteGL2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg); nvgDeleteGL2(internal->o_vg != nullptr ? internal->o_vg : vg); +#endif +#else +#if defined NANOVG_GLES2 + nvgDeleteGLES2(fbVg); +#else + nvgDeleteGL2(fbVg); +#endif #endif } }