diff --git a/Makefile b/Makefile index 4c8c24a..7a41563 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ include dpf/Makefile.base.mk all: dgl plugins gen +SKIP_NANOVG = true + # -------------------------------------------------------------- dgl: @@ -40,9 +42,13 @@ clean: rm -rf plugins/CVCRack/Rack/dep/include rm -rf plugins/CVCRack/Rack/dep/lib rm -rf plugins/CVCRack/Rack/dep/share + rm -rf plugins/CVCRack/Rack/dep/curl-7.66.0 rm -rf plugins/CVCRack/Rack/dep/glew-2.1.0 rm -rf plugins/CVCRack/Rack/dep/jansson-2.12 + rm -rf plugins/CVCRack/Rack/dep/libarchive-3.4.3 + rm -rf plugins/CVCRack/Rack/dep/openssl-1.1.1d rm -rf plugins/CVCRack/Rack/dep/speexdsp-SpeexDSP-1.2rc3 + rm -rf plugins/CVCRack/Rack/dep/zstd-1.4.5 # -------------------------------------------------------------- diff --git a/dpf b/dpf index 23f8956..c17c260 160000 --- a/dpf +++ b/dpf @@ -1 +1 @@ -Subproject commit 23f89562acbd637a23b9f0333877939ad26c0595 +Subproject commit c17c260d08613ab46e13dc578104c74b5713a435 diff --git a/plugins/CVCRack/CVCRackPlugin.cpp b/plugins/CVCRack/CVCRackPlugin.cpp index 9736c68..d55a3d5 100644 --- a/plugins/CVCRack/CVCRackPlugin.cpp +++ b/plugins/CVCRack/CVCRackPlugin.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include + #include #include "DistrhoPlugin.hpp" @@ -53,6 +56,7 @@ struct Initializer { // Load settings settings::init(); + #if 0 try { settings::load(); } @@ -66,6 +70,7 @@ struct Initializer { } */ } + #endif // Check existence of the system res/ directory std::string resDir = asset::system("res"); @@ -88,12 +93,18 @@ struct Initializer { plugin::init(); library::init(); // discord::init(); + + ui::init(); + window::init(); } ~Initializer() { using namespace rack; + window::destroy(); + ui::destroy(); + // discord::destroy(); library::destroy(); midi::destroy(); @@ -104,9 +115,9 @@ struct Initializer { } }; -static Initializer& getInitializerInstance() +static const Initializer& getInitializerInstance() { - static Initializer init; + static const Initializer init; return init; } diff --git a/plugins/CVCRack/CVCRackUI.cpp b/plugins/CVCRack/CVCRackUI.cpp index 9a7abd9..30495df 100644 --- a/plugins/CVCRack/CVCRackUI.cpp +++ b/plugins/CVCRack/CVCRackUI.cpp @@ -23,6 +23,7 @@ #include #include "DistrhoUI.hpp" +#include "ResizeHandle.hpp" GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window) { return nullptr; } GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char*) {} @@ -32,6 +33,10 @@ GLFWAPI int glfwGetKeyScancode(int key) { return 0; } namespace rack { namespace window { DISTRHO_NAMESPACE::UI* lastUI = nullptr; + + void mouseButtonCallback(Window* win, int button, int action, int mods); + void cursorPosCallback(Window* win, double xpos, double ypos); + void scrollCallback(Window* win, double x, double y); } } @@ -39,35 +44,33 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- -struct Initializer { - Initializer() +struct Initializer2 { + Initializer2() { using namespace rack; - ui::init(); - window::init(); } - ~Initializer() + ~Initializer2() { using namespace rack; - window::destroy(); - ui::destroy(); } }; -static Initializer& getInitializerInstance() +static const Initializer2& getInitializer2Instance() { - static Initializer init; + static const Initializer2 init; return init; } class CVCRackUI : public UI { + ResizeHandle fResizeHandle; public: CVCRackUI() - : UI(1280, 720) + : UI(1280, 720), + fResizeHandle(this) { using namespace rack; @@ -97,11 +100,16 @@ public: contextSet(NULL); } - void onNanoDisplay() override + void onDisplay() override { APP->window->step(); } + void uiIdle() override + { + repaint(); + } + protected: /* -------------------------------------------------------------------------------------------------------- * DSP/Plugin Callbacks */ @@ -116,6 +124,61 @@ protected: // ------------------------------------------------------------------------------------------------------- + bool onMouse(const MouseEvent& ev) override + { + int button; + int mods = 0; + int action = ev.press; + + if (ev.mod & kModifierControl) + mods |= GLFW_MOD_CONTROL; + if (ev.mod & kModifierShift) + mods |= GLFW_MOD_SHIFT; + if (ev.mod & kModifierAlt) + mods |= GLFW_MOD_ALT; + + switch (ev.button) + { + case 0: + button = GLFW_MOUSE_BUTTON_MIDDLE; + break; + case 1: + button = GLFW_MOUSE_BUTTON_LEFT; + break; + case 2: + button = GLFW_MOUSE_BUTTON_RIGHT; + break; + default: + button = 0; + break; + } + + mouseButtonCallback(APP->window, button, action, mods); + return true; + } + + bool onMotion(const MotionEvent& ev) override + { + cursorPosCallback(APP->window, ev.pos.getX(), ev.pos.getY()); + return true; + } + + bool onScroll(const ScrollEvent& ev) override + { + scrollCallback(APP->window, ev.delta.getX(), ev.delta.getY()); + return true; + } + + #if 0 + void onResize(const ResizeEvent& ev) override + { + UI::onResize(ev); + // APP->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); + } + #endif + + // TODO uiFocus + private: /** Set our UI class as non-copyable and add a leak detector just in case. @@ -128,7 +191,7 @@ private: UI* createUI() { - getInitializerInstance(); + getInitializer2Instance(); return new CVCRackUI(); } diff --git a/plugins/CVCRack/DistrhoPluginInfo.h b/plugins/CVCRack/DistrhoPluginInfo.h index 8324656..0e8af8f 100644 --- a/plugins/CVCRack/DistrhoPluginInfo.h +++ b/plugins/CVCRack/DistrhoPluginInfo.h @@ -20,7 +20,7 @@ #define DISTRHO_PLUGIN_BRAND "DISTRHO" #define DISTRHO_PLUGIN_NAME "CVCRack" -#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/plugins/glBars" +#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/plugins/CVCRack" #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_NUM_INPUTS 2 @@ -30,8 +30,8 @@ // #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 +// #define DISTRHO_UI_USE_NANOVG 1 +#define DISTRHO_UI_USER_RESIZABLE 0 enum Parameters { kParameterCount diff --git a/plugins/CVCRack/Makefile b/plugins/CVCRack/Makefile index 5cb4089..8037eb2 100644 --- a/plugins/CVCRack/Makefile +++ b/plugins/CVCRack/Makefile @@ -17,13 +17,13 @@ FILES_DSP = \ FILES_UI = \ CVCRackUI.cpp \ - dep.cpp \ Window.cpp # -------------------------------------------------------------- # Import base definitions # UI_TYPE = external +SKIP_NANOVG = true include ../../dpf/Makefile.base.mk # -------------------------------------------------------------- @@ -33,6 +33,7 @@ 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 # FIXME dont use this FILES_UI += Rack/dep/osdialog/osdialog.c @@ -46,7 +47,7 @@ endif FILES_DSP += $(wildcard Rack/src/*.c) FILES_DSP += $(wildcard Rack/src/*/*.c) -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/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/libcrypto.a @@ -85,14 +86,14 @@ 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 += -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/glfw/include -# BASE_FLAGS += -IRack/dep/nanovg/src +BASE_FLAGS += -IRack/dep/nanovg/src BASE_FLAGS += -IRack/dep/nanosvg/src BASE_FLAGS += -IRack/dep/osdialog BASE_FLAGS += -IRack/dep/oui-blendish diff --git a/plugins/CVCRack/ResizeHandle.hpp b/plugins/CVCRack/ResizeHandle.hpp new file mode 100644 index 0000000..237949f --- /dev/null +++ b/plugins/CVCRack/ResizeHandle.hpp @@ -0,0 +1,185 @@ +/* + * Resize handle for DPF + * Copyright (C) 2021 Filipe Coelho + * + * 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. + */ + +#pragma once + +#include "TopLevelWidget.hpp" +#include "../dgl/Color.hpp" + +START_NAMESPACE_DGL + +/** Resize handle for DPF windows, will sit on bottom-right. */ +class ResizeHandle : public TopLevelWidget +{ +public: + /** Constructor for placing this handle on top of a window. */ + explicit ResizeHandle(Window& window) + : TopLevelWidget(window), + handleSize(16), + resizing(false) + { + resetArea(); + } + + /** Overloaded constructor, will fetch the window from an existing top-level widget. */ + explicit ResizeHandle(TopLevelWidget* const tlw) + : TopLevelWidget(tlw->getWindow()), + handleSize(16), + resizing(false) + { + resetArea(); + } + + /** Set the handle size, minimum 16. */ + void setHandleSize(const uint size) + { + handleSize = std::max(16u, size); + resetArea(); + } + +protected: + void onDisplay() override + { + const GraphicsContext& context(getGraphicsContext()); + const double lineWidth = 1.0 * getScaleFactor(); + +#ifdef DGL_OPENGL + glUseProgram(0); + glMatrixMode(GL_MODELVIEW); +#endif + + // draw white lines, 1px wide + Color(1.0f, 1.0f, 1.0f).setFor(context); + l1.draw(context, lineWidth); + l2.draw(context, lineWidth); + l3.draw(context, lineWidth); + + // draw black lines, offset by 1px and 1px wide + Color(0.0f, 0.0f, 0.0f).setFor(context); + Line l1b(l1), l2b(l2), l3b(l3); + l1b.moveBy(lineWidth, lineWidth); + l2b.moveBy(lineWidth, lineWidth); + l3b.moveBy(lineWidth, lineWidth); + l1b.draw(context, lineWidth); + l2b.draw(context, lineWidth); + l3b.draw(context, lineWidth); + } + + bool onMouse(const MouseEvent& ev) override + { + if (ev.button != 1) + return false; + + if (ev.press && area.contains(ev.pos)) + { + resizing = true; + resizingSize = Size(getWidth(), getHeight()); + lastResizePoint = ev.pos; + return true; + } + + if (resizing && ! ev.press) + { + resizing = false; + return true; + } + + return false; + } + + bool onMotion(const MotionEvent& ev) override + { + if (! resizing) + return false; + + const Size offset(ev.pos.getX() - lastResizePoint.getX(), + ev.pos.getY() - lastResizePoint.getY()); + + resizingSize += offset; + lastResizePoint = ev.pos; + + // TODO min width, min height + const uint minWidth = 16; + const uint minHeight = 16; + + if (resizingSize.getWidth() < minWidth) + resizingSize.setWidth(minWidth); + if (resizingSize.getWidth() > 16384) + resizingSize.setWidth(16384); + if (resizingSize.getHeight() < minHeight) + resizingSize.setHeight(minHeight); + if (resizingSize.getHeight() > 16384) + resizingSize.setHeight(16384); + + setSize(resizingSize.getWidth(), resizingSize.getHeight()); + return true; + } + + void onResize(const ResizeEvent& ev) override + { + TopLevelWidget::onResize(ev); + resetArea(); + } + +private: + Rectangle area; + Line l1, l2, l3; + uint handleSize; + + // event handling state + bool resizing; + Point lastResizePoint; + Size resizingSize; + + void resetArea() + { + const double scaleFactor = getScaleFactor(); + const uint margin = 0.0 * scaleFactor; + const uint size = handleSize * scaleFactor; + + area = Rectangle(getWidth() - size - margin, + getHeight() - size - margin, + size, size); + + recreateLines(area.getX(), area.getY(), size); + } + + void recreateLines(const uint x, const uint y, const uint size) + { + uint linesize = size; + uint offset = 0; + + // 1st line, full diagonal size + l1.setStartPos(x + size, y); + l1.setEndPos(x, y + size); + + // 2nd line, bit more to the right and down, cropped + offset += size / 3; + linesize -= size / 3; + l2.setStartPos(x + linesize + offset, y + offset); + l2.setEndPos(x + offset, y + linesize + offset); + + // 3rd line, even more right and down + offset += size / 3; + linesize -= size / 3; + l3.setStartPos(x + linesize + offset, y + offset); + l3.setEndPos(x + offset, y + linesize + offset); + } + + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) +}; + +END_NAMESPACE_DGL diff --git a/plugins/CVCRack/Window.cpp b/plugins/CVCRack/Window.cpp index 7a00237..0ee9abc 100644 --- a/plugins/CVCRack/Window.cpp +++ b/plugins/CVCRack/Window.cpp @@ -68,6 +68,7 @@ std::shared_ptr Image::load(const std::string& filename) { struct Window::Internal { DISTRHO_NAMESPACE::UI* ui; + math::Vec size; std::string lastWindowTitle; @@ -94,11 +95,18 @@ struct Window::Internal { Window::Window() { internal = new Internal; internal->ui = lastUI; - - vg = lastUI->getContext(); + internal->size = minWindowSize; int err; + // Set up GLEW + glewExperimental = GL_TRUE; + err = glewInit(); + if (err != GLEW_OK) { + osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); + exit(1); + } + const GLubyte* vendor = glGetString(GL_VENDOR); const GLubyte* renderer = glGetString(GL_RENDERER); const GLubyte* version = glGetString(GL_VERSION); @@ -109,6 +117,21 @@ Window::Window() { // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here. glGetError(); + // Set up NanoVG + int nvgFlags = NVG_ANTIALIAS; +#if defined NANOVG_GL2 + vg = nvgCreateGL2(nvgFlags); + fbVg = nvgCreateSharedGL2(vg, nvgFlags); +#elif defined NANOVG_GL3 + vg = nvgCreateGL3(nvgFlags); +#elif defined NANOVG_GLES2 + vg = nvgCreateGLES2(nvgFlags); +#endif + if (!vg) { + osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize NanoVG. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed."); + exit(1); + } + // Load default Blendish font uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); bndSetFont(uiFont->handle); @@ -132,17 +155,26 @@ Window::~Window() { // nvgDeleteClone(fbVg); +#if defined NANOVG_GL2 + nvgDeleteGL2(vg); + nvgDeleteGL2(fbVg); +#elif defined NANOVG_GL3 + nvgDeleteGL3(vg); +#elif defined NANOVG_GLES2 + nvgDeleteGLES2(vg); +#endif + delete internal; } math::Vec Window::getSize() { - return math::Vec(1280, 720); + return internal->size; } void Window::setSize(math::Vec size) { - size = size.max(minWindowSize); + internal->size = size.max(minWindowSize); } @@ -163,14 +195,12 @@ void Window::step() { 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(); + // Context* context = contextGet(); // glfwPollEvents(); - contextSet(context); + // contextSet(context); // Set window title std::string windowTitle = APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION; @@ -223,9 +253,9 @@ void Window::step() { 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); + glViewport(0, 0, 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(); } @@ -370,6 +400,56 @@ bool& Window::fbDirtyOnSubpixelChange() { } +void mouseButtonCallback(Window* win, int button, int action, int mods) { + /* +#if defined ARCH_MAC + // Remap Ctrl-left click to right click on Mac + if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) { + button = GLFW_MOUSE_BUTTON_RIGHT; + mods &= ~GLFW_MOD_CONTROL; + } + // Remap Ctrl-shift-left click to middle click on Mac + if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) { + button = GLFW_MOUSE_BUTTON_MIDDLE; + mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT); + } +#endif +*/ + + APP->event->handleButton(win->internal->lastMousePos, button, action, mods); +} + +void cursorPosCallback(Window* win, double xpos, double ypos) { + math::Vec mousePos = math::Vec(xpos, ypos).div(win->pixelRatio / win->windowRatio).round(); + math::Vec mouseDelta = mousePos.minus(win->internal->lastMousePos); + + // Workaround for GLFW warping mouse to a different position when the cursor is locked or unlocked. + if (win->internal->ignoreNextMouseDelta) { + win->internal->ignoreNextMouseDelta = false; + mouseDelta = math::Vec(); + } + + win->internal->lastMousePos = mousePos; + + APP->event->handleHover(mousePos, mouseDelta); + + // Keyboard/mouse MIDI driver + math::Vec scaledPos(xpos / win->internal->ui->getWidth(), ypos / win->internal->ui->getHeight()); + keyboard::mouseMove(scaledPos); +} + +void scrollCallback(Window* win, double x, double y) { + math::Vec scrollDelta = math::Vec(x, y); +#if defined ARCH_MAC + scrollDelta = scrollDelta.mult(10.0); +#else + scrollDelta = scrollDelta.mult(50.0); +#endif + + APP->event->handleScroll(win->internal->lastMousePos, scrollDelta); +} + + void init() { } diff --git a/plugins/CVCRack/dep.cpp b/plugins/CVCRack/dep.cpp deleted file mode 100644 index 4d8b9dc..0000000 --- a/plugins/CVCRack/dep.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// 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