From d6dcdfff55e7b2016394c8df7100487598747ced Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 7 Oct 2021 21:23:11 +0100 Subject: [PATCH] Dump of macOS tests, starting integrating some code, very WIP --- README.md | 5 +- dpf | 2 +- plugins/CVCRack/CVCRackPlugin.cpp | 91 +++++++ plugins/CVCRack/CVCRackUI.cpp | 88 ++++++- plugins/CVCRack/DistrhoPluginInfo.h | 3 + plugins/CVCRack/Makefile | 26 +- plugins/CVCRack/Window.cpp | 381 ++++++++++++++++++++++++++++ plugins/CVCRack/dep.cpp | 19 ++ 8 files changed, 593 insertions(+), 22 deletions(-) create mode 100644 plugins/CVCRack/Window.cpp create mode 100644 plugins/CVCRack/dep.cpp diff --git a/README.md b/README.md index a1c2e1d..6a273f1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # DISTRHO CVCRack -WORK IN PROGRESS +WORK IN PROGRESS - nothing to see here + +Just an experiment to see how far we can go about a VCV plugin version that is opensource and uses original VCV source core directly. +Maybe it will work, maybe not, hard to say at this point. This is a DPF'ied build of [VCVRack](https://github.com/VCVRack/Rack), allowing it to be used as an audio plugin. diff --git a/dpf b/dpf index c4e1210..23f8956 160000 --- a/dpf +++ b/dpf @@ -1 +1 @@ -Subproject commit c4e1210897e804a19b3fa35d542765b3feb0236e +Subproject commit 23f89562acbd637a23b9f0333877939ad26c0595 diff --git a/plugins/CVCRack/CVCRackPlugin.cpp b/plugins/CVCRack/CVCRackPlugin.cpp index 6375e81..9736c68 100644 --- a/plugins/CVCRack/CVCRackPlugin.cpp +++ b/plugins/CVCRack/CVCRackPlugin.cpp @@ -14,12 +14,102 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + #include "DistrhoPlugin.hpp" START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- +struct Initializer { + Initializer() + { + using namespace rack; + + settings::devMode = true; + system::init(); + asset::init(); + logger::init(); + random::init(); + + // Log environment + INFO("%s %s v%s", APP_NAME.c_str(), APP_EDITION.c_str(), APP_VERSION.c_str()); + INFO("%s", system::getOperatingSystemInfo().c_str()); + INFO("System directory: %s", asset::systemDir.c_str()); + INFO("User directory: %s", asset::userDir.c_str()); + INFO("System time: %s", string::formatTimeISO(system::getUnixTime()).c_str()); + + // Load settings + settings::init(); + try { + settings::load(); + } + catch (Exception& e) { + std::string message = e.what(); + message += "\n\nResetting settings to default"; + d_stdout(message.c_str()); + /* + if (!osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK_CANCEL, msg.c_str())) { + exit(1); + } + */ + } + + // Check existence of the system res/ directory + std::string resDir = asset::system("res"); + if (!system::isDirectory(resDir)) { + std::string message = string::f("Rack's resource directory \"%s\" does not exist. Make sure Rack is correctly installed and launched.", resDir.c_str()); + d_stderr2(message.c_str()); + /* + osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, message.c_str()); + */ + exit(1); + } + + INFO("Initializing environment"); + // network::init(); + audio::init(); + // rtaudioInit(); + midi::init(); + // rtmidiInit(); + keyboard::init(); + plugin::init(); + library::init(); + // discord::init(); + } + + ~Initializer() + { + using namespace rack; + + // discord::destroy(); + library::destroy(); + midi::destroy(); + audio::destroy(); + plugin::destroy(); + INFO("Destroying logger"); + logger::destroy(); + } +}; + +static Initializer& getInitializerInstance() +{ + static Initializer init; + return init; +} + /** Plugin to demonstrate parameter outputs using meters. */ @@ -130,6 +220,7 @@ private: Plugin* createPlugin() { + getInitializerInstance(); return new CVCRackPlugin(); } diff --git a/plugins/CVCRack/CVCRackUI.cpp b/plugins/CVCRack/CVCRackUI.cpp index d867f5e..9a7abd9 100644 --- a/plugins/CVCRack/CVCRackUI.cpp +++ b/plugins/CVCRack/CVCRackUI.cpp @@ -14,19 +14,92 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +#include +#include +#include +#include + #include "DistrhoUI.hpp" +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; } +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char*) {} +GLFWAPI const char* glfwGetKeyName(int key, int scancode) { return nullptr; } +GLFWAPI int glfwGetKeyScancode(int key) { return 0; } + +namespace rack { +namespace window { + DISTRHO_NAMESPACE::UI* lastUI = nullptr; +} +} + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- +struct Initializer { + Initializer() + { + using namespace rack; + + ui::init(); + window::init(); + } + + ~Initializer() + { + using namespace rack; + + window::destroy(); + ui::destroy(); + } +}; + +static Initializer& getInitializerInstance() +{ + static Initializer init; + return init; +} + class CVCRackUI : public UI { public: CVCRackUI() - : UI(128, 512) + : UI(1280, 720) { - setGeometryConstraints(32, 128, false); + using namespace rack; + + // Initialize context + INFO("Initializing context"); + window::lastUI = this; + contextSet(new Context); + APP->engine = new engine::Engine; + APP->history = new history::State; + APP->event = new widget::EventState; + APP->scene = new app::Scene; + APP->event->rootWidget = APP->scene; + APP->patch = new patch::Manager; + /*if (!settings::headless)*/ { + APP->window = new window::Window; + } + window::lastUI = nullptr; + + APP->engine->startFallbackThread(); + } + + ~CVCRackUI() override + { + using namespace rack; + + delete APP; + contextSet(NULL); + } + + void onNanoDisplay() override + { + APP->window->step(); } protected: @@ -41,16 +114,6 @@ protected: { } - /* -------------------------------------------------------------------------------------------------------- - * Widget Callbacks */ - - /** - The drawing function. - */ - void onDisplay() override - { - } - // ------------------------------------------------------------------------------------------------------- private: @@ -65,6 +128,7 @@ private: UI* createUI() { + getInitializerInstance(); return new CVCRackUI(); } diff --git a/plugins/CVCRack/DistrhoPluginInfo.h b/plugins/CVCRack/DistrhoPluginInfo.h index ec1fe13..8324656 100644 --- a/plugins/CVCRack/DistrhoPluginInfo.h +++ b/plugins/CVCRack/DistrhoPluginInfo.h @@ -28,6 +28,9 @@ #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 // #define DISTRHO_PLUGIN_LV2_CATEGORY "lv2:AnalyserPlugin" // #define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Analyzer" +// #define DISTRHO_PLUGIN_HAS_EMBED_UI 1 +// #define DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1 +#define DISTRHO_UI_USE_NANOVG 1 #define DISTRHO_UI_USER_RESIZABLE 1 enum Parameters { diff --git a/plugins/CVCRack/Makefile b/plugins/CVCRack/Makefile index 13e4aa9..5cb4089 100644 --- a/plugins/CVCRack/Makefile +++ b/plugins/CVCRack/Makefile @@ -16,11 +16,14 @@ FILES_DSP = \ CVCRackPlugin.cpp FILES_UI = \ - CVCRackUI.cpp + CVCRackUI.cpp \ + dep.cpp \ + Window.cpp # -------------------------------------------------------------- # Import base definitions +# UI_TYPE = external include ../../dpf/Makefile.base.mk # -------------------------------------------------------------- @@ -30,8 +33,6 @@ FILES_DSP += Rack/dep/pffft/pffft.c FILES_DSP += Rack/dep/pffft/fftpack.c FILES_UI += Rack/dep/oui-blendish/blendish.c -FILES_UI += Rack/dep/nanovg/src/nanovg.c -# FILES_UI += Rack/dep/glfw/deps/glad.c # FIXME dont use this FILES_UI += Rack/dep/osdialog/osdialog.c @@ -45,13 +46,15 @@ endif FILES_DSP += $(wildcard Rack/src/*.c) FILES_DSP += $(wildcard Rack/src/*/*.c) -FILES_DSP += $(filter-out Rack/src/network.cpp Rack/src/rtaudio.cpp Rack/src/rtmidi.cpp, $(wildcard Rack/src/*.cpp)) -FILES_DSP += $(wildcard Rack/src/*/*.cpp) +FILES_DSP += $(filter-out Rack/src/dep.cpp Rack/src/gamepad.cpp Rack/src/rtaudio.cpp Rack/src/rtmidi.cpp, $(wildcard Rack/src/*.cpp)) +FILES_DSP += $(filter-out Rack/src/window/Window.cpp, $(wildcard Rack/src/*/*.cpp)) -# EXTRA_LIBS = Rack/dep/lib/libcurl.a -EXTRA_LIBS += Rack/dep/lib/libglfw3.a +EXTRA_LIBS = Rack/dep/lib/libcrypto.a +EXTRA_LIBS += Rack/dep/lib/libcurl.a +# EXTRA_LIBS += Rack/dep/lib/libglfw3.a EXTRA_LIBS += Rack/dep/lib/libjansson.a EXTRA_LIBS += Rack/dep/lib/libspeexdsp.a +EXTRA_LIBS += Rack/dep/lib/libssl.a EXTRA_LIBS += Rack/dep/lib/libzstd.a ifeq ($(WINDOWS),true) @@ -76,16 +79,20 @@ endif Rack/dep/lib/%.a: $(MAKE) CMAKE="$(CMAKE) -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_INSTALL_PREFIX='$(abspath Rack/dep)'" -C Rack/dep lib/$*.a +Rack/dep/lib/libcrypto.a: Rack/dep/lib/libssl.a + # -------------------------------------------------------------- # Extra flags for VCV stuff BASE_FLAGS += -D_APP_VERSION=2.git.0 +BASE_FLAGS += -I$(DPF_PATH)/dgl/src/nanovg BASE_FLAGS += -IRack/include BASE_FLAGS += -IRack/dep/include BASE_FLAGS += -IRack/dep/filesystem/include BASE_FLAGS += -IRack/dep/fuzzysearchdatabase/src BASE_FLAGS += -IRack/dep/glfw/deps -BASE_FLAGS += -IRack/dep/nanovg/src +BASE_FLAGS += -IRack/dep/glfw/include +# BASE_FLAGS += -IRack/dep/nanovg/src BASE_FLAGS += -IRack/dep/nanosvg/src BASE_FLAGS += -IRack/dep/osdialog BASE_FLAGS += -IRack/dep/oui-blendish @@ -115,8 +122,11 @@ endif ifeq ($(MACOS),true) LINK_FLAGS += -framework IOKit +# LINK_FLAGS += -Wl,-all_load endif +# LINK_FLAGS += $(OPENGL_LIBS) + # TODO needed on windows? need to check LINK_FLAGS += -lpthread diff --git a/plugins/CVCRack/Window.cpp b/plugins/CVCRack/Window.cpp new file mode 100644 index 0000000..7a00237 --- /dev/null +++ b/plugins/CVCRack/Window.cpp @@ -0,0 +1,381 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include // used in Window::screenshot +#include // used in Window::screenshot + +#include "DistrhoUI.hpp" + +namespace rack { +namespace window { + +extern DISTRHO_NAMESPACE::UI* lastUI; + +static const math::Vec minWindowSize = math::Vec(640, 480); + + +void Font::loadFile(const std::string& filename, NVGcontext* vg) { + this->vg = vg; + handle = nvgCreateFont(vg, filename.c_str(), filename.c_str()); + if (handle < 0) + throw Exception("Failed to load font %s", filename.c_str()); + INFO("Loaded font %s", filename.c_str()); +} + + +Font::~Font() { + // There is no NanoVG deleteFont() function yet, so do nothing +} + + +std::shared_ptr Font::load(const std::string& filename) { + return APP->window->loadFont(filename); +} + + +void Image::loadFile(const std::string& filename, NVGcontext* vg) { + this->vg = vg; + handle = nvgCreateImage(vg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); + if (handle <= 0) + throw Exception("Failed to load image %s", filename.c_str()); + INFO("Loaded image %s", filename.c_str()); +} + + +Image::~Image() { + // TODO What if handle is invalid? + if (handle >= 0) + nvgDeleteImage(vg, handle); +} + + +std::shared_ptr Image::load(const std::string& filename) { + return APP->window->loadImage(filename); +} + + +struct Window::Internal { + DISTRHO_NAMESPACE::UI* ui; + + std::string lastWindowTitle; + + int lastWindowX = 0; + int lastWindowY = 0; + int lastWindowWidth = 0; + int lastWindowHeight = 0; + + int frame = 0; + bool ignoreNextMouseDelta = false; + int frameSwapInterval = -1; + double monitorRefreshRate = 0.0; + double frameTime = 0.0; + double lastFrameDuration = 0.0; + + math::Vec lastMousePos; + + std::map> fontCache; + std::map> imageCache; + + bool fbDirtyOnSubpixelChange = true; +}; + +Window::Window() { + internal = new Internal; + internal->ui = lastUI; + + vg = lastUI->getContext(); + + int err; + + const GLubyte* vendor = glGetString(GL_VENDOR); + const GLubyte* renderer = glGetString(GL_RENDERER); + const GLubyte* version = glGetString(GL_VERSION); + INFO("Renderer: %s %s", vendor, renderer); + INFO("OpenGL: %s", version); + INFO("UI pointer: %p", lastUI); + + // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. + glGetError(); + + // Load default Blendish font + uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); + bndSetFont(uiFont->handle); + + if (APP->scene) { + widget::Widget::ContextCreateEvent e; + APP->scene->onContextCreate(e); + } +} + + +Window::~Window() { + if (APP->scene) { + widget::Widget::ContextDestroyEvent e; + APP->scene->onContextDestroy(e); + } + + // Fonts and Images in the cache must be deleted before the NanoVG context is deleted + internal->fontCache.clear(); + internal->imageCache.clear(); + + // nvgDeleteClone(fbVg); + + delete internal; +} + + +math::Vec Window::getSize() { + return math::Vec(1280, 720); +} + + +void Window::setSize(math::Vec size) { + size = size.max(minWindowSize); +} + + +void Window::run() { + internal->frame = 0; +} + + +void Window::step() { + double frameTime = system::getTime(); + double lastFrameTime = internal->frameTime; + internal->frameTime = frameTime; + internal->lastFrameDuration = frameTime - lastFrameTime; + // DEBUG("%.2lf Hz", 1.0 / internal->lastFrameDuration); + double t1 = 0.0, t2 = 0.0, t3 = 0.0, t4 = 0.0, t5 = 0.0; + + // Make event handlers and step() have a clean NanoVG context + nvgReset(vg); + + bndSetFont(uiFont->handle); + nvgFillColor(vg, nvgRGBf(1, 1, 1)); + nvgStrokeColor(vg, nvgRGBf(1, 1, 1)); + + // Poll events + // Save and restore context because event handler set their own context based on which window they originate from. + Context* context = contextGet(); + // glfwPollEvents(); + contextSet(context); + + // Set window title + std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION; + if (APP->patch->path != "") { + windowTitle += " - "; + if (!APP->history->isSaved()) + windowTitle += "*"; + windowTitle += system::getFilename(APP->patch->path); + } + if (windowTitle != internal->lastWindowTitle) { + internal->ui->getWindow().setTitle(windowTitle.c_str()); + internal->lastWindowTitle = windowTitle; + } + + // Get desired pixel ratio + float newPixelRatio = internal->ui->getScaleFactor(); + if (newPixelRatio != pixelRatio) { + pixelRatio = newPixelRatio; + APP->event->handleDirty(); + } + + // Get framebuffer/window ratio + int winWidth = internal->ui->getWidth(); + int winHeight = internal->ui->getHeight(); + int fbWidth = winWidth;// * newPixelRatio; + int fbHeight = winHeight;// * newPixelRatio; + windowRatio = (float)fbWidth / winWidth; + t1 = system::getTime(); + + if (APP->scene) { + // DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth); + // Resize scene + APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio); + + // Step scene + APP->scene->step(); + t2 = system::getTime(); + + // Render scene + bool visible = true; + if (visible) { + // Update and render + nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); + nvgScale(vg, pixelRatio, pixelRatio); + + // Draw scene + widget::Widget::DrawArgs args; + args.vg = vg; + args.clipBox = APP->scene->box.zeroPos(); + APP->scene->draw(args); + t3 = system::getTime(); + + // glViewport(0, -winHeight, fbWidth, fbHeight); + // glClearColor(0.0, 0.0, 0.0, 1.0); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + nvgEndFrame(vg); + t4 = system::getTime(); + } + } + + t5 = system::getTime(); + + // DEBUG("pre-step %6.1f step %6.1f draw %6.1f nvgEndFrame %6.1f glfwSwapBuffers %6.1f total %6.1f", + // (t1 - frameTime) * 1e3f, + // (t2 - t1) * 1e3f, + // (t3 - t2) * 1e3f, + // (t4 - t2) * 1e3f, + // (t5 - t4) * 1e3f, + // (t5 - frameTime) * 1e3f + // ); + internal->frame++; +} + + +void Window::screenshot(const std::string&) { +} + + +void Window::screenshotModules(const std::string&, float) { +} + + +void Window::close() { + internal->ui->getWindow().close(); +} + + +void Window::cursorLock() { + if (!settings::allowCursorLock) + return; + + internal->ignoreNextMouseDelta = true; +} + + +void Window::cursorUnlock() { + if (!settings::allowCursorLock) + return; + + internal->ignoreNextMouseDelta = true; +} + + +bool Window::isCursorLocked() { + return false; +} + + +int Window::getMods() { + int mods = 0; + /* + if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) + mods |= GLFW_MOD_SHIFT; + if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) + mods |= GLFW_MOD_CONTROL; + if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS) + mods |= GLFW_MOD_ALT; + if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS) + mods |= GLFW_MOD_SUPER; + */ + return mods; +} + + +void Window::setFullScreen(bool) { +} + + +bool Window::isFullScreen() { + return false; +} + + +double Window::getMonitorRefreshRate() { + return internal->monitorRefreshRate; +} + + +double Window::getFrameTime() { + return internal->frameTime; +} + + +double Window::getLastFrameDuration() { + return internal->lastFrameDuration; +} + + +double Window::getFrameDurationRemaining() { + double frameDurationDesired = internal->frameSwapInterval / internal->monitorRefreshRate; + return frameDurationDesired - (system::getTime() - internal->frameTime); +} + + +std::shared_ptr Window::loadFont(const std::string& filename) { + const auto& pair = internal->fontCache.find(filename); + if (pair != internal->fontCache.end()) + return pair->second; + + // Load font + std::shared_ptr font; + try { + font = std::make_shared(); + font->loadFile(filename, vg); + } + catch (Exception& e) { + WARN("%s", e.what()); + font = NULL; + } + internal->fontCache[filename] = font; + return font; +} + + +std::shared_ptr Window::loadImage(const std::string& filename) { + const auto& pair = internal->imageCache.find(filename); + if (pair != internal->imageCache.end()) + return pair->second; + + // Load image + std::shared_ptr image; + try { + image = std::make_shared(); + image->loadFile(filename, vg); + } + catch (Exception& e) { + WARN("%s", e.what()); + image = NULL; + } + internal->imageCache[filename] = image; + return image; +} + + +bool& Window::fbDirtyOnSubpixelChange() { + return internal->fbDirtyOnSubpixelChange; +} + + +void init() { +} + +void destroy() { +} + + +} // namespace window +} // namespace rack diff --git a/plugins/CVCRack/dep.cpp b/plugins/CVCRack/dep.cpp new file mode 100644 index 0000000..4d8b9dc --- /dev/null +++ b/plugins/CVCRack/dep.cpp @@ -0,0 +1,19 @@ +// This source file compiles those annoying implementation-in-header libraries + +#include // for fopen_u8 + +#define GLEW_STATIC +#define GLEW_NO_GLU +#include + +#include + +#define BLENDISH_IMPLEMENTATION +#include + +#define NANOSVG_IMPLEMENTATION +#define NANOSVG_ALL_COLOR_KEYWORDS +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include