diff --git a/src/Cardinal/DistrhoPluginInfo.h b/src/Cardinal/DistrhoPluginInfo.h index 93dc94b..855aab0 100644 --- a/src/Cardinal/DistrhoPluginInfo.h +++ b/src/Cardinal/DistrhoPluginInfo.h @@ -27,7 +27,6 @@ #else #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 -#define DISTRHO_UI_USE_NANOVG 1 #define DISTRHO_UI_USER_RESIZABLE 1 #endif #define DISTRHO_PLUGIN_NUM_INPUTS 2 diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index 3dc9381..42da423 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -379,6 +379,7 @@ public: 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(); @@ -386,16 +387,6 @@ public: #ifdef HAVE_LIBLO fInitializer->oscPlugin = this; #endif - -#if defined(__MOD_DEVICES__) && !defined(HEADLESS) - context->window = new rack::window::Window; - rack::window::WindowInit(context->window, this); - /* - context->scene->removeChild(context->scene->menuBar); - context->scene->menuBar = rack::app::createMenuBar(getWindow(), getApp().isStandalone()); - context->scene->addChildBelow(context->scene->menuBar, context->scene->rackScroll); - */ -#endif } ~CardinalPlugin() override @@ -413,11 +404,6 @@ public: { const ScopedContext sc(this); - -#if defined(__MOD_DEVICES__) && !defined(HEADLESS) - delete context->window; - context->window = nullptr; -#endif delete context; } diff --git a/src/CardinalSynth/DistrhoPluginInfo.h b/src/CardinalSynth/DistrhoPluginInfo.h index 00b1ae8..2042014 100644 --- a/src/CardinalSynth/DistrhoPluginInfo.h +++ b/src/CardinalSynth/DistrhoPluginInfo.h @@ -27,7 +27,6 @@ #else #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 -#define DISTRHO_UI_USE_NANOVG 1 #define DISTRHO_UI_USER_RESIZABLE 1 #endif #define DISTRHO_PLUGIN_IS_SYNTH 1 diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index e5e0266..292e317 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -45,8 +45,8 @@ namespace app { widget::Widget* createMenuBar(Window& window, bool isStandalone); } namespace window { - void WindowInit(Window* window, DISTRHO_NAMESPACE::UI* ui); - void WindowMods(Window* window, int mods); + void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui); + void WindowSetMods(Window* window, int mods); } } @@ -80,7 +80,7 @@ class CardinalUI : public UI, : context(ui->fContext) { rack::contextSet(context); - rack::window::WindowMods(context->window, mods); + rack::window::WindowSetMods(context->window, mods); WindowParametersRestore(context->window); } @@ -106,38 +106,37 @@ public: if (scaleFactor != 1) setSize(1228 * scaleFactor, 666 * scaleFactor); - rack::window::Window* const window = new rack::window::Window; - rack::window::WindowInit(window, this); - rack::contextSet(fContext); - fContext->scene->removeChild(fContext->scene->menuBar); - fContext->scene->menuBar = rack::app::createMenuBar(getWindow(), getApp().isStandalone()); - fContext->scene->addChildBelow(fContext->scene->menuBar, fContext->scene->rackScroll); + rack::window::WindowSetPluginUI(fContext->window, this); - fContext->window = window; + if (fContext->scene->menuBar != nullptr) + fContext->scene->removeChild(fContext->scene->menuBar); - rack::widget::Widget::ContextCreateEvent e; - fContext->scene->onContextCreate(e); + fContext->scene->menuBar = rack::app::createMenuBar(getWindow(), getApp().isStandalone()); + fContext->scene->addChildBelow(fContext->scene->menuBar, fContext->scene->rackScroll); - window->step(); + fContext->window->step(); rack::contextSet(nullptr); - WindowParametersSetCallback(window, this); + WindowParametersSetCallback(fContext->window, this); } ~CardinalUI() override { rack::contextSet(fContext); - delete fContext->window; - fContext->window = nullptr; + rack::widget::Widget* const menuBar = fContext->scene->menuBar; + fContext->scene->menuBar = nullptr; + fContext->scene->removeChild(menuBar); + + rack::window::WindowSetPluginUI(fContext->window, nullptr); rack::contextSet(nullptr); } - void onNanoDisplay() override + void onDisplay() override { const ScopedContext sc(this); fContext->window->step(); @@ -481,8 +480,6 @@ protected: default: key = ev.key; break; } - rack::window::WindowMods(fContext->window, mods); - const ScopedContext sc(this, mods); return fContext->event->handleKey(fLastMousePos, key, ev.keycode, action, mods); } diff --git a/src/override/Window.cpp b/src/override/Window.cpp index a4db28b..7ffaeea 100644 --- a/src/override/Window.cpp +++ b/src/override/Window.cpp @@ -44,8 +44,14 @@ #endif #include "DistrhoUI.hpp" +#include "Application.hpp" #include "../WindowParameters.hpp" +#ifndef DGL_NO_SHARED_RESOURCES +# include "NanoVG.hpp" +# include "src/Resources.hpp" +#endif + namespace rack { namespace window { @@ -93,10 +99,27 @@ std::shared_ptr Image::load(const std::string& filename) { } +struct FontWithOriginalContext : Font { + int ohandle = -1; + std::string ofilename; +}; + +struct ImageWithOriginalContext : Image { + int ohandle = -1; + std::string ofilename; +}; + + struct Window::Internal { DISTRHO_NAMESPACE::UI* ui = nullptr; DISTRHO_NAMESPACE::WindowParameters params; DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr; + DGL_NAMESPACE::Application hiddenApp; + DGL_NAMESPACE::Window hiddenWindow; + NVGcontext* r_vg = nullptr; + NVGcontext* r_fbVg = nullptr; + NVGcontext* o_vg = nullptr; + NVGcontext* o_fbVg = nullptr; math::Vec size = minWindowSize; std::string lastWindowTitle; @@ -108,66 +131,185 @@ struct Window::Internal { double frameTime = 0.0; double lastFrameDuration = 0.0; - std::map> fontCache; - std::map> imageCache; + std::map> fontCache; + std::map> imageCache; bool fbDirtyOnSubpixelChange = true; + + Internal() + : hiddenApp(), + hiddenWindow(hiddenApp) { hiddenApp.idle(); } }; -Window::Window() { - internal = new Internal; -} -void WindowInit(Window* const window, DISTRHO_NAMESPACE::UI* const ui) +#ifndef DGL_NO_SHARED_RESOURCES +static int loadFallbackFont(NVGcontext* const vg) { - 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); + const int font = nvgFindFont(vg, NANOVG_DEJAVU_SANS_TTF); + if (font >= 0) + return font; - window->internal->ui = ui; - window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight()); + using namespace dpf_resources; - window->vg = ui->getContext(); + return nvgCreateFontMem(vg, NANOVG_DEJAVU_SANS_TTF, + (uchar*)dejavusans_ttf, dejavusans_ttf_size, 0); +} +#endif + + +Window::Window() { + internal = new Internal; + + DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow); + + // Set up NanoVG + const int nvgFlags = NVG_ANTIALIAS; #ifdef NANOVG_GLES2 - window->fbVg = nvgCreateSharedGLES2(window->vg, NVG_ANTIALIAS); + vg = nvgCreateGLES2(nvgFlags); + fbVg = nvgCreateSharedGLES2(vg, nvgFlags); #else - window->fbVg = nvgCreateSharedGL2(window->vg, NVG_ANTIALIAS); + vg = nvgCreateGL2(nvgFlags); + fbVg = nvgCreateSharedGL2(vg, nvgFlags); #endif // Load default Blendish font #ifndef DGL_NO_SHARED_RESOURCES - ui->loadSharedResources(); - window->uiFont = std::make_shared(); - window->uiFont->vg = window->vg; - window->uiFont->handle = nvgFindFont(window->vg, NANOVG_DEJAVU_SANS_TTF); - window->internal->fontCache["res/fonts/DejaVuSans.ttf"] = window->uiFont; + uiFont = std::make_shared(); + uiFont->vg = vg; + uiFont->handle = loadFallbackFont(vg); + + std::shared_ptr uiFont2; + uiFont2 = std::make_shared(); + uiFont2->vg = vg; + uiFont2->handle = loadFallbackFont(vg); + uiFont2->ofilename = asset::system("res/fonts/DejaVuSans.ttf"); + internal->fontCache[uiFont2->ofilename] = uiFont2; +#else + uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); +#endif + + if (uiFont != nullptr) + bndSetFont(uiFont->handle); +} + +void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) +{ + if (ui != nullptr) + { + 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); + + window->internal->ui = ui; + window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight()); + + // Set up NanoVG + const int nvgFlags = NVG_ANTIALIAS; +#ifdef NANOVG_GLES2 + window->internal->r_vg = nvgCreateSharedGLES2(nvgFlags); + window->internal->r_fbVg = nvgCreateSharedGLES2(window->internal->r_vg, nvgFlags); #else - window->loadFont(asset::system("res/fonts/DejaVuSans.ttf")); + window->internal->r_vg = nvgCreateGL2(nvgFlags); + window->internal->r_fbVg = nvgCreateSharedGL2(window->internal->r_vg, nvgFlags); #endif - if (window->uiFont != nullptr) - bndSetFont(window->uiFont->handle); + // swap contexts + window->internal->o_vg = window->vg; + window->internal->o_fbVg = window->fbVg; + window->vg = window->internal->r_vg; + window->fbVg = window->internal->r_fbVg; + + // also for fonts and images + window->uiFont->vg = window->vg; + window->uiFont->handle = loadFallbackFont(window->vg); + for (auto& font : window->internal->fontCache) + { + font.second->vg = window->vg; + font.second->ohandle = font.second->handle; + font.second->handle = nvgCreateFont(window->vg, + font.second->ofilename.c_str(), font.second->ofilename.c_str()); + } + for (auto& image : window->internal->imageCache) + { + image.second->vg = window->vg; + image.second->ohandle = image.second->handle; + image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(), + NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); + } + + // Init settings + WindowParametersRestore(window); + + widget::Widget::ContextCreateEvent e; + APP->scene->onContextCreate(e); + } + else + { + widget::Widget::ContextDestroyEvent e; + APP->scene->onContextDestroy(e); + + // swap contexts + window->uiFont->vg = window->internal->o_vg; + window->vg = window->internal->o_vg; + window->fbVg = window->internal->o_fbVg; + window->internal->o_vg = nullptr; + window->internal->o_fbVg = nullptr; + + // also for fonts and images + window->uiFont->vg = window->vg; + window->uiFont->handle = loadFallbackFont(window->vg); + for (auto& font : window->internal->fontCache) + { + font.second->vg = window->vg; + font.second->handle = font.second->ohandle; + font.second->ohandle = -1; + } + for (auto& image : window->internal->imageCache) + { + image.second->vg = window->vg; + image.second->handle = image.second->ohandle; + image.second->ohandle = -1; + } + + // also for images + +#if defined NANOVG_GLES2 + nvgDeleteGLES2(window->internal->r_vg); + nvgDeleteGLES2(window->internal->r_fbVg); +#else + nvgDeleteGL2(window->internal->r_vg); + nvgDeleteGL2(window->internal->r_fbVg); +#endif - // Init settings - WindowParametersRestore(window); + // window->internal->hiddenWindow.close(); + window->internal->ui = nullptr; + window->internal->callback = nullptr; + } } -void WindowMods(Window* const window, const int mods) +void WindowSetMods(Window* const window, const int mods) { window->internal->mods = mods; } Window::~Window() { - if (APP->scene) { - widget::Widget::ContextDestroyEvent e; - APP->scene->onContextDestroy(e); - } + { + DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow); - // Fonts and Images in the cache must be deleted before the NanoVG context is deleted - internal->fontCache.clear(); - internal->imageCache.clear(); + // Fonts and Images in the cache must be deleted before the NanoVG context is deleted + internal->fontCache.clear(); + internal->imageCache.clear(); + +#if defined NANOVG_GLES2 + nvgDeleteGLES2(vg); + nvgDeleteGLES2(fbVg); +#else + nvgDeleteGL2(vg); + nvgDeleteGL2(fbVg); +#endif + } delete internal; } @@ -189,6 +331,8 @@ void Window::run() { void Window::step() { + DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,); + double frameTime = system::getTime(); double lastFrameTime = internal->frameTime; internal->frameTime = frameTime; @@ -196,7 +340,7 @@ void Window::step() { // DEBUG("%.2lf Hz", 1.0 / internal->lastFrameDuration); // Make event handlers and step() have a clean NanoVG context -// nvgReset(vg); + nvgReset(vg); if (uiFont != nullptr) bndSetFont(uiFont->handle); @@ -238,6 +382,7 @@ void Window::step() { // Render scene // Update and render + nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); nvgScale(vg, pixelRatio, pixelRatio); // Draw scene @@ -249,6 +394,7 @@ void Window::step() { 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); } internal->frame++; @@ -264,6 +410,8 @@ void Window::screenshotModules(const std::string&, float) { void Window::close() { + DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,); + internal->ui->getWindow().close(); } @@ -322,14 +470,15 @@ std::shared_ptr Window::loadFont(const std::string& filename) { return pair->second; // Load font - std::shared_ptr font; + std::shared_ptr font; try { - font = std::make_shared(); + font = std::make_shared(); + font->ofilename = filename; font->loadFile(filename, vg); } catch (Exception& e) { WARN("%s", e.what()); - font = NULL; + font = nullptr; } internal->fontCache[filename] = font; return font; @@ -342,14 +491,15 @@ std::shared_ptr Window::loadImage(const std::string& filename) { return pair->second; // Load image - std::shared_ptr image; + std::shared_ptr image; try { - image = std::make_shared(); + image = std::make_shared(); + image->ofilename = filename; image->loadFile(filename, vg); } catch (Exception& e) { WARN("%s", e.what()); - image = NULL; + image = nullptr; } internal->imageCache[filename] = image; return image;