Signed-off-by: falkTX <falktx@falktx.com>tags/22.02
| @@ -0,0 +1,12 @@ | |||||
| #!/bin/bash | |||||
| set -e | |||||
| diff -U3 ../Rack/dep/oui-blendish/blendish.c blendish.c > diffs/blendish.c.diff | |||||
| diff -U3 ../Rack/src/common.cpp common.cpp > diffs/common.cpp.diff | |||||
| diff -U3 ../Rack/src/context.cpp context.cpp > diffs/context.cpp.diff | |||||
| diff -U3 ../Rack/src/app/MenuBar.cpp MenuBar.cpp > diffs/MenuBar.cpp.diff | |||||
| diff -U3 ../Rack/src/app/Scene.cpp Scene.cpp > diffs/Scene.cpp.diff | |||||
| diff -U3 ../Rack/src/engine/Engine.cpp Engine.cpp > diffs/Engine.cpp.diff | |||||
| diff -U3 ../Rack/src/plugin/Model.cpp Model.cpp > diffs/Model.cpp.diff | |||||
| diff -U3 ../Rack/src/window/Window.cpp Window.cpp > diffs/Window.cpp.diff | |||||
| @@ -0,0 +1,761 @@ | |||||
| --- ../Rack/src/engine/Engine.cpp 2022-01-15 14:44:46.395281005 +0000 | |||||
| +++ Engine.cpp 2022-01-23 17:13:03.200930905 +0000 | |||||
| @@ -1,3 +1,30 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's engine/Engine.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <algorithm> | |||||
| #include <set> | |||||
| #include <thread> | |||||
| @@ -11,178 +38,25 @@ | |||||
| #include <settings.hpp> | |||||
| #include <system.hpp> | |||||
| #include <random.hpp> | |||||
| -#include <context.hpp> | |||||
| #include <patch.hpp> | |||||
| #include <plugin.hpp> | |||||
| #include <mutex.hpp> | |||||
| +#include <helpers.hpp> | |||||
| + | |||||
| +#ifdef NDEBUG | |||||
| +# undef DEBUG | |||||
| +#endif | |||||
| +#include "DistrhoUtils.hpp" | |||||
| namespace rack { | |||||
| namespace engine { | |||||
| -static void initMXCSR() { | |||||
| - // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode | |||||
| - // https://software.intel.com/en-us/node/682949 | |||||
| - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); | |||||
| - _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); | |||||
| - // Reset other flags | |||||
| - _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); | |||||
| -} | |||||
| - | |||||
| - | |||||
| -/** Barrier based on mutexes. | |||||
| -Not finished or tested, do not use. | |||||
| -*/ | |||||
| -struct Barrier { | |||||
| - int count = 0; | |||||
| - uint8_t step = 0; | |||||
| - int threads = 0; | |||||
| - | |||||
| - std::mutex mutex; | |||||
| - std::condition_variable cv; | |||||
| - | |||||
| - void setThreads(int threads) { | |||||
| - this->threads = threads; | |||||
| - } | |||||
| - | |||||
| - void wait() { | |||||
| - std::unique_lock<std::mutex> lock(mutex); | |||||
| - uint8_t s = step; | |||||
| - if (++count >= threads) { | |||||
| - // We're the last thread. Reset next phase. | |||||
| - count = 0; | |||||
| - // Allow other threads to exit wait() | |||||
| - step++; | |||||
| - cv.notify_all(); | |||||
| - return; | |||||
| - } | |||||
| - | |||||
| - cv.wait(lock, [&] { | |||||
| - return step != s; | |||||
| - }); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -/** 2-phase barrier based on spin-locking. | |||||
| -*/ | |||||
| -struct SpinBarrier { | |||||
| - std::atomic<int> count{0}; | |||||
| - std::atomic<uint8_t> step{0}; | |||||
| - int threads = 0; | |||||
| - | |||||
| - /** Must be called when no threads are calling wait(). | |||||
| - */ | |||||
| - void setThreads(int threads) { | |||||
| - this->threads = threads; | |||||
| - } | |||||
| - | |||||
| - void wait() { | |||||
| - uint8_t s = step; | |||||
| - if (count.fetch_add(1, std::memory_order_acquire) + 1 >= threads) { | |||||
| - // We're the last thread. Reset next phase. | |||||
| - count = 0; | |||||
| - // Allow other threads to exit wait() | |||||
| - step++; | |||||
| - return; | |||||
| - } | |||||
| - | |||||
| - // Spin until the last thread begins waiting | |||||
| - while (true) { | |||||
| - if (step.load(std::memory_order_relaxed) != s) | |||||
| - return; | |||||
| - __builtin_ia32_pause(); | |||||
| - } | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -/** Barrier that spin-locks until yield() is called, and then all threads switch to a mutex. | |||||
| -yield() should be called if it is likely that all threads will block for a while and continuing to spin-lock is unnecessary. | |||||
| -Saves CPU power after yield is called. | |||||
| -*/ | |||||
| -struct HybridBarrier { | |||||
| - std::atomic<int> count{0}; | |||||
| - std::atomic<uint8_t> step{0}; | |||||
| - int threads = 0; | |||||
| - | |||||
| - std::atomic<bool> yielded{false}; | |||||
| - std::mutex mutex; | |||||
| - std::condition_variable cv; | |||||
| - | |||||
| - void setThreads(int threads) { | |||||
| - this->threads = threads; | |||||
| - } | |||||
| - | |||||
| - void yield() { | |||||
| - yielded = true; | |||||
| - } | |||||
| - | |||||
| - void wait() { | |||||
| - uint8_t s = step; | |||||
| - if (count.fetch_add(1, std::memory_order_acquire) + 1 >= threads) { | |||||
| - // We're the last thread. Reset next phase. | |||||
| - count = 0; | |||||
| - bool wasYielded = yielded; | |||||
| - yielded = false; | |||||
| - // Allow other threads to exit wait() | |||||
| - step++; | |||||
| - if (wasYielded) { | |||||
| - std::unique_lock<std::mutex> lock(mutex); | |||||
| - cv.notify_all(); | |||||
| - } | |||||
| - return; | |||||
| - } | |||||
| - | |||||
| - // Spin until the last thread begins waiting | |||||
| - while (!yielded.load(std::memory_order_relaxed)) { | |||||
| - if (step.load(std::memory_order_relaxed) != s) | |||||
| - return; | |||||
| - __builtin_ia32_pause(); | |||||
| - } | |||||
| - | |||||
| - // Wait on mutex CV | |||||
| - std::unique_lock<std::mutex> lock(mutex); | |||||
| - cv.wait(lock, [&] { | |||||
| - return step != s; | |||||
| - }); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct EngineWorker { | |||||
| - Engine* engine; | |||||
| - int id; | |||||
| - std::thread thread; | |||||
| - bool running = false; | |||||
| - | |||||
| - void start() { | |||||
| - assert(!running); | |||||
| - running = true; | |||||
| - thread = std::thread([&] { | |||||
| - run(); | |||||
| - }); | |||||
| - } | |||||
| - | |||||
| - void requestStop() { | |||||
| - running = false; | |||||
| - } | |||||
| - | |||||
| - void join() { | |||||
| - assert(thread.joinable()); | |||||
| - thread.join(); | |||||
| - } | |||||
| - | |||||
| - void run(); | |||||
| -}; | |||||
| - | |||||
| - | |||||
| struct Engine::Internal { | |||||
| std::vector<Module*> modules; | |||||
| std::vector<Cable*> cables; | |||||
| std::set<ParamHandle*> paramHandles; | |||||
| - Module* masterModule = NULL; | |||||
| // moduleId | |||||
| std::map<int64_t, Module*> modulesCache; | |||||
| @@ -217,22 +91,6 @@ | |||||
| Readers lock when using the engine's state. | |||||
| */ | |||||
| SharedMutex mutex; | |||||
| - /** Mutex that guards stepBlock() so it's not called simultaneously. | |||||
| - */ | |||||
| - std::mutex blockMutex; | |||||
| - | |||||
| - int threadCount = 0; | |||||
| - std::vector<EngineWorker> workers; | |||||
| - HybridBarrier engineBarrier; | |||||
| - HybridBarrier workerBarrier; | |||||
| - std::atomic<int> workerModuleIndex; | |||||
| - // For worker threads | |||||
| - Context* context; | |||||
| - | |||||
| - bool fallbackRunning = false; | |||||
| - std::thread fallbackThread; | |||||
| - std::mutex fallbackMutex; | |||||
| - std::condition_variable fallbackCv; | |||||
| }; | |||||
| @@ -260,71 +118,6 @@ | |||||
| } | |||||
| -static void Engine_relaunchWorkers(Engine* that, int threadCount) { | |||||
| - Engine::Internal* internal = that->internal; | |||||
| - if (threadCount == internal->threadCount) | |||||
| - return; | |||||
| - | |||||
| - if (internal->threadCount > 0) { | |||||
| - // Stop engine workers | |||||
| - for (EngineWorker& worker : internal->workers) { | |||||
| - worker.requestStop(); | |||||
| - } | |||||
| - internal->engineBarrier.wait(); | |||||
| - | |||||
| - // Join and destroy engine workers | |||||
| - for (EngineWorker& worker : internal->workers) { | |||||
| - worker.join(); | |||||
| - } | |||||
| - internal->workers.resize(0); | |||||
| - } | |||||
| - | |||||
| - // Configure engine | |||||
| - internal->threadCount = threadCount; | |||||
| - | |||||
| - // Set barrier counts | |||||
| - internal->engineBarrier.setThreads(threadCount); | |||||
| - internal->workerBarrier.setThreads(threadCount); | |||||
| - | |||||
| - if (threadCount > 0) { | |||||
| - // Create and start engine workers | |||||
| - internal->workers.resize(threadCount - 1); | |||||
| - for (int id = 1; id < threadCount; id++) { | |||||
| - EngineWorker& worker = internal->workers[id - 1]; | |||||
| - worker.id = id; | |||||
| - worker.engine = that; | |||||
| - worker.start(); | |||||
| - } | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -static void Engine_stepWorker(Engine* that, int threadId) { | |||||
| - Engine::Internal* internal = that->internal; | |||||
| - | |||||
| - // int threadCount = internal->threadCount; | |||||
| - int modulesLen = internal->modules.size(); | |||||
| - | |||||
| - // Build ProcessArgs | |||||
| - Module::ProcessArgs processArgs; | |||||
| - processArgs.sampleRate = internal->sampleRate; | |||||
| - processArgs.sampleTime = internal->sampleTime; | |||||
| - processArgs.frame = internal->frame; | |||||
| - | |||||
| - // Step each module | |||||
| - while (true) { | |||||
| - // Choose next module | |||||
| - // First-come-first serve module-to-thread allocation algorithm | |||||
| - int i = internal->workerModuleIndex++; | |||||
| - if (i >= modulesLen) | |||||
| - break; | |||||
| - | |||||
| - Module* module = internal->modules[i]; | |||||
| - module->doProcess(processArgs); | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| static void Cable_step(Cable* that) { | |||||
| Output* output = &that->outputModule->outputs[that->outputId]; | |||||
| Input* input = &that->inputModule->inputs[that->inputId]; | |||||
| @@ -373,12 +166,12 @@ | |||||
| } | |||||
| // Step cables | |||||
| - for (Cable* cable : that->internal->cables) { | |||||
| + for (Cable* cable : internal->cables) { | |||||
| Cable_step(cable); | |||||
| } | |||||
| // Flip messages for each module | |||||
| - for (Module* module : that->internal->modules) { | |||||
| + for (Module* module : internal->modules) { | |||||
| if (module->leftExpander.messageFlipRequested) { | |||||
| std::swap(module->leftExpander.producerMessage, module->leftExpander.consumerMessage); | |||||
| module->leftExpander.messageFlipRequested = false; | |||||
| @@ -389,13 +182,18 @@ | |||||
| } | |||||
| } | |||||
| - // Step modules along with workers | |||||
| - internal->workerModuleIndex = 0; | |||||
| - internal->engineBarrier.wait(); | |||||
| - Engine_stepWorker(that, 0); | |||||
| - internal->workerBarrier.wait(); | |||||
| + // Build ProcessArgs | |||||
| + Module::ProcessArgs processArgs; | |||||
| + processArgs.sampleRate = internal->sampleRate; | |||||
| + processArgs.sampleTime = internal->sampleTime; | |||||
| + processArgs.frame = internal->frame; | |||||
| + | |||||
| + // Step each module | |||||
| + for (Module* module : internal->modules) { | |||||
| + module->doProcess(processArgs); | |||||
| + } | |||||
| - internal->frame++; | |||||
| + ++internal->frame; | |||||
| } | |||||
| @@ -460,37 +258,22 @@ | |||||
| Engine::Engine() { | |||||
| internal = new Internal; | |||||
| - | |||||
| - internal->context = contextGet(); | |||||
| - setSuggestedSampleRate(0.f); | |||||
| } | |||||
| Engine::~Engine() { | |||||
| - // Stop fallback thread if running | |||||
| - { | |||||
| - std::lock_guard<std::mutex> lock(internal->fallbackMutex); | |||||
| - internal->fallbackRunning = false; | |||||
| - internal->fallbackCv.notify_all(); | |||||
| - } | |||||
| - if (internal->fallbackThread.joinable()) | |||||
| - internal->fallbackThread.join(); | |||||
| - | |||||
| - // Shut down workers | |||||
| - Engine_relaunchWorkers(this, 0); | |||||
| - | |||||
| // Clear modules, cables, etc | |||||
| clear(); | |||||
| // Make sure there are no cables or modules in the rack on destruction. | |||||
| // If this happens, a module must have failed to remove itself before the RackWidget was destroyed. | |||||
| - assert(internal->cables.empty()); | |||||
| - assert(internal->modules.empty()); | |||||
| - assert(internal->paramHandles.empty()); | |||||
| - | |||||
| - assert(internal->modulesCache.empty()); | |||||
| - assert(internal->cablesCache.empty()); | |||||
| - assert(internal->paramHandlesCache.empty()); | |||||
| + DISTRHO_SAFE_ASSERT(internal->cables.empty()); | |||||
| + DISTRHO_SAFE_ASSERT(internal->modules.empty()); | |||||
| + DISTRHO_SAFE_ASSERT(internal->paramHandles.empty()); | |||||
| + | |||||
| + DISTRHO_SAFE_ASSERT(internal->modulesCache.empty()); | |||||
| + DISTRHO_SAFE_ASSERT(internal->cablesCache.empty()); | |||||
| + DISTRHO_SAFE_ASSERT(internal->paramHandlesCache.empty()); | |||||
| delete internal; | |||||
| } | |||||
| @@ -526,11 +309,8 @@ | |||||
| // Start timer before locking | |||||
| double startTime = system::getTime(); | |||||
| - std::lock_guard<std::mutex> stepLock(internal->blockMutex); | |||||
| SharedLock<SharedMutex> lock(internal->mutex); | |||||
| // Configure thread | |||||
| - uint32_t csr = _mm_getcsr(); | |||||
| - initMXCSR(); | |||||
| random::init(); | |||||
| internal->blockFrame = internal->frame; | |||||
| @@ -543,16 +323,11 @@ | |||||
| Engine_updateExpander_NoLock(this, module, true); | |||||
| } | |||||
| - // Launch workers | |||||
| - Engine_relaunchWorkers(this, settings::threadCount); | |||||
| - | |||||
| // Step individual frames | |||||
| for (int i = 0; i < frames; i++) { | |||||
| Engine_stepFrame(this); | |||||
| } | |||||
| - yieldWorkers(); | |||||
| - | |||||
| internal->block++; | |||||
| // Stop timer | |||||
| @@ -572,47 +347,19 @@ | |||||
| internal->meterTotal = 0.0; | |||||
| internal->meterMax = 0.0; | |||||
| } | |||||
| - | |||||
| - // Reset MXCSR back to original value | |||||
| - _mm_setcsr(csr); | |||||
| } | |||||
| void Engine::setMasterModule(Module* module) { | |||||
| - if (module == internal->masterModule) | |||||
| - return; | |||||
| - std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| - setMasterModule_NoLock(module); | |||||
| } | |||||
| void Engine::setMasterModule_NoLock(Module* module) { | |||||
| - if (module == internal->masterModule) | |||||
| - return; | |||||
| - | |||||
| - if (internal->masterModule) { | |||||
| - // Dispatch UnsetMasterEvent | |||||
| - Module::UnsetMasterEvent e; | |||||
| - internal->masterModule->onUnsetMaster(e); | |||||
| - } | |||||
| - | |||||
| - internal->masterModule = module; | |||||
| - | |||||
| - if (internal->masterModule) { | |||||
| - // Dispatch SetMasterEvent | |||||
| - Module::SetMasterEvent e; | |||||
| - internal->masterModule->onSetMaster(e); | |||||
| - } | |||||
| - | |||||
| - // Wake up fallback thread if master module was unset | |||||
| - if (!internal->masterModule) { | |||||
| - internal->fallbackCv.notify_all(); | |||||
| - } | |||||
| } | |||||
| Module* Engine::getMasterModule() { | |||||
| - return internal->masterModule; | |||||
| + return nullptr; | |||||
| } | |||||
| @@ -639,16 +386,6 @@ | |||||
| void Engine::setSuggestedSampleRate(float suggestedSampleRate) { | |||||
| - if (settings::sampleRate > 0) { | |||||
| - setSampleRate(settings::sampleRate); | |||||
| - } | |||||
| - else if (suggestedSampleRate > 0) { | |||||
| - setSampleRate(suggestedSampleRate); | |||||
| - } | |||||
| - else { | |||||
| - // Fallback sample rate | |||||
| - setSampleRate(44100.f); | |||||
| - } | |||||
| } | |||||
| @@ -658,7 +395,6 @@ | |||||
| void Engine::yieldWorkers() { | |||||
| - internal->workerBarrier.yield(); | |||||
| } | |||||
| @@ -738,10 +474,10 @@ | |||||
| void Engine::addModule(Module* module) { | |||||
| std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| - assert(module); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
| // Check that the module is not already added | |||||
| auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
| - assert(it == internal->modules.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it == internal->modules.end(),); | |||||
| // Set ID if unset or collides with an existing ID | |||||
| while (module->id < 0 || internal->modulesCache.find(module->id) != internal->modulesCache.end()) { | |||||
| // Randomly generate ID | |||||
| @@ -773,10 +509,14 @@ | |||||
| void Engine::removeModule_NoLock(Module* module) { | |||||
| - assert(module); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
| // Check that the module actually exists | |||||
| auto it = std::find(internal->modules.begin(), internal->modules.end(), module); | |||||
| - assert(it != internal->modules.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it != internal->modules.end(),); | |||||
| + // Remove from widgets cache | |||||
| + CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(module->model); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(helper != nullptr,); | |||||
| + helper->removeCachedModuleWidget(module); | |||||
| // Dispatch RemoveEvent | |||||
| Module::RemoveEvent eRemove; | |||||
| module->onRemove(eRemove); | |||||
| @@ -785,18 +525,14 @@ | |||||
| if (paramHandle->moduleId == module->id) | |||||
| paramHandle->module = NULL; | |||||
| } | |||||
| - // Unset master module | |||||
| - if (getMasterModule() == module) { | |||||
| - setMasterModule_NoLock(NULL); | |||||
| - } | |||||
| // If a param is being smoothed on this module, stop smoothing it immediately | |||||
| if (module == internal->smoothModule) { | |||||
| internal->smoothModule = NULL; | |||||
| } | |||||
| // Check that all cables are disconnected | |||||
| for (Cable* cable : internal->cables) { | |||||
| - assert(cable->inputModule != module); | |||||
| - assert(cable->outputModule != module); | |||||
| + DISTRHO_SAFE_ASSERT(cable->inputModule != module); | |||||
| + DISTRHO_SAFE_ASSERT(cable->outputModule != module); | |||||
| } | |||||
| // Update expanders of other modules | |||||
| for (Module* m : internal->modules) { | |||||
| @@ -844,7 +580,7 @@ | |||||
| void Engine::resetModule(Module* module) { | |||||
| std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| - assert(module); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
| Module::ResetEvent eReset; | |||||
| module->onReset(eReset); | |||||
| @@ -853,7 +589,7 @@ | |||||
| void Engine::randomizeModule(Module* module) { | |||||
| std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| - assert(module); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
| Module::RandomizeEvent eRandomize; | |||||
| module->onRandomize(eRandomize); | |||||
| @@ -861,7 +597,7 @@ | |||||
| void Engine::bypassModule(Module* module, bool bypassed) { | |||||
| - assert(module); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(module,); | |||||
| if (module->isBypassed() == bypassed) | |||||
| return; | |||||
| @@ -946,16 +682,16 @@ | |||||
| void Engine::addCable(Cable* cable) { | |||||
| std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| - assert(cable); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(cable,); | |||||
| // Check cable properties | |||||
| - assert(cable->inputModule); | |||||
| - assert(cable->outputModule); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(cable->inputModule,); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(cable->outputModule,); | |||||
| bool outputWasConnected = false; | |||||
| for (Cable* cable2 : internal->cables) { | |||||
| // Check that the cable is not already added | |||||
| - assert(cable2 != cable); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(cable2 != cable,); | |||||
| // Check that the input is not already used by another cable | |||||
| - assert(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId)); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(!(cable2->inputModule == cable->inputModule && cable2->inputId == cable->inputId),); | |||||
| // Get connected status of output, to decide whether we need to call a PortChangeEvent. | |||||
| // It's best to not trust `cable->outputModule->outputs[cable->outputId]->isConnected()` | |||||
| if (cable2->outputModule == cable->outputModule && cable2->outputId == cable->outputId) | |||||
| @@ -996,10 +732,10 @@ | |||||
| void Engine::removeCable_NoLock(Cable* cable) { | |||||
| - assert(cable); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(cable,); | |||||
| // Check that the cable is already added | |||||
| auto it = std::find(internal->cables.begin(), internal->cables.end(), cable); | |||||
| - assert(it != internal->cables.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it != internal->cables.end(),); | |||||
| // Remove the cable | |||||
| internal->cablesCache.erase(cable->id); | |||||
| internal->cables.erase(it); | |||||
| @@ -1085,11 +821,11 @@ | |||||
| std::lock_guard<SharedMutex> lock(internal->mutex); | |||||
| // New ParamHandles must be blank. | |||||
| // This means we don't have to refresh the cache. | |||||
| - assert(paramHandle->moduleId < 0); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(paramHandle->moduleId < 0,); | |||||
| // Check that the ParamHandle is not already added | |||||
| auto it = internal->paramHandles.find(paramHandle); | |||||
| - assert(it == internal->paramHandles.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it == internal->paramHandles.end(),); | |||||
| // Add it | |||||
| internal->paramHandles.insert(paramHandle); | |||||
| @@ -1106,7 +842,7 @@ | |||||
| void Engine::removeParamHandle_NoLock(ParamHandle* paramHandle) { | |||||
| // Check that the ParamHandle is already added | |||||
| auto it = internal->paramHandles.find(paramHandle); | |||||
| - assert(it != internal->paramHandles.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it != internal->paramHandles.end(),); | |||||
| // Remove it | |||||
| paramHandle->module = NULL; | |||||
| @@ -1143,7 +879,7 @@ | |||||
| void Engine::updateParamHandle_NoLock(ParamHandle* paramHandle, int64_t moduleId, int paramId, bool overwrite) { | |||||
| // Check that it exists | |||||
| auto it = internal->paramHandles.find(paramHandle); | |||||
| - assert(it != internal->paramHandles.end()); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(it != internal->paramHandles.end(),); | |||||
| // Set IDs | |||||
| paramHandle->moduleId = moduleId; | |||||
| @@ -1197,11 +933,6 @@ | |||||
| } | |||||
| json_object_set_new(rootJ, "cables", cablesJ); | |||||
| - // masterModule | |||||
| - if (internal->masterModule) { | |||||
| - json_object_set_new(rootJ, "masterModuleId", json_integer(internal->masterModule->id)); | |||||
| - } | |||||
| - | |||||
| return rootJ; | |||||
| } | |||||
| @@ -1225,14 +956,20 @@ | |||||
| } | |||||
| catch (Exception& e) { | |||||
| WARN("Cannot load model: %s", e.what()); | |||||
| - APP->patch->log(e.what()); | |||||
| + // APP->patch->log(e.what()); | |||||
| continue; | |||||
| } | |||||
| // Create module | |||||
| - INFO("Creating module %s", model->getFullName().c_str()); | |||||
| - Module* module = model->createModule(); | |||||
| - assert(module); | |||||
| + Module* const module = model->createModule(); | |||||
| + DISTRHO_SAFE_ASSERT_CONTINUE(module != nullptr); | |||||
| + | |||||
| + // Create the widget too, needed by a few modules | |||||
| + CardinalPluginModelHelper* const helper = dynamic_cast<CardinalPluginModelHelper*>(model); | |||||
| + DISTRHO_SAFE_ASSERT_CONTINUE(helper != nullptr); | |||||
| + | |||||
| + app::ModuleWidget* const moduleWidget = helper->createModuleWidgetFromEngineLoad(module); | |||||
| + DISTRHO_SAFE_ASSERT_CONTINUE(moduleWidget != nullptr); | |||||
| try { | |||||
| // This doesn't need a lock because the Module is not added to the Engine yet. | |||||
| @@ -1248,7 +985,8 @@ | |||||
| } | |||||
| catch (Exception& e) { | |||||
| WARN("Cannot load module: %s", e.what()); | |||||
| - APP->patch->log(e.what()); | |||||
| + // APP->patch->log(e.what()); | |||||
| + helper->removeCachedModuleWidget(module); | |||||
| delete module; | |||||
| continue; | |||||
| } | |||||
| @@ -1285,67 +1023,10 @@ | |||||
| continue; | |||||
| } | |||||
| } | |||||
| - | |||||
| - // masterModule | |||||
| - json_t* masterModuleIdJ = json_object_get(rootJ, "masterModuleId"); | |||||
| - if (masterModuleIdJ) { | |||||
| - Module* masterModule = getModule(json_integer_value(masterModuleIdJ)); | |||||
| - setMasterModule(masterModule); | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -void EngineWorker::run() { | |||||
| - // Configure thread | |||||
| - contextSet(engine->internal->context); | |||||
| - system::setThreadName(string::f("Worker %d", id)); | |||||
| - initMXCSR(); | |||||
| - random::init(); | |||||
| - | |||||
| - while (true) { | |||||
| - engine->internal->engineBarrier.wait(); | |||||
| - if (!running) | |||||
| - return; | |||||
| - Engine_stepWorker(engine, id); | |||||
| - engine->internal->workerBarrier.wait(); | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -static void Engine_fallbackRun(Engine* that) { | |||||
| - system::setThreadName("Engine fallback"); | |||||
| - contextSet(that->internal->context); | |||||
| - | |||||
| - while (that->internal->fallbackRunning) { | |||||
| - if (!that->getMasterModule()) { | |||||
| - // Step blocks and wait | |||||
| - double start = system::getTime(); | |||||
| - int frames = std::floor(that->getSampleRate() / 60); | |||||
| - that->stepBlock(frames); | |||||
| - double end = system::getTime(); | |||||
| - | |||||
| - double duration = frames * that->getSampleTime() - (end - start); | |||||
| - if (duration > 0.0) { | |||||
| - std::this_thread::sleep_for(std::chrono::duration<double>(duration)); | |||||
| - } | |||||
| - } | |||||
| - else { | |||||
| - // Wait for master module to be unset, or for the request to stop running | |||||
| - std::unique_lock<std::mutex> lock(that->internal->fallbackMutex); | |||||
| - that->internal->fallbackCv.wait(lock, [&]() { | |||||
| - return !that->internal->fallbackRunning || !that->getMasterModule(); | |||||
| - }); | |||||
| - } | |||||
| - } | |||||
| } | |||||
| void Engine::startFallbackThread() { | |||||
| - if (internal->fallbackThread.joinable()) | |||||
| - return; | |||||
| - | |||||
| - internal->fallbackRunning = true; | |||||
| - internal->fallbackThread = std::thread(Engine_fallbackRun, this); | |||||
| } | |||||
| @@ -0,0 +1,704 @@ | |||||
| --- ../Rack/src/app/MenuBar.cpp 2022-01-15 14:44:46.391280963 +0000 | |||||
| +++ MenuBar.cpp 2022-01-23 17:13:16.500279828 +0000 | |||||
| @@ -1,8 +1,33 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's app/MenuBar.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <thread> | |||||
| #include <utility> | |||||
| -#include <osdialog.h> | |||||
| - | |||||
| #include <app/MenuBar.hpp> | |||||
| #include <app/TipWindow.hpp> | |||||
| #include <widget/OpaqueWidget.hpp> | |||||
| @@ -25,6 +50,11 @@ | |||||
| #include <patch.hpp> | |||||
| #include <library.hpp> | |||||
| +#ifdef HAVE_LIBLO | |||||
| +# include <lo/lo.h> | |||||
| +#endif | |||||
| + | |||||
| +#include "../CardinalCommon.hpp" | |||||
| namespace rack { | |||||
| namespace app { | |||||
| @@ -48,80 +78,108 @@ | |||||
| }; | |||||
| -struct NotificationIcon : widget::Widget { | |||||
| - void draw(const DrawArgs& args) override { | |||||
| - nvgBeginPath(args.vg); | |||||
| - float radius = 4; | |||||
| - nvgCircle(args.vg, radius, radius, radius); | |||||
| - nvgFillColor(args.vg, nvgRGBf(1.0, 0.0, 0.0)); | |||||
| - nvgFill(args.vg); | |||||
| - nvgStrokeColor(args.vg, nvgRGBf(0.5, 0.0, 0.0)); | |||||
| - nvgStroke(args.vg); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| //////////////////// | |||||
| // File | |||||
| //////////////////// | |||||
| struct FileButton : MenuButton { | |||||
| + const bool isStandalone; | |||||
| + | |||||
| +#ifdef HAVE_LIBLO | |||||
| + bool oscConnected = false; | |||||
| + lo_server oscServer = nullptr; | |||||
| + | |||||
| + static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self) | |||||
| + { | |||||
| + d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc); | |||||
| + | |||||
| + if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') { | |||||
| + d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s); | |||||
| + if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0) | |||||
| + static_cast<FileButton*>(self)->oscConnected = true; | |||||
| + } | |||||
| + return 0; | |||||
| + } | |||||
| + | |||||
| + ~FileButton() { | |||||
| + lo_server_free(oscServer); | |||||
| + } | |||||
| +#endif | |||||
| + | |||||
| + FileButton(const bool standalone) | |||||
| + : MenuButton(), isStandalone(standalone) {} | |||||
| + | |||||
| void onAction(const ActionEvent& e) override { | |||||
| ui::Menu* menu = createMenu(); | |||||
| menu->cornerFlags = BND_CORNER_TOP; | |||||
| menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | |||||
| menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() { | |||||
| - APP->patch->loadTemplateDialog(); | |||||
| + patchUtils::loadTemplateDialog(); | |||||
| })); | |||||
| - menu->addChild(createMenuItem("Open", RACK_MOD_CTRL_NAME "+O", []() { | |||||
| - APP->patch->loadDialog(); | |||||
| + menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() { | |||||
| + patchUtils::loadDialog(); | |||||
| })); | |||||
| - menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) { | |||||
| - for (const std::string& path : settings::recentPatchPaths) { | |||||
| - std::string name = system::getStem(path); | |||||
| - menu->addChild(createMenuItem(name, "", [=]() { | |||||
| - APP->patch->loadPathDialog(path); | |||||
| - })); | |||||
| - } | |||||
| - }, settings::recentPatchPaths.empty())); | |||||
| - | |||||
| menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { | |||||
| - APP->patch->saveDialog(); | |||||
| - })); | |||||
| - | |||||
| - menu->addChild(createMenuItem("Save as", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||||
| - APP->patch->saveAsDialog(); | |||||
| + // NOTE: will do nothing if path is empty, intentionally | |||||
| + patchUtils::saveDialog(APP->patch->path); | |||||
| + }, APP->patch->path.empty())); | |||||
| + | |||||
| + menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||||
| + patchUtils::saveAsDialog(); | |||||
| })); | |||||
| - menu->addChild(createMenuItem("Save a copy", "", []() { | |||||
| - APP->patch->saveAsDialog(false); | |||||
| - })); | |||||
| +#ifdef HAVE_LIBLO | |||||
| + if (oscServer == nullptr || !oscConnected) { | |||||
| + menu->addChild(createMenuItem("Connect to MOD", "", [this]() { | |||||
| + if (oscServer == nullptr) { | |||||
| + oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,); | |||||
| + lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, this); | |||||
| + } | |||||
| + const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | |||||
| + lo_send(addr, "/hello", ""); | |||||
| + lo_address_free(addr); | |||||
| + })); | |||||
| + } else { | |||||
| + menu->addChild(createMenuItem("Deploy to MOD", "F7", []() { | |||||
| + patchUtils::deployToMOD(); | |||||
| + })); | |||||
| + } | |||||
| +#endif | |||||
| menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { | |||||
| - APP->patch->revertDialog(); | |||||
| - }, APP->patch->path == "")); | |||||
| - | |||||
| - menu->addChild(createMenuItem("Overwrite template", "", []() { | |||||
| - APP->patch->saveTemplateDialog(); | |||||
| - })); | |||||
| + patchUtils::revertDialog(); | |||||
| + }, APP->patch->path.empty())); | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| // Load selection | |||||
| menu->addChild(createMenuItem("Import selection", "", [=]() { | |||||
| - APP->scene->rack->loadSelectionDialog(); | |||||
| + patchUtils::loadSelectionDialog(); | |||||
| }, false, true)); | |||||
| - menu->addChild(new ui::MenuSeparator); | |||||
| + if (isStandalone) { | |||||
| + menu->addChild(new ui::MenuSeparator); | |||||
| - menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() { | |||||
| - APP->window->close(); | |||||
| - })); | |||||
| + menu->addChild(createMenuItem("Quit", RACK_MOD_CTRL_NAME "+Q", []() { | |||||
| + APP->window->close(); | |||||
| + })); | |||||
| + }; | |||||
| } | |||||
| + | |||||
| +#ifdef HAVE_LIBLO | |||||
| + void step() override { | |||||
| + MenuButton::step(); | |||||
| + if (oscServer != nullptr) { | |||||
| + while (lo_server_recv_noblock(oscServer, 0) != 0) {} | |||||
| + } | |||||
| + } | |||||
| +#endif | |||||
| }; | |||||
| @@ -166,7 +224,7 @@ | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| - APP->scene->rack->appendSelectionContextMenu(menu); | |||||
| + patchUtils::appendSelectionContextMenu(menu); | |||||
| } | |||||
| }; | |||||
| @@ -256,7 +314,7 @@ | |||||
| return settings::cableTension; | |||||
| } | |||||
| float getDefaultValue() override { | |||||
| - return 0.5; | |||||
| + return 0.75; | |||||
| } | |||||
| float getDisplayValue() override { | |||||
| return getValue() * 100; | |||||
| @@ -421,28 +479,9 @@ | |||||
| haloBrightnessSlider->box.size.x = 250.0; | |||||
| menu->addChild(haloBrightnessSlider); | |||||
| - double frameRate = APP->window->getMonitorRefreshRate() / settings::frameSwapInterval; | |||||
| - menu->addChild(createSubmenuItem("Frame rate", string::f("%.0f Hz", frameRate), [=](ui::Menu* menu) { | |||||
| - for (int i = 1; i <= 6; i++) { | |||||
| - double frameRate = APP->window->getMonitorRefreshRate() / i; | |||||
| - menu->addChild(createCheckMenuItem(string::f("%.0f Hz", frameRate), "", | |||||
| - [=]() {return settings::frameSwapInterval == i;}, | |||||
| - [=]() {settings::frameSwapInterval = i;} | |||||
| - )); | |||||
| - } | |||||
| - })); | |||||
| - | |||||
| - bool fullscreen = APP->window->isFullScreen(); | |||||
| - std::string fullscreenText = "F11"; | |||||
| - if (fullscreen) | |||||
| - fullscreenText += " " CHECKMARK_STRING; | |||||
| - menu->addChild(createMenuItem("Fullscreen", fullscreenText, [=]() { | |||||
| - APP->window->setFullScreen(!fullscreen); | |||||
| - })); | |||||
| - | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| - menu->addChild(createBoolPtrMenuItem("Lock cursor while dragging params", "", &settings::allowCursorLock)); | |||||
| + // menu->addChild(createBoolPtrMenuItem("Hide cursor while dragging", "", &settings::allowCursorLock)); | |||||
| static const std::vector<std::string> knobModeLabels = { | |||||
| "Linear", | |||||
| @@ -467,6 +506,21 @@ | |||||
| menu->addChild(knobScrollSensitivitySlider); | |||||
| menu->addChild(createBoolPtrMenuItem("Lock module positions", "", &settings::lockModules)); | |||||
| + | |||||
| + static const std::vector<std::string> rateLimitLabels = { | |||||
| + "None", | |||||
| + "2x", | |||||
| + "4x", | |||||
| + }; | |||||
| + static const std::vector<int> rateLimits = {0, 1, 2}; | |||||
| + menu->addChild(createSubmenuItem("Update rate limit", rateLimitLabels[settings::rateLimit], [=](ui::Menu* menu) { | |||||
| + for (int rateLimit : rateLimits) { | |||||
| + menu->addChild(createCheckMenuItem(rateLimitLabels[rateLimit], "", | |||||
| + [=]() {return settings::rateLimit == rateLimit;}, | |||||
| + [=]() {settings::rateLimit = rateLimit;} | |||||
| + )); | |||||
| + } | |||||
| + })); | |||||
| } | |||||
| }; | |||||
| @@ -476,47 +530,6 @@ | |||||
| //////////////////// | |||||
| -struct SampleRateItem : ui::MenuItem { | |||||
| - ui::Menu* createChildMenu() override { | |||||
| - ui::Menu* menu = new ui::Menu; | |||||
| - | |||||
| - // Auto sample rate | |||||
| - std::string rightText; | |||||
| - if (settings::sampleRate == 0) { | |||||
| - float sampleRate = APP->engine->getSampleRate(); | |||||
| - rightText += string::f("(%g kHz) ", sampleRate / 1000.f); | |||||
| - } | |||||
| - menu->addChild(createCheckMenuItem("Auto", rightText, | |||||
| - [=]() {return settings::sampleRate == 0;}, | |||||
| - [=]() {settings::sampleRate = 0;} | |||||
| - )); | |||||
| - | |||||
| - // Power-of-2 oversample times 44.1kHz or 48kHz | |||||
| - for (int i = -2; i <= 4; i++) { | |||||
| - for (int j = 0; j < 2; j++) { | |||||
| - float oversample = std::pow(2.f, i); | |||||
| - float sampleRate = (j == 0) ? 44100.f : 48000.f; | |||||
| - sampleRate *= oversample; | |||||
| - | |||||
| - std::string text = string::f("%g kHz", sampleRate / 1000.f); | |||||
| - std::string rightText; | |||||
| - if (oversample > 1.f) { | |||||
| - rightText += string::f("(%.0fx)", oversample); | |||||
| - } | |||||
| - else if (oversample < 1.f) { | |||||
| - rightText += string::f("(1/%.0fx)", 1.f / oversample); | |||||
| - } | |||||
| - menu->addChild(createCheckMenuItem(text, rightText, | |||||
| - [=]() {return settings::sampleRate == sampleRate;}, | |||||
| - [=]() {settings::sampleRate = sampleRate;} | |||||
| - )); | |||||
| - } | |||||
| - } | |||||
| - return menu; | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| struct EngineButton : MenuButton { | |||||
| void onAction(const ActionEvent& e) override { | |||||
| ui::Menu* menu = createMenu(); | |||||
| @@ -529,269 +542,6 @@ | |||||
| menu->addChild(createMenuItem("Performance meters", cpuMeterText, [=]() { | |||||
| settings::cpuMeter ^= true; | |||||
| })); | |||||
| - | |||||
| - menu->addChild(createMenuItem<SampleRateItem>("Sample rate", RIGHT_ARROW)); | |||||
| - | |||||
| - menu->addChild(createSubmenuItem("Threads", string::f("%d", settings::threadCount), [=](ui::Menu* menu) { | |||||
| - // BUG This assumes SMT is enabled. | |||||
| - int cores = system::getLogicalCoreCount() / 2; | |||||
| - | |||||
| - for (int i = 1; i <= 2 * cores; i++) { | |||||
| - std::string rightText; | |||||
| - if (i == cores) | |||||
| - rightText += "(most modules)"; | |||||
| - else if (i == 1) | |||||
| - rightText += "(lowest CPU usage)"; | |||||
| - menu->addChild(createCheckMenuItem(string::f("%d", i), rightText, | |||||
| - [=]() {return settings::threadCount == i;}, | |||||
| - [=]() {settings::threadCount = i;} | |||||
| - )); | |||||
| - } | |||||
| - })); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -//////////////////// | |||||
| -// Plugins | |||||
| -//////////////////// | |||||
| - | |||||
| - | |||||
| -struct AccountPasswordField : ui::PasswordField { | |||||
| - ui::MenuItem* logInItem; | |||||
| - void onAction(const ActionEvent& e) override { | |||||
| - logInItem->doAction(); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct LogInItem : ui::MenuItem { | |||||
| - ui::TextField* emailField; | |||||
| - ui::TextField* passwordField; | |||||
| - | |||||
| - void onAction(const ActionEvent& e) override { | |||||
| - std::string email = emailField->text; | |||||
| - std::string password = passwordField->text; | |||||
| - std::thread t([=] { | |||||
| - library::logIn(email, password); | |||||
| - library::checkUpdates(); | |||||
| - }); | |||||
| - t.detach(); | |||||
| - e.unconsume(); | |||||
| - } | |||||
| - | |||||
| - void step() override { | |||||
| - text = "Log in"; | |||||
| - rightText = library::loginStatus; | |||||
| - MenuItem::step(); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct SyncUpdatesItem : ui::MenuItem { | |||||
| - void step() override { | |||||
| - if (library::updateStatus != "") { | |||||
| - text = library::updateStatus; | |||||
| - } | |||||
| - else if (library::isSyncing) { | |||||
| - text = "Updating..."; | |||||
| - } | |||||
| - else if (!library::hasUpdates()) { | |||||
| - text = "Up-to-date"; | |||||
| - } | |||||
| - else { | |||||
| - text = "Update all"; | |||||
| - } | |||||
| - | |||||
| - disabled = library::isSyncing || !library::hasUpdates(); | |||||
| - MenuItem::step(); | |||||
| - } | |||||
| - | |||||
| - void onAction(const ActionEvent& e) override { | |||||
| - std::thread t([=] { | |||||
| - library::syncUpdates(); | |||||
| - }); | |||||
| - t.detach(); | |||||
| - e.unconsume(); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct SyncUpdateItem : ui::MenuItem { | |||||
| - std::string slug; | |||||
| - | |||||
| - void setUpdate(const std::string& slug) { | |||||
| - this->slug = slug; | |||||
| - | |||||
| - auto it = library::updateInfos.find(slug); | |||||
| - if (it == library::updateInfos.end()) | |||||
| - return; | |||||
| - library::UpdateInfo update = it->second; | |||||
| - | |||||
| - text = update.name; | |||||
| - } | |||||
| - | |||||
| - ui::Menu* createChildMenu() override { | |||||
| - auto it = library::updateInfos.find(slug); | |||||
| - if (it == library::updateInfos.end()) | |||||
| - return NULL; | |||||
| - library::UpdateInfo update = it->second; | |||||
| - | |||||
| - if (update.changelogUrl == "") | |||||
| - return NULL; | |||||
| - | |||||
| - ui::Menu* menu = new ui::Menu; | |||||
| - | |||||
| - std::string changelogUrl = update.changelogUrl; | |||||
| - menu->addChild(createMenuItem("Changelog", "", [=]() { | |||||
| - system::openBrowser(changelogUrl); | |||||
| - })); | |||||
| - | |||||
| - return menu; | |||||
| - } | |||||
| - | |||||
| - void step() override { | |||||
| - disabled = library::isSyncing; | |||||
| - | |||||
| - auto it = library::updateInfos.find(slug); | |||||
| - if (it != library::updateInfos.end()) { | |||||
| - library::UpdateInfo update = it->second; | |||||
| - | |||||
| - if (update.downloaded) { | |||||
| - rightText = CHECKMARK_STRING; | |||||
| - disabled = true; | |||||
| - } | |||||
| - else if (slug == library::updateSlug) { | |||||
| - rightText = string::f("%.0f%%", library::updateProgress * 100.f); | |||||
| - } | |||||
| - else { | |||||
| - rightText = ""; | |||||
| - plugin::Plugin* p = plugin::getPlugin(slug); | |||||
| - if (p) { | |||||
| - rightText += p->version + " → "; | |||||
| - } | |||||
| - rightText += update.version; | |||||
| - } | |||||
| - } | |||||
| - | |||||
| - MenuItem::step(); | |||||
| - } | |||||
| - | |||||
| - void onAction(const ActionEvent& e) override { | |||||
| - std::thread t([=] { | |||||
| - library::syncUpdate(slug); | |||||
| - }); | |||||
| - t.detach(); | |||||
| - e.unconsume(); | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct LibraryMenu : ui::Menu { | |||||
| - LibraryMenu() { | |||||
| - refresh(); | |||||
| - } | |||||
| - | |||||
| - void step() override { | |||||
| - // Refresh menu when appropriate | |||||
| - if (library::refreshRequested) { | |||||
| - library::refreshRequested = false; | |||||
| - refresh(); | |||||
| - } | |||||
| - Menu::step(); | |||||
| - } | |||||
| - | |||||
| - void refresh() { | |||||
| - setChildMenu(NULL); | |||||
| - clearChildren(); | |||||
| - | |||||
| - if (settings::devMode) { | |||||
| - addChild(createMenuLabel("Disabled in development mode")); | |||||
| - } | |||||
| - else if (!library::isLoggedIn()) { | |||||
| - addChild(createMenuItem("Register VCV account", "", [=]() { | |||||
| - system::openBrowser("https://vcvrack.com/login"); | |||||
| - })); | |||||
| - | |||||
| - ui::TextField* emailField = new ui::TextField; | |||||
| - emailField->placeholder = "Email"; | |||||
| - emailField->box.size.x = 240.0; | |||||
| - addChild(emailField); | |||||
| - | |||||
| - AccountPasswordField* passwordField = new AccountPasswordField; | |||||
| - passwordField->placeholder = "Password"; | |||||
| - passwordField->box.size.x = 240.0; | |||||
| - passwordField->nextField = emailField; | |||||
| - emailField->nextField = passwordField; | |||||
| - addChild(passwordField); | |||||
| - | |||||
| - LogInItem* logInItem = new LogInItem; | |||||
| - logInItem->emailField = emailField; | |||||
| - logInItem->passwordField = passwordField; | |||||
| - passwordField->logInItem = logInItem; | |||||
| - addChild(logInItem); | |||||
| - } | |||||
| - else { | |||||
| - addChild(createMenuItem("Log out", "", [=]() { | |||||
| - library::logOut(); | |||||
| - })); | |||||
| - | |||||
| - addChild(createMenuItem("Browse VCV Library", "", [=]() { | |||||
| - system::openBrowser("https://library.vcvrack.com/"); | |||||
| - })); | |||||
| - | |||||
| - SyncUpdatesItem* syncItem = new SyncUpdatesItem; | |||||
| - syncItem->text = "Update all"; | |||||
| - addChild(syncItem); | |||||
| - | |||||
| - if (!library::updateInfos.empty()) { | |||||
| - addChild(new ui::MenuSeparator); | |||||
| - addChild(createMenuLabel("Updates")); | |||||
| - | |||||
| - for (auto& pair : library::updateInfos) { | |||||
| - SyncUpdateItem* updateItem = new SyncUpdateItem; | |||||
| - updateItem->setUpdate(pair.first); | |||||
| - addChild(updateItem); | |||||
| - } | |||||
| - } | |||||
| - } | |||||
| - } | |||||
| -}; | |||||
| - | |||||
| - | |||||
| -struct LibraryButton : MenuButton { | |||||
| - NotificationIcon* notification; | |||||
| - | |||||
| - LibraryButton() { | |||||
| - notification = new NotificationIcon; | |||||
| - addChild(notification); | |||||
| - } | |||||
| - | |||||
| - void onAction(const ActionEvent& e) override { | |||||
| - ui::Menu* menu = createMenu<LibraryMenu>(); | |||||
| - menu->cornerFlags = BND_CORNER_TOP; | |||||
| - menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | |||||
| - // Check for updates when menu is opened | |||||
| - std::thread t([&]() { | |||||
| - system::setThreadName("Library"); | |||||
| - library::checkUpdates(); | |||||
| - }); | |||||
| - t.detach(); | |||||
| - } | |||||
| - | |||||
| - void step() override { | |||||
| - notification->box.pos = math::Vec(0, 0); | |||||
| - notification->visible = library::hasUpdates(); | |||||
| - | |||||
| - // Popup when updates finish downloading | |||||
| - if (library::restartRequested) { | |||||
| - library::restartRequested = false; | |||||
| - if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "All plugins have been downloaded. Close and re-launch Rack to load new updates.")) { | |||||
| - APP->window->close(); | |||||
| - } | |||||
| - } | |||||
| - | |||||
| - MenuButton::step(); | |||||
| } | |||||
| }; | |||||
| @@ -802,63 +552,24 @@ | |||||
| struct HelpButton : MenuButton { | |||||
| - NotificationIcon* notification; | |||||
| - | |||||
| - HelpButton() { | |||||
| - notification = new NotificationIcon; | |||||
| - addChild(notification); | |||||
| - } | |||||
| - | |||||
| void onAction(const ActionEvent& e) override { | |||||
| ui::Menu* menu = createMenu(); | |||||
| menu->cornerFlags = BND_CORNER_TOP; | |||||
| menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); | |||||
| - menu->addChild(createMenuItem("Tips", "", [=]() { | |||||
| - APP->scene->addChild(tipWindowCreate()); | |||||
| - })); | |||||
| - | |||||
| - menu->addChild(createMenuItem("User manual", "F1", [=]() { | |||||
| + menu->addChild(createMenuItem("Rack User manual", "F1", [=]() { | |||||
| system::openBrowser("https://vcvrack.com/manual/"); | |||||
| })); | |||||
| - menu->addChild(createMenuItem("VCVRack.com", "", [=]() { | |||||
| - system::openBrowser("https://vcvrack.com/"); | |||||
| - })); | |||||
| - | |||||
| - menu->addChild(createMenuItem("Open user folder", "", [=]() { | |||||
| - system::openDirectory(asset::user("")); | |||||
| + menu->addChild(createMenuItem("Cardinal Project page", "", [=]() { | |||||
| + system::openBrowser("https://github.com/DISTRHO/Cardinal/"); | |||||
| })); | |||||
| - if (library::isAppUpdateAvailable()) { | |||||
| - menu->addChild(new ui::MenuSeparator); | |||||
| - | |||||
| - menu->addChild(createMenuItem("Update " + APP_NAME, APP_VERSION + " → " + library::appVersion, [=]() { | |||||
| - system::openBrowser(library::appDownloadUrl); | |||||
| - })); | |||||
| - | |||||
| - menu->addChild(createMenuItem("Review changelog", "", [=]() { | |||||
| - system::openBrowser(library::appChangelogUrl); | |||||
| - })); | |||||
| - } | |||||
| - else if (!settings::autoCheckUpdates && !settings::devMode) { | |||||
| - menu->addChild(createMenuItem("Check for " + APP_NAME + " update", "", [=]() { | |||||
| - std::thread t([&]() { | |||||
| - library::checkAppUpdate(); | |||||
| - }); | |||||
| - t.detach(); | |||||
| - }, false, true)); | |||||
| - } | |||||
| - | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| - menu->addChild(createMenuLabel(APP_NAME + " " + APP_EDITION_NAME + " " + APP_VERSION)); | |||||
| - } | |||||
| + menu->addChild(createMenuLabel(APP_EDITION + " " + APP_EDITION_NAME)); | |||||
| - void step() override { | |||||
| - notification->box.pos = math::Vec(0, 0); | |||||
| - notification->visible = library::isAppUpdateAvailable(); | |||||
| - MenuButton::step(); | |||||
| + menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible")); | |||||
| } | |||||
| }; | |||||
| @@ -908,7 +619,9 @@ | |||||
| struct MenuBar : widget::OpaqueWidget { | |||||
| MeterLabel* meterLabel; | |||||
| - MenuBar() { | |||||
| + MenuBar(const bool isStandalone) | |||||
| + : widget::OpaqueWidget() | |||||
| + { | |||||
| const float margin = 5; | |||||
| box.size.y = BND_WIDGET_HEIGHT + 2 * margin; | |||||
| @@ -917,7 +630,7 @@ | |||||
| layout->spacing = math::Vec(0, 0); | |||||
| addChild(layout); | |||||
| - FileButton* fileButton = new FileButton; | |||||
| + FileButton* fileButton = new FileButton(isStandalone); | |||||
| fileButton->text = "File"; | |||||
| layout->addChild(fileButton); | |||||
| @@ -933,10 +646,6 @@ | |||||
| engineButton->text = "Engine"; | |||||
| layout->addChild(engineButton); | |||||
| - LibraryButton* libraryButton = new LibraryButton; | |||||
| - libraryButton->text = "Library"; | |||||
| - layout->addChild(libraryButton); | |||||
| - | |||||
| HelpButton* helpButton = new HelpButton; | |||||
| helpButton->text = "Help"; | |||||
| layout->addChild(helpButton); | |||||
| @@ -971,7 +680,11 @@ | |||||
| widget::Widget* createMenuBar() { | |||||
| - menuBar::MenuBar* menuBar = new menuBar::MenuBar; | |||||
| + return new widget::Widget; | |||||
| +} | |||||
| + | |||||
| +widget::Widget* createMenuBar(const bool isStandalone) { | |||||
| + menuBar::MenuBar* menuBar = new menuBar::MenuBar(isStandalone); | |||||
| return menuBar; | |||||
| } | |||||
| @@ -0,0 +1,129 @@ | |||||
| --- ../Rack/src/plugin/Model.cpp 2021-10-17 13:57:23.257633662 +0100 | |||||
| +++ Model.cpp 2022-01-23 17:13:22.080013846 +0000 | |||||
| @@ -1,3 +1,30 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's plugin/Model.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <algorithm> | |||||
| #include <plugin/Model.hpp> | |||||
| @@ -17,7 +44,7 @@ | |||||
| void Model::fromJson(json_t* rootJ) { | |||||
| - assert(plugin); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr,); | |||||
| json_t* nameJ = json_object_get(rootJ, "name"); | |||||
| if (nameJ) | |||||
| @@ -54,11 +81,6 @@ | |||||
| if (manualUrlJ) | |||||
| manualUrl = json_string_value(manualUrlJ); | |||||
| - // modularGridUrl | |||||
| - json_t* modularGridUrlJ = json_object_get(rootJ, "modularGridUrl"); | |||||
| - if (modularGridUrlJ) | |||||
| - modularGridUrl = json_string_value(modularGridUrlJ); | |||||
| - | |||||
| // hidden | |||||
| json_t* hiddenJ = json_object_get(rootJ, "hidden"); | |||||
| // Use `disabled` as an alias which was deprecated in Rack 2.0 | |||||
| @@ -73,7 +95,7 @@ | |||||
| std::string Model::getFullName() { | |||||
| - assert(plugin); | |||||
| + DISTRHO_SAFE_ASSERT_RETURN(plugin, {}); | |||||
| return plugin->getBrand() + " " + name; | |||||
| } | |||||
| @@ -95,7 +117,7 @@ | |||||
| } | |||||
| -void Model::appendContextMenu(ui::Menu* menu, bool inBrowser) { | |||||
| +void Model::appendContextMenu(ui::Menu* menu, bool) { | |||||
| // plugin | |||||
| menu->addChild(createMenuItem("Plugin: " + plugin->name, "", [=]() { | |||||
| system::openBrowser(plugin->pluginUrl); | |||||
| @@ -132,18 +154,6 @@ | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| - // VCV Library page | |||||
| - menu->addChild(createMenuItem("VCV Library page", "", [=]() { | |||||
| - system::openBrowser("https://library.vcvrack.com/" + plugin->slug + "/" + slug); | |||||
| - })); | |||||
| - | |||||
| - // modularGridUrl | |||||
| - if (modularGridUrl != "") { | |||||
| - menu->addChild(createMenuItem("ModularGrid page", "", [=]() { | |||||
| - system::openBrowser(modularGridUrl); | |||||
| - })); | |||||
| - } | |||||
| - | |||||
| // manual | |||||
| std::string manualUrl = getManualUrl(); | |||||
| if (manualUrl != "") { | |||||
| @@ -172,35 +182,15 @@ | |||||
| system::openBrowser(plugin->changelogUrl); | |||||
| })); | |||||
| } | |||||
| - | |||||
| - // plugin folder | |||||
| - if (plugin->path != "") { | |||||
| - menu->addChild(createMenuItem("Open plugin folder", "", [=]() { | |||||
| - system::openDirectory(plugin->path); | |||||
| - })); | |||||
| - } | |||||
| - | |||||
| - // Favorite | |||||
| - std::string favoriteRightText = inBrowser ? (RACK_MOD_CTRL_NAME "+click") : ""; | |||||
| - if (isFavorite()) | |||||
| - favoriteRightText += " " CHECKMARK_STRING; | |||||
| - menu->addChild(createMenuItem("Favorite", favoriteRightText, | |||||
| - [=]() { | |||||
| - setFavorite(!isFavorite()); | |||||
| - } | |||||
| - )); | |||||
| } | |||||
| bool Model::isFavorite() { | |||||
| - const settings::ModuleInfo* mi = settings::getModuleInfo(plugin->slug, slug); | |||||
| - return mi && mi->favorite; | |||||
| + return false; | |||||
| } | |||||
| -void Model::setFavorite(bool favorite) { | |||||
| - settings::ModuleInfo& mi = settings::moduleInfos[plugin->slug][slug]; | |||||
| - mi.favorite = favorite; | |||||
| +void Model::setFavorite(bool) { | |||||
| } | |||||
| @@ -0,0 +1,234 @@ | |||||
| --- ../Rack/src/app/Scene.cpp 2021-12-14 21:35:44.414568198 +0000 | |||||
| +++ Scene.cpp 2022-01-23 17:13:24.715889665 +0000 | |||||
| @@ -1,3 +1,30 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's app/Scene.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <thread> | |||||
| #include <osdialog.h> | |||||
| @@ -14,31 +41,49 @@ | |||||
| #include <patch.hpp> | |||||
| #include <asset.hpp> | |||||
| +#include "../CardinalCommon.hpp" | |||||
| + | |||||
| namespace rack { | |||||
| namespace app { | |||||
| struct ResizeHandle : widget::OpaqueWidget { | |||||
| - math::Vec size; | |||||
| - | |||||
| void draw(const DrawArgs& args) override { | |||||
| + nvgStrokeColor(args.vg, nvgRGBf(1, 1, 1)); | |||||
| + nvgStrokeWidth(args.vg, 1); | |||||
| + | |||||
| nvgBeginPath(args.vg); | |||||
| - nvgMoveTo(args.vg, box.size.x, box.size.y); | |||||
| + nvgMoveTo(args.vg, box.size.x, 0); | |||||
| nvgLineTo(args.vg, 0, box.size.y); | |||||
| - nvgLineTo(args.vg, box.size.x, 0); | |||||
| - nvgClosePath(args.vg); | |||||
| - nvgFillColor(args.vg, nvgRGBAf(1, 1, 1, 0.15)); | |||||
| - nvgFill(args.vg); | |||||
| - } | |||||
| + nvgStroke(args.vg); | |||||
| - void onDragStart(const DragStartEvent& e) override { | |||||
| - size = APP->window->getSize(); | |||||
| - } | |||||
| + nvgBeginPath(args.vg); | |||||
| + nvgMoveTo(args.vg, box.size.x + 5, 0); | |||||
| + nvgLineTo(args.vg, 0, box.size.y + 5); | |||||
| + nvgStroke(args.vg); | |||||
| + | |||||
| + nvgBeginPath(args.vg); | |||||
| + nvgMoveTo(args.vg, box.size.x + 10, 0); | |||||
| + nvgLineTo(args.vg, 0, box.size.y + 10); | |||||
| + nvgStroke(args.vg); | |||||
| + | |||||
| + nvgStrokeColor(args.vg, nvgRGBf(0, 0, 0)); | |||||
| + | |||||
| + nvgBeginPath(args.vg); | |||||
| + nvgMoveTo(args.vg, box.size.x+1, 0); | |||||
| + nvgLineTo(args.vg, 0, box.size.y+1); | |||||
| + nvgStroke(args.vg); | |||||
| - void onDragMove(const DragMoveEvent& e) override { | |||||
| - size = size.plus(e.mouseDelta); | |||||
| - APP->window->setSize(size.round()); | |||||
| + nvgBeginPath(args.vg); | |||||
| + nvgMoveTo(args.vg, box.size.x + 6, 0); | |||||
| + nvgLineTo(args.vg, 0, box.size.y + 6); | |||||
| + nvgStroke(args.vg); | |||||
| + | |||||
| + nvgBeginPath(args.vg); | |||||
| + nvgMoveTo(args.vg, box.size.x + 11, 0); | |||||
| + nvgLineTo(args.vg, 0, box.size.y + 11); | |||||
| + nvgStroke(args.vg); | |||||
| } | |||||
| }; | |||||
| @@ -46,12 +91,15 @@ | |||||
| struct Scene::Internal { | |||||
| ResizeHandle* resizeHandle; | |||||
| - double lastAutosaveTime = 0.0; | |||||
| - | |||||
| bool heldArrowKeys[4] = {}; | |||||
| }; | |||||
| +void hideResizeHandle(Scene* scene) { | |||||
| + scene->internal->resizeHandle->hide(); | |||||
| +} | |||||
| + | |||||
| + | |||||
| Scene::Scene() { | |||||
| internal = new Internal; | |||||
| @@ -67,13 +115,8 @@ | |||||
| browser->hide(); | |||||
| addChild(browser); | |||||
| - if (settings::showTipsOnLaunch) { | |||||
| - addChild(tipWindowCreate()); | |||||
| - } | |||||
| - | |||||
| internal->resizeHandle = new ResizeHandle; | |||||
| - internal->resizeHandle->box.size = math::Vec(15, 15); | |||||
| - internal->resizeHandle->hide(); | |||||
| + internal->resizeHandle->box.size = math::Vec(16, 16); | |||||
| addChild(internal->resizeHandle); | |||||
| } | |||||
| @@ -89,32 +132,13 @@ | |||||
| void Scene::step() { | |||||
| - if (APP->window->isFullScreen()) { | |||||
| - // Expand RackScrollWidget to cover entire screen if fullscreen | |||||
| - rackScroll->box.pos.y = 0; | |||||
| - } | |||||
| - else { | |||||
| - // Always show MenuBar if not fullscreen | |||||
| - menuBar->show(); | |||||
| - rackScroll->box.pos.y = menuBar->box.size.y; | |||||
| - } | |||||
| - | |||||
| internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size); | |||||
| // Resize owned descendants | |||||
| menuBar->box.size.x = box.size.x; | |||||
| + rackScroll->box.pos.y = menuBar->box.size.y; | |||||
| rackScroll->box.size = box.size.minus(rackScroll->box.pos); | |||||
| - // Autosave periodically | |||||
| - if (settings::autosaveInterval > 0.0) { | |||||
| - double time = system::getTime(); | |||||
| - if (time - internal->lastAutosaveTime >= settings::autosaveInterval) { | |||||
| - internal->lastAutosaveTime = time; | |||||
| - APP->patch->saveAutosave(); | |||||
| - settings::save(); | |||||
| - } | |||||
| - } | |||||
| - | |||||
| // Scroll RackScrollWidget with arrow keys | |||||
| math::Vec arrowDelta; | |||||
| if (internal->heldArrowKeys[0]) { | |||||
| @@ -172,7 +196,7 @@ | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||||
| // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str()); | |||||
| if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| - APP->patch->loadTemplateDialog(); | |||||
| + patchUtils::loadTemplateDialog(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| @@ -180,19 +204,20 @@ | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| - APP->patch->loadDialog(); | |||||
| + patchUtils::loadDialog(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { | |||||
| - APP->patch->revertDialog(); | |||||
| + patchUtils::revertDialog(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| - APP->patch->saveDialog(); | |||||
| + // NOTE: will do nothing if path is empty, intentionally | |||||
| + patchUtils::saveDialog(APP->patch->path); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { | |||||
| - APP->patch->saveAsDialog(); | |||||
| + patchUtils::saveAsDialog(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| @@ -232,10 +257,8 @@ | |||||
| settings::cpuMeter ^= true; | |||||
| e.consume(this); | |||||
| } | |||||
| - if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) { | |||||
| - APP->window->setFullScreen(!APP->window->isFullScreen()); | |||||
| - // The MenuBar will be hidden when the mouse moves over the RackScrollWidget. | |||||
| - // menuBar->hide(); | |||||
| + if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) { | |||||
| + patchUtils::deployToMOD(); | |||||
| e.consume(this); | |||||
| } | |||||
| @@ -326,13 +349,6 @@ | |||||
| // Key commands that can be overridden by children | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||||
| - // Alternate key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding. | |||||
| - if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) { | |||||
| - if (APP->window->isFullScreen()) { | |||||
| - APP->window->setFullScreen(false); | |||||
| - e.consume(this); | |||||
| - } | |||||
| - } | |||||
| if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| rack->pasteClipboardAction(); | |||||
| e.consume(this); | |||||
| @@ -351,7 +367,7 @@ | |||||
| std::string extension = system::getExtension(path); | |||||
| if (extension == ".vcv") { | |||||
| - APP->patch->loadPathDialog(path); | |||||
| + patchUtils::loadPathDialog(path); | |||||
| e.consume(this); | |||||
| return; | |||||
| } | |||||
| @@ -0,0 +1,29 @@ | |||||
| --- ../Rack/dep/oui-blendish/blendish.c 2021-10-17 13:57:24.613620711 +0100 | |||||
| +++ blendish.c 2021-12-13 09:36:22.182673256 +0000 | |||||
| @@ -61,7 +61,7 @@ | |||||
| } | |||||
| #else | |||||
| - #define BND_INLINE inline | |||||
| + #define BND_INLINE static inline | |||||
| #define bnd_fminf(a, b) fminf(a, b) | |||||
| #define bnd_fmaxf(a, b) fmaxf(a, b) | |||||
| #define bnd_fmin(a, b) fmin(a, b) | |||||
| @@ -1061,7 +1061,7 @@ | |||||
| // search horizontal position | |||||
| static NVGglyphPosition glyphs[BND_MAX_GLYPHS]; | |||||
| int nglyphs = nvgTextGlyphPositions( | |||||
| - ctx, x, y, rows[row].start, rows[row].end + 1, glyphs, BND_MAX_GLYPHS); | |||||
| + ctx, x, y, rows[row].start, rows[row].end, glyphs, BND_MAX_GLYPHS); | |||||
| int col, p = 0; | |||||
| for (col = 0; col < nglyphs && glyphs[col].x < px; ++col) | |||||
| p = glyphs[col].str - label; | |||||
| @@ -1083,7 +1083,7 @@ | |||||
| if (nrows == 0) return; | |||||
| *cx = rows[r].minx; | |||||
| nglyphs = nvgTextGlyphPositions( | |||||
| - ctx, x, y, rows[r].start, rows[r].end+1, glyphs, BND_MAX_GLYPHS); | |||||
| + ctx, x, y, rows[r].start, rows[r].end, glyphs, BND_MAX_GLYPHS); | |||||
| for (int i=0; i < nglyphs; ++i) { | |||||
| *cx=glyphs[i].x; | |||||
| if (glyphs[i].str == caret) break; | |||||
| @@ -0,0 +1,69 @@ | |||||
| --- ../Rack/src/common.cpp 2021-11-23 19:57:23.719015894 +0000 | |||||
| +++ common.cpp 2022-01-23 17:13:08.824652617 +0000 | |||||
| @@ -1,6 +1,38 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's common.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <common.hpp> | |||||
| #include <string.hpp> | |||||
| +#ifdef NDEBUG | |||||
| +# undef DEBUG | |||||
| +#endif | |||||
| + | |||||
| +#include "DistrhoPluginUtils.hpp" | |||||
| #if defined ARCH_WIN | |||||
| #include <windows.h> | |||||
| @@ -14,20 +46,21 @@ | |||||
| namespace rack { | |||||
| - | |||||
| -const std::string APP_NAME = "VCV Rack"; | |||||
| -const std::string APP_EDITION = "Free"; | |||||
| -const std::string APP_EDITION_NAME = "Free"; | |||||
| +const std::string APP_NAME = "Cardinal"; | |||||
| +const std::string APP_EDITION = getPluginFormatName(); | |||||
| +const std::string APP_EDITION_NAME = "Audio Plugin"; | |||||
| const std::string APP_VERSION_MAJOR = "2"; | |||||
| -const std::string APP_VERSION = TOSTRING(_APP_VERSION); | |||||
| +const std::string APP_VERSION = "2.0"; | |||||
| #if defined ARCH_WIN | |||||
| const std::string APP_OS = "win"; | |||||
| #elif ARCH_MAC | |||||
| const std::string APP_OS = "mac"; | |||||
| #elif defined ARCH_LIN | |||||
| const std::string APP_OS = "lin"; | |||||
| +#else | |||||
| + #error ARCH_LIN undefined | |||||
| #endif | |||||
| -const std::string API_URL = "https://api.vcvrack.com"; | |||||
| +const std::string API_URL = ""; | |||||
| Exception::Exception(const char* format, ...) { | |||||
| @@ -0,0 +1,57 @@ | |||||
| --- ../Rack/src/context.cpp 2022-01-15 14:44:46.391280963 +0000 | |||||
| +++ context.cpp 2022-01-23 17:13:11.652514338 +0000 | |||||
| @@ -1,3 +1,30 @@ | |||||
| +/* | |||||
| + * DISTRHO Cardinal Plugin | |||||
| + * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| + * | |||||
| + * This program is free software; you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or any later version. | |||||
| + * | |||||
| + * This program is distributed in the hope that it will be useful, | |||||
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| + * GNU General Public License for more details. | |||||
| + * | |||||
| + * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's context.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * This program is free software: you can redistribute it and/or | |||||
| + * modify it under the terms of the GNU General Public License as | |||||
| + * published by the Free Software Foundation; either version 3 of | |||||
| + * the License, or (at your option) any later version. | |||||
| + */ | |||||
| + | |||||
| #include <context.hpp> | |||||
| #include <window/Window.hpp> | |||||
| #include <patch.hpp> | |||||
| @@ -6,9 +33,13 @@ | |||||
| #include <history.hpp> | |||||
| #include <settings.hpp> | |||||
| +#ifdef NDEBUG | |||||
| +# undef DEBUG | |||||
| +#endif | |||||
| -namespace rack { | |||||
| +#include "DistrhoUtils.hpp" | |||||
| +namespace rack { | |||||
| Context::~Context() { | |||||
| // Deleting NULL is safe in C++. | |||||
| @@ -44,7 +75,7 @@ | |||||
| static thread_local Context* threadContext = NULL; | |||||
| Context* contextGet() { | |||||
| - assert(threadContext); | |||||
| + DISTRHO_SAFE_ASSERT(threadContext != nullptr); | |||||
| return threadContext; | |||||
| } | |||||