Signed-off-by: falkTX <falktx@falktx.com>tags/23.02
| @@ -29,6 +29,12 @@ | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| // from PluginContext.hpp | |||
| #ifndef HEADLESS | |||
| START_NAMESPACE_DGL | |||
| class NanoTopLevelWidget; | |||
| END_NAMESPACE_DGL | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| static constexpr const uint32_t kModuleParameters = 24; | |||
| @@ -68,6 +74,7 @@ struct CardinalPluginContext : rack::Context { | |||
| uint32_t midiEventCount; | |||
| Plugin* const plugin; | |||
| #ifndef HEADLESS | |||
| DGL_NAMESPACE::NanoTopLevelWidget* tlw; | |||
| UI* ui; | |||
| #endif | |||
| CardinalPluginContext(Plugin* const p); | |||
| @@ -59,6 +59,115 @@ | |||
| const std::string CARDINAL_VERSION = "22.12"; | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| void handleHostParameterDrag(const CardinalPluginContext* pcontext, uint index, bool started) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pcontext->ui != nullptr,); | |||
| if (started) | |||
| { | |||
| pcontext->ui->editParameter(index, true); | |||
| pcontext->ui->setParameterValue(index, pcontext->parameters[index]); | |||
| } | |||
| else | |||
| { | |||
| pcontext->ui->editParameter(index, false); | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| bool CardinalPluginContext::addIdleCallback(IdleCallback* const cb) const | |||
| { | |||
| if (ui == nullptr) | |||
| return false; | |||
| ui->addIdleCallback(cb); | |||
| return true; | |||
| } | |||
| void CardinalPluginContext::removeIdleCallback(IdleCallback* const cb) const | |||
| { | |||
| if (ui == nullptr) | |||
| return; | |||
| ui->removeIdleCallback(cb); | |||
| } | |||
| void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message, const uint8_t channel) | |||
| { | |||
| if (bypassed) | |||
| return; | |||
| const size_t size = message.bytes.size(); | |||
| DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(message.frame >= 0,); | |||
| MidiEvent event; | |||
| event.frame = message.frame; | |||
| switch (message.bytes[0] & 0xF0) | |||
| { | |||
| case 0x80: | |||
| case 0x90: | |||
| case 0xA0: | |||
| case 0xB0: | |||
| case 0xE0: | |||
| event.size = 3; | |||
| break; | |||
| case 0xC0: | |||
| case 0xD0: | |||
| event.size = 2; | |||
| break; | |||
| case 0xF0: | |||
| switch (message.bytes[0] & 0x0F) | |||
| { | |||
| case 0x0: | |||
| case 0x4: | |||
| case 0x5: | |||
| case 0x7: | |||
| case 0x9: | |||
| case 0xD: | |||
| // unsupported | |||
| return; | |||
| case 0x1: | |||
| case 0x2: | |||
| case 0x3: | |||
| case 0xE: | |||
| event.size = 3; | |||
| break; | |||
| case 0x6: | |||
| case 0x8: | |||
| case 0xA: | |||
| case 0xB: | |||
| case 0xC: | |||
| case 0xF: | |||
| event.size = 1; | |||
| break; | |||
| } | |||
| break; | |||
| default: | |||
| // invalid | |||
| return; | |||
| } | |||
| DISTRHO_SAFE_ASSERT_RETURN(size >= event.size,); | |||
| std::memcpy(event.data, message.bytes.data(), event.size); | |||
| if (channel != 0 && event.data[0] < 0xF0) | |||
| event.data[0] |= channel & 0x0F; | |||
| plugin->writeMidiEvent(event); | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| namespace rack { | |||
| bool isStandalone() | |||
| @@ -118,6 +227,8 @@ std::string homeDir() | |||
| } // namespace rack | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| namespace patchUtils | |||
| { | |||
| @@ -289,6 +400,8 @@ void openBrowser(const std::string& url) | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| void async_dialog_filebrowser(const bool saving, | |||
| const char* const defaultName, | |||
| const char* const startDir, | |||
| @@ -430,75 +430,6 @@ struct Initializer | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message, const uint8_t channel) | |||
| { | |||
| if (bypassed) | |||
| return; | |||
| const size_t size = message.bytes.size(); | |||
| DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(message.frame >= 0,); | |||
| MidiEvent event; | |||
| event.frame = message.frame; | |||
| switch (message.bytes[0] & 0xF0) | |||
| { | |||
| case 0x80: | |||
| case 0x90: | |||
| case 0xA0: | |||
| case 0xB0: | |||
| case 0xE0: | |||
| event.size = 3; | |||
| break; | |||
| case 0xC0: | |||
| case 0xD0: | |||
| event.size = 2; | |||
| break; | |||
| case 0xF0: | |||
| switch (message.bytes[0] & 0x0F) | |||
| { | |||
| case 0x0: | |||
| case 0x4: | |||
| case 0x5: | |||
| case 0x7: | |||
| case 0x9: | |||
| case 0xD: | |||
| // unsupported | |||
| return; | |||
| case 0x1: | |||
| case 0x2: | |||
| case 0x3: | |||
| case 0xE: | |||
| event.size = 3; | |||
| break; | |||
| case 0x6: | |||
| case 0x8: | |||
| case 0xA: | |||
| case 0xB: | |||
| case 0xC: | |||
| case 0xF: | |||
| event.size = 1; | |||
| break; | |||
| } | |||
| break; | |||
| default: | |||
| // invalid | |||
| return; | |||
| } | |||
| DISTRHO_SAFE_ASSERT_RETURN(size >= event.size,); | |||
| std::memcpy(event.data, message.bytes.data(), event.size); | |||
| if (channel != 0 && event.data[0] < 0xF0) | |||
| event.data[0] |= channel & 0x0F; | |||
| plugin->writeMidiEvent(event); | |||
| } | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| struct ScopedContext { | |||
| ScopedContext(const CardinalBasePlugin* const plugin) | |||
| { | |||
| @@ -1393,6 +1324,6 @@ Plugin* createPlugin() | |||
| return new CardinalPlugin(); | |||
| } | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||
| @@ -7,8 +7,11 @@ | |||
| # -------------------------------------------------------------- | |||
| # Carla stuff | |||
| CWD = ../../carla/source | |||
| ifneq ($(STATIC_BUILD),true) | |||
| STATIC_PLUGIN_TARGET = true | |||
| CWD = ../../carla/source | |||
| include $(CWD)/Makefile.deps.mk | |||
| CARLA_BUILD_DIR = ../../carla/build | |||
| @@ -23,7 +26,9 @@ CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/carla_engine_ | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/carla_plugin.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/native-plugins.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/audio_decoder.a | |||
| ifneq ($(WASM),true) | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/jackbridge.min.a | |||
| endif | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/lilv.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/rtmempool.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/sfzero.a | |||
| @@ -31,10 +36,17 @@ CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/water.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/ysfx.a | |||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/zita-resampler.a | |||
| endif # STATIC_BUILD | |||
| # -------------------------------------------------------------- | |||
| # Import base definitions | |||
| DISTRHO_NAMESPACE = CardinalDISTRHO | |||
| DGL_NAMESPACE = CardinalDGL | |||
| NVG_DISABLE_SKIPPING_WHITESPACE = true | |||
| NVG_FONT_TEXTURE_FLAGS = NVG_IMAGE_NEAREST | |||
| USE_NANOVG_FBO = true | |||
| WASM_EXCEPTIONS = true | |||
| include ../../dpf/Makefile.base.mk | |||
| # -------------------------------------------------------------- | |||
| @@ -57,7 +69,11 @@ endif | |||
| # -------------------------------------------------------------- | |||
| # Extra libraries to link against | |||
| ifeq ($(NOPLUGINS),true) | |||
| RACK_EXTRA_LIBS = ../../plugins/noplugins.a | |||
| else | |||
| RACK_EXTRA_LIBS = ../../plugins/plugins.a | |||
| endif | |||
| RACK_EXTRA_LIBS += ../rack.a | |||
| RACK_EXTRA_LIBS += $(DEP_LIB_PATH)/libquickjs.a | |||
| @@ -74,22 +90,51 @@ RACK_EXTRA_LIBS += $(DEP_LIB_PATH)/libzstd.a | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # surgext libraries | |||
| ifneq ($(NOPLUGINS),true) | |||
| SURGE_DEP_PATH = $(abspath ../../deps/surge-build) | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/src/common/libsurge-common.a | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/src/common/libjuce_dsp_rack_sub.a | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/airwindows/libairwindows.a | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/eurorack/libeurorack.a | |||
| ifeq ($(DEBUG),true) | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/fmt/libfmtd.a | |||
| else | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/fmt/libfmt.a | |||
| endif | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/sqlite-3.23.3/libsqlite.a | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/sst/sst-plugininfra/libsst-plugininfra.a | |||
| ifneq ($(WINDOWS),true) | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/sst/sst-plugininfra/libs/filesystem/libfilesystem.a | |||
| endif | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/sst/sst-plugininfra/libs/strnatcmp/libstrnatcmp.a | |||
| RACK_EXTRA_LIBS += $(SURGE_DEP_PATH)/libs/sst/sst-plugininfra/libs/tinyxml/libtinyxml.a | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # FIXME | |||
| ifeq ($(CIBUILD)$(WASM),truetrue) | |||
| ifneq ($(STATIC_BUILD),true) | |||
| STATIC_CARLA_PLUGIN_LIBS = -lsndfile -lopus -lFLAC -lvorbisenc -lvorbis -logg -lm | |||
| endif | |||
| endif | |||
| EXTRA_DEPENDENCIES = $(RACK_EXTRA_LIBS) $(CARLA_EXTRA_LIBS) | |||
| EXTRA_LIBS = $(RACK_EXTRA_LIBS) $(CARLA_EXTRA_LIBS) $(STATIC_CARLA_PLUGIN_LIBS) | |||
| ifeq ($(shell pkg-config --exists fftw3f && echo true),true) | |||
| ifeq ($(shell $(PKG_CONFIG) --exists fftw3f && echo true),true) | |||
| EXTRA_DEPENDENCIES += ../../deps/aubio/libaubio.a | |||
| EXTRA_LIBS += ../../deps/aubio/libaubio.a | |||
| EXTRA_LIBS += $(shell $(PKG_CONFIG) --libs fftw3f) | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # Extra flags for liblo | |||
| BASE_FLAGS += -DHAVE_LIBLO | |||
| BASE_FLAGS += $(LIBLO_FLAGS) | |||
| LINK_FLAGS += $(LIBLO_LIBS) | |||
| ifneq ($(NOPLUGINS),true) | |||
| ifeq ($(MACOS),true) | |||
| EXTRA_LIBS += -framework Accelerate | |||
| endif | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # Extra flags for VCV stuff | |||
| @@ -106,11 +151,11 @@ BASE_FLAGS += -DPRIVATE= | |||
| BASE_FLAGS += -I.. | |||
| BASE_FLAGS += -I../../dpf/dgl/src/nanovg | |||
| BASE_FLAGS += -I../../include | |||
| BASE_FLAGS += -I../../include/neon-compat | |||
| BASE_FLAGS += -I../../include/simd-compat | |||
| BASE_FLAGS += -I../Rack/include | |||
| ifeq ($(SYSDEPS),true) | |||
| BASE_FLAGS += -DCARDINAL_SYSDEPS | |||
| BASE_FLAGS += $(shell pkg-config --cflags jansson libarchive samplerate speexdsp) | |||
| BASE_FLAGS += $(shell $(PKG_CONFIG) --cflags jansson libarchive samplerate speexdsp) | |||
| else | |||
| BASE_FLAGS += -DZSTDLIB_VISIBILITY= | |||
| BASE_FLAGS += -I../Rack/dep/include | |||
| @@ -119,41 +164,98 @@ BASE_FLAGS += -I../Rack/dep/glfw/include | |||
| BASE_FLAGS += -I../Rack/dep/nanosvg/src | |||
| BASE_FLAGS += -I../Rack/dep/oui-blendish | |||
| ifeq ($(WASM),true) | |||
| BASE_FLAGS += -DNANOVG_GLES2=1 | |||
| BASE_FLAGS += -msse -msse2 -msse3 -msimd128 | |||
| else ifneq ($(HAIKU),true) | |||
| ifeq ($(HEADLESS),true) | |||
| BASE_FLAGS += -DHEADLESS | |||
| endif | |||
| ifeq ($(MOD_BUILD),true) | |||
| BASE_FLAGS += -DDISTRHO_PLUGIN_USES_MODGUI=1 -DDISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE=0xffff | |||
| endif | |||
| ifneq ($(WASM),true) | |||
| ifneq ($(HAIKU),true) | |||
| BASE_FLAGS += -pthread | |||
| endif | |||
| endif | |||
| ifeq ($(WINDOWS),true) | |||
| BASE_FLAGS += -D_USE_MATH_DEFINES | |||
| BASE_FLAGS += -DWIN32_LEAN_AND_MEAN | |||
| BASE_FLAGS += -D_WIN32_WINNT=0x0600 | |||
| BASE_FLAGS += -I../../include/mingw-compat | |||
| BASE_FLAGS += -I../../include/mingw-std-threads | |||
| endif | |||
| ifeq ($(USE_GLES2),true) | |||
| BASE_FLAGS += -DNANOVG_GLES2_FORCED | |||
| else ifeq ($(USE_GLES3),true) | |||
| BASE_FLAGS += -DNANOVG_GLES3_FORCED | |||
| endif | |||
| BUILD_C_FLAGS += -std=gnu11 | |||
| BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing | |||
| BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing | |||
| ifneq ($(MACOS),true) | |||
| BUILD_CXX_FLAGS += -faligned-new -Wno-abi | |||
| ifeq ($(MOD_BUILD),true) | |||
| BUILD_CXX_FLAGS += -std=gnu++17 | |||
| endif | |||
| endif | |||
| # Rack code is not tested for this flag, unset it | |||
| BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS | |||
| # Ignore bad behaviour from Rack API | |||
| BUILD_CXX_FLAGS += -Wno-format-security | |||
| # -------------------------------------------------------------- | |||
| # FIXME lots of warnings from VCV side | |||
| BASE_FLAGS += -Wno-unused-parameter | |||
| BASE_FLAGS += -Wno-unused-variable | |||
| # -------------------------------------------------------------- | |||
| # extra linker flags | |||
| ifeq ($(HAIKU),true) | |||
| ifeq ($(WASM),true) | |||
| ifneq ($(STATIC_BUILD),true) | |||
| LINK_FLAGS += --use-preload-plugins | |||
| LINK_FLAGS += --preload-file=./jsfx | |||
| LINK_FLAGS += --preload-file=./lv2 | |||
| endif | |||
| LINK_FLAGS += --preload-file=../../bin/CardinalNative.lv2/resources@/resources | |||
| LINK_FLAGS += --use-preload-cache | |||
| ifneq ($(NOPLUGINS),true) | |||
| SYMLINKED_DIRS_RESOURCES = | |||
| # find . -type l | grep -v svg | grep -v ttf | grep -v art | grep -v json | grep -v png | grep -v otf | sort | |||
| SYMLINKED_DIRS_RESOURCES += BaconPlugs/res/midi/chopin | |||
| SYMLINKED_DIRS_RESOURCES += BaconPlugs/res/midi/debussy | |||
| SYMLINKED_DIRS_RESOURCES += BaconPlugs/res/midi/goldberg | |||
| SYMLINKED_DIRS_RESOURCES += cf/playeroscs | |||
| SYMLINKED_DIRS_RESOURCES += DrumKit/res/samples | |||
| SYMLINKED_DIRS_RESOURCES += Fundamental/presets | |||
| SYMLINKED_DIRS_RESOURCES += GrandeModular/presets | |||
| SYMLINKED_DIRS_RESOURCES += LyraeModules/presets | |||
| SYMLINKED_DIRS_RESOURCES += Meander/res | |||
| SYMLINKED_DIRS_RESOURCES += MindMeldModular/presets | |||
| SYMLINKED_DIRS_RESOURCES += MindMeldModular/res/ShapeMaster/CommunityPresets | |||
| SYMLINKED_DIRS_RESOURCES += MindMeldModular/res/ShapeMaster/CommunityShapes | |||
| SYMLINKED_DIRS_RESOURCES += MindMeldModular/res/ShapeMaster/MindMeldPresets | |||
| SYMLINKED_DIRS_RESOURCES += MindMeldModular/res/ShapeMaster/MindMeldShapes | |||
| SYMLINKED_DIRS_RESOURCES += Mog/res | |||
| SYMLINKED_DIRS_RESOURCES += nonlinearcircuits/res | |||
| SYMLINKED_DIRS_RESOURCES += Orbits/presets | |||
| SYMLINKED_DIRS_RESOURCES += stoermelder-packone/presets | |||
| SYMLINKED_DIRS_RESOURCES += surgext/build/surge-data/fx_presets | |||
| SYMLINKED_DIRS_RESOURCES += surgext/build/surge-data/wavetables | |||
| SYMLINKED_DIRS_RESOURCES += surgext/patches | |||
| SYMLINKED_DIRS_RESOURCES += surgext/presets | |||
| LINK_FLAGS += $(foreach d,$(SYMLINKED_DIRS_RESOURCES),--preload-file=../../bin/CardinalNative.lv2/resources/$(d)@/resources/$(d)) | |||
| endif | |||
| LINK_FLAGS += -sALLOW_MEMORY_GROWTH | |||
| LINK_FLAGS += -sINITIAL_MEMORY=64Mb | |||
| LINK_FLAGS += -sLZ4=1 | |||
| LINK_FLAGS += --shell-file=../emscripten/shell.html | |||
| LINK_FLAGS += -O3 | |||
| else ifeq ($(HAIKU),true) | |||
| LINK_FLAGS += -lpthread | |||
| else | |||
| LINK_FLAGS += -pthread | |||
| @@ -181,7 +283,7 @@ EXTRA_LIBS += -lws2_32 -lwinmm | |||
| endif | |||
| ifeq ($(SYSDEPS),true) | |||
| EXTRA_LIBS += $(shell pkg-config --libs jansson libarchive samplerate speexdsp) | |||
| EXTRA_LIBS += $(shell $(PKG_CONFIG) --libs jansson libarchive samplerate speexdsp) | |||
| endif | |||
| ifeq ($(WITH_LTO),true) | |||
| @@ -193,6 +295,13 @@ LINK_FLAGS += -Wno-stringop-overflow | |||
| endif | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # Extra flags for liblo | |||
| BASE_FLAGS += -DHAVE_LIBLO | |||
| BASE_FLAGS += $(LIBLO_FLAGS) | |||
| LINK_FLAGS += $(LIBLO_LIBS) | |||
| # -------------------------------------------------------------- | |||
| # fallback path to resource files | |||
| @@ -17,7 +17,7 @@ | |||
| #include "RemoteUI.hpp" | |||
| // #include <asset.hpp> | |||
| #include <asset.hpp> | |||
| // #include <random.hpp> | |||
| #include <patch.hpp> | |||
| #include <settings.hpp> | |||
| @@ -26,66 +26,293 @@ | |||
| #include <app/Scene.hpp> | |||
| #include <engine/Engine.hpp> | |||
| #include "AsyncDialog.hpp" | |||
| #include "WindowParameters.hpp" | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| namespace rack { | |||
| namespace app { | |||
| widget::Widget* createMenuBar(bool isStandalone); | |||
| } | |||
| namespace window { | |||
| void WindowSetPluginRemote(Window* window, NanoTopLevelWidget* tlw); | |||
| void WindowSetMods(Window* window, int mods); | |||
| void WindowSetInternalSize(rack::window::Window* window, math::Vec size); | |||
| } | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| CardinalRemoteUI::CardinalRemoteUI(Window& window, const std::string& templatePath) | |||
| : NanoTopLevelWidget(window), | |||
| context(nullptr) | |||
| : NanoTopLevelWidget(window) | |||
| { | |||
| // create unique temporary path for this instance | |||
| try { | |||
| char uidBuf[24]; | |||
| const std::string tmp = rack::system::getTempDirectory(); | |||
| CardinalPluginContext& context(*static_cast<CardinalPluginContext*>(rack::contextGet())); | |||
| context.nativeWindowId = getWindow().getNativeWindowHandle(); | |||
| context.tlw = this; | |||
| for (int i=1;; ++i) | |||
| { | |||
| std::snprintf(uidBuf, sizeof(uidBuf), "CardinalRemote.%04d", i); | |||
| const std::string trypath = rack::system::join(tmp, uidBuf); | |||
| if (! rack::system::exists(trypath)) | |||
| { | |||
| if (rack::system::createDirectories(trypath)) | |||
| autosavePath = trypath; | |||
| break; | |||
| } | |||
| } | |||
| } DISTRHO_SAFE_EXCEPTION("create unique temporary path"); | |||
| window.setIgnoringKeyRepeat(true); | |||
| context.nativeWindowId = window.getNativeWindowHandle(); | |||
| rack::contextSet(&context); | |||
| const double scaleFactor = getScaleFactor(); | |||
| context.bufferSize = 512; | |||
| rack::settings::sampleRate = context.sampleRate = 48000; | |||
| setGeometryConstraints(648 * scaleFactor, 538 * scaleFactor); | |||
| context.engine = new rack::engine::Engine; | |||
| context.engine->setSampleRate(context.sampleRate); | |||
| if (scaleFactor != 1.0) | |||
| setSize(DISTRHO_UI_DEFAULT_WIDTH * scaleFactor, DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor); | |||
| context.history = new rack::history::State; | |||
| context.patch = new rack::patch::Manager; | |||
| context.patch->autosavePath = autosavePath; | |||
| context.patch->templatePath = templatePath; | |||
| rack::window::WindowSetPluginRemote(context.window, this); | |||
| context.event = new rack::widget::EventState; | |||
| context.scene = new rack::app::Scene; | |||
| context.event->rootWidget = context.scene; | |||
| context.window = new rack::window::Window; | |||
| if (rack::widget::Widget* const menuBar = context.scene->menuBar) | |||
| { | |||
| context.scene->removeChild(menuBar); | |||
| delete menuBar; | |||
| } | |||
| context.patch->loadTemplate(); | |||
| context.scene->rackScroll->reset(); | |||
| context.scene->menuBar = rack::app::createMenuBar(true); | |||
| context.scene->addChildBelow(context.scene->menuBar, context.scene->rackScroll); | |||
| context.nativeWindowId = getWindow().getNativeWindowHandle(); | |||
| // hide "Browse VCV Library" button | |||
| rack::widget::Widget* const browser = context.scene->browser->children.back(); | |||
| rack::widget::Widget* const headerLayout = browser->children.front(); | |||
| rack::widget::Widget* const libraryButton = headerLayout->children.back(); | |||
| libraryButton->hide(); | |||
| // Report to user if something is wrong with the installation | |||
| std::string errorMessage; | |||
| if (rack::asset::systemDir.empty()) | |||
| { | |||
| errorMessage = "Failed to locate Cardinal plugin bundle.\n" | |||
| "Install Cardinal with its plugin bundle folder intact and try again."; | |||
| } | |||
| else if (! rack::system::exists(rack::asset::systemDir)) | |||
| { | |||
| errorMessage = rack::string::f("System directory \"%s\" does not exist. " | |||
| "Make sure Cardinal was downloaded and installed correctly.", | |||
| rack::asset::systemDir.c_str()); | |||
| } | |||
| if (! errorMessage.empty()) | |||
| { | |||
| static bool shown = false; | |||
| if (! shown) | |||
| { | |||
| shown = true; | |||
| asyncDialog::create(errorMessage.c_str()); | |||
| } | |||
| } | |||
| context.window->step(); | |||
| // WindowParametersSetCallback(context.window, this); | |||
| } | |||
| CardinalRemoteUI::~CardinalRemoteUI() | |||
| { | |||
| rack::contextSet(&context); | |||
| CardinalPluginContext& context(*static_cast<CardinalPluginContext*>(rack::contextGet())); | |||
| context.nativeWindowId = 0; | |||
| context.patch->clear(); | |||
| if (! autosavePath.empty()) | |||
| rack::system::removeRecursively(autosavePath); | |||
| } | |||
| void CardinalRemoteUI::onNanoDisplay() | |||
| { | |||
| rack::contextSet(&context); | |||
| CardinalPluginContext& context(*static_cast<CardinalPluginContext*>(rack::contextGet())); | |||
| context.window->step(); | |||
| // TODO | |||
| repaint(); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| static int glfwMods(const uint mod) noexcept | |||
| { | |||
| int mods = 0; | |||
| if (mod & kModifierControl) | |||
| mods |= GLFW_MOD_CONTROL; | |||
| if (mod & kModifierShift) | |||
| mods |= GLFW_MOD_SHIFT; | |||
| if (mod & kModifierAlt) | |||
| mods |= GLFW_MOD_ALT; | |||
| if (mod & kModifierSuper) | |||
| mods |= GLFW_MOD_SUPER; | |||
| /* | |||
| if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) | |||
| mods |= GLFW_MOD_SHIFT; | |||
| if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) | |||
| mods |= GLFW_MOD_CONTROL; | |||
| if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS) | |||
| mods |= GLFW_MOD_ALT; | |||
| if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS) | |||
| mods |= GLFW_MOD_SUPER; | |||
| */ | |||
| return mods; | |||
| } | |||
| bool CardinalRemoteUI::onMouse(const MouseEvent& ev) | |||
| { | |||
| if (ev.press) | |||
| getWindow().focus(); | |||
| const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE; | |||
| int mods = glfwMods(ev.mod); | |||
| int button; | |||
| switch (ev.button) | |||
| { | |||
| case 1: button = GLFW_MOUSE_BUTTON_LEFT; break; | |||
| case 2: button = GLFW_MOUSE_BUTTON_RIGHT; break; | |||
| case 3: button = GLFW_MOUSE_BUTTON_MIDDLE; break; | |||
| default: | |||
| button = ev.button; | |||
| break; | |||
| } | |||
| #ifdef DISTRHO_OS_MAC | |||
| // Remap Ctrl-left click to right click on macOS | |||
| 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 macOS | |||
| 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 | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| return context->event->handleButton(lastMousePos, button, action, mods); | |||
| } | |||
| bool CardinalRemoteUI::onMotion(const MotionEvent& ev) | |||
| { | |||
| const rack::math::Vec mousePos = rack::math::Vec(ev.pos.getX(), ev.pos.getY()).div(getScaleFactor()).round(); | |||
| const rack::math::Vec mouseDelta = mousePos.minus(lastMousePos); | |||
| lastMousePos = mousePos; | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| return context->event->handleHover(mousePos, mouseDelta); | |||
| } | |||
| bool CardinalRemoteUI::onScroll(const ScrollEvent& ev) | |||
| { | |||
| rack::math::Vec scrollDelta = rack::math::Vec(ev.delta.getX(), ev.delta.getY()); | |||
| #ifndef DISTRHO_OS_MAC | |||
| scrollDelta = scrollDelta.mult(50.0); | |||
| #endif | |||
| const int mods = glfwMods(ev.mod); | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| return context->event->handleScroll(lastMousePos, scrollDelta); | |||
| } | |||
| bool CardinalRemoteUI::onCharacterInput(const CharacterInputEvent& ev) | |||
| { | |||
| if (ev.character < ' ' || ev.character >= kKeyDelete) | |||
| return false; | |||
| const int mods = glfwMods(ev.mod); | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| return context->event->handleText(lastMousePos, ev.character); | |||
| } | |||
| bool CardinalRemoteUI::onKeyboard(const KeyboardEvent& ev) | |||
| { | |||
| const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE; | |||
| const int mods = glfwMods(ev.mod); | |||
| /* These are unsupported in pugl right now | |||
| #define GLFW_KEY_KP_0 320 | |||
| #define GLFW_KEY_KP_1 321 | |||
| #define GLFW_KEY_KP_2 322 | |||
| #define GLFW_KEY_KP_3 323 | |||
| #define GLFW_KEY_KP_4 324 | |||
| #define GLFW_KEY_KP_5 325 | |||
| #define GLFW_KEY_KP_6 326 | |||
| #define GLFW_KEY_KP_7 327 | |||
| #define GLFW_KEY_KP_8 328 | |||
| #define GLFW_KEY_KP_9 329 | |||
| #define GLFW_KEY_KP_DECIMAL 330 | |||
| #define GLFW_KEY_KP_DIVIDE 331 | |||
| #define GLFW_KEY_KP_MULTIPLY 332 | |||
| #define GLFW_KEY_KP_SUBTRACT 333 | |||
| #define GLFW_KEY_KP_ADD 334 | |||
| #define GLFW_KEY_KP_ENTER 335 | |||
| #define GLFW_KEY_KP_EQUAL 336 | |||
| */ | |||
| int key; | |||
| switch (ev.key) | |||
| { | |||
| case '\r': key = GLFW_KEY_ENTER; break; | |||
| case '\t': key = GLFW_KEY_TAB; break; | |||
| case kKeyBackspace: key = GLFW_KEY_BACKSPACE; break; | |||
| case kKeyEscape: key = GLFW_KEY_ESCAPE; break; | |||
| case kKeyDelete: key = GLFW_KEY_DELETE; break; | |||
| case kKeyF1: key = GLFW_KEY_F1; break; | |||
| case kKeyF2: key = GLFW_KEY_F2; break; | |||
| case kKeyF3: key = GLFW_KEY_F3; break; | |||
| case kKeyF4: key = GLFW_KEY_F4; break; | |||
| case kKeyF5: key = GLFW_KEY_F5; break; | |||
| case kKeyF6: key = GLFW_KEY_F6; break; | |||
| case kKeyF7: key = GLFW_KEY_F7; break; | |||
| case kKeyF8: key = GLFW_KEY_F8; break; | |||
| case kKeyF9: key = GLFW_KEY_F9; break; | |||
| case kKeyF10: key = GLFW_KEY_F10; break; | |||
| case kKeyF11: key = GLFW_KEY_F11; break; | |||
| case kKeyF12: key = GLFW_KEY_F12; break; | |||
| case kKeyLeft: key = GLFW_KEY_LEFT; break; | |||
| case kKeyUp: key = GLFW_KEY_UP; break; | |||
| case kKeyRight: key = GLFW_KEY_RIGHT; break; | |||
| case kKeyDown: key = GLFW_KEY_DOWN; break; | |||
| case kKeyPageUp: key = GLFW_KEY_PAGE_UP; break; | |||
| case kKeyPageDown: key = GLFW_KEY_PAGE_DOWN; break; | |||
| case kKeyHome: key = GLFW_KEY_HOME; break; | |||
| case kKeyEnd: key = GLFW_KEY_END; break; | |||
| case kKeyInsert: key = GLFW_KEY_INSERT; break; | |||
| case kKeyShiftL: key = GLFW_KEY_LEFT_SHIFT; break; | |||
| case kKeyShiftR: key = GLFW_KEY_RIGHT_SHIFT; break; | |||
| case kKeyControlL: key = GLFW_KEY_LEFT_CONTROL; break; | |||
| case kKeyControlR: key = GLFW_KEY_RIGHT_CONTROL; break; | |||
| case kKeyAltL: key = GLFW_KEY_LEFT_ALT; break; | |||
| case kKeyAltR: key = GLFW_KEY_RIGHT_ALT; break; | |||
| case kKeySuperL: key = GLFW_KEY_LEFT_SUPER; break; | |||
| case kKeySuperR: key = GLFW_KEY_RIGHT_SUPER; break; | |||
| case kKeyMenu: key = GLFW_KEY_MENU; break; | |||
| case kKeyCapsLock: key = GLFW_KEY_CAPS_LOCK; break; | |||
| case kKeyScrollLock: key = GLFW_KEY_SCROLL_LOCK; break; | |||
| case kKeyNumLock: key = GLFW_KEY_NUM_LOCK; break; | |||
| case kKeyPrintScreen: key = GLFW_KEY_PRINT_SCREEN; break; | |||
| case kKeyPause: key = GLFW_KEY_PAUSE; break; | |||
| default: | |||
| // glfw expects uppercase | |||
| if (ev.key >= 'a' && ev.key <= 'z') | |||
| key = ev.key - ('a' - 'A'); | |||
| else | |||
| key = ev.key; | |||
| break; | |||
| } | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| return context->event->handleKey(lastMousePos, key, ev.keycode, action, mods); | |||
| } | |||
| void CardinalRemoteUI::onResize(const ResizeEvent& ev) | |||
| { | |||
| NanoTopLevelWidget::onResize(ev); | |||
| CardinalPluginContext* context = static_cast<CardinalPluginContext*>(rack::contextGet()); | |||
| if (context->window != nullptr) | |||
| WindowSetInternalSize(context->window, rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -20,10 +20,11 @@ | |||
| #include "NanoVG.hpp" | |||
| #include "PluginContext.hpp" | |||
| #include <app/common.hpp> | |||
| class CardinalRemoteUI : public NanoTopLevelWidget | |||
| { | |||
| CardinalPluginContext context; | |||
| std::string autosavePath; | |||
| rack::math::Vec lastMousePos; | |||
| public: | |||
| explicit CardinalRemoteUI(Window& window, const std::string& templatePath); | |||
| @@ -31,6 +32,12 @@ public: | |||
| protected: | |||
| void onNanoDisplay() override; | |||
| bool onMouse(const MouseEvent& ev) override; | |||
| bool onMotion(const MotionEvent& ev) override; | |||
| bool onScroll(const ScrollEvent& ev) override; | |||
| bool onCharacterInput(const CharacterInputEvent& ev) override; | |||
| bool onKeyboard(const KeyboardEvent& ev) override; | |||
| void onResize(const ResizeEvent& ev) override; | |||
| DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalRemoteUI) | |||
| }; | |||
| @@ -20,13 +20,19 @@ | |||
| #include "RemoteUI.hpp" | |||
| #include <asset.hpp> | |||
| #include <patch.hpp> | |||
| #include <random.hpp> | |||
| #include <settings.hpp> | |||
| #include <system.hpp> | |||
| #include <app/Browser.hpp> | |||
| #include <app/Scene.hpp> | |||
| #include <engine/Engine.hpp> | |||
| #include <ui/common.hpp> | |||
| #include "PluginContext.hpp" | |||
| namespace rack { | |||
| namespace plugin { | |||
| void initStaticPlugins(); | |||
| @@ -34,6 +40,26 @@ namespace plugin { | |||
| } | |||
| } | |||
| START_NAMESPACE_DISTRHO | |||
| bool isUsingNativeAudio() noexcept { return false; } | |||
| bool supportsAudioInput() { return false; } | |||
| bool supportsBufferSizeChanges() { return false; } | |||
| bool supportsMIDI() { return false; } | |||
| bool isAudioInputEnabled() { return false; } | |||
| bool isMIDIEnabled() { return false; } | |||
| uint getBufferSize() { return 0; } | |||
| bool requestAudioInput() { return false; } | |||
| bool requestBufferSizeChange(uint) { return false; } | |||
| bool requestMIDI() { return false; } | |||
| const char* getPluginFormatName() noexcept { return "Remote"; } | |||
| uint32_t Plugin::getBufferSize() const noexcept { return 128; } | |||
| double Plugin::getSampleRate() const noexcept { return 48000; } | |||
| bool Plugin::writeMidiEvent(const MidiEvent&) noexcept { return false; } | |||
| END_NAMESPACE_DISTRHO | |||
| int main(const int argc, const char* argv[]) | |||
| { | |||
| using namespace rack; | |||
| @@ -42,7 +68,6 @@ int main(const int argc, const char* argv[]) | |||
| settings::autoCheckUpdates = false; | |||
| settings::autosaveInterval = 0; | |||
| settings::devMode = true; | |||
| settings::discordUpdateActivity = false; | |||
| settings::isPlugin = true; | |||
| settings::skipLoadOnLaunch = true; | |||
| settings::showTipsOnLaunch = false; | |||
| @@ -131,17 +156,68 @@ int main(const int argc, const char* argv[]) | |||
| INFO("Initializing plugin browser DB"); | |||
| app::browserInit(); | |||
| // create unique temporary path for this instance | |||
| std::string autosavePath; | |||
| try { | |||
| char uidBuf[24]; | |||
| const std::string tmp = rack::system::getTempDirectory(); | |||
| for (int i=1;; ++i) | |||
| { | |||
| std::snprintf(uidBuf, sizeof(uidBuf), "CardinalRemote.%04d", i); | |||
| const std::string trypath = rack::system::join(tmp, uidBuf); | |||
| if (! rack::system::exists(trypath)) | |||
| { | |||
| if (rack::system::createDirectories(trypath)) | |||
| autosavePath = trypath; | |||
| break; | |||
| } | |||
| } | |||
| } DISTRHO_SAFE_EXCEPTION("create unique temporary path"); | |||
| CardinalPluginContext context(nullptr); | |||
| rack::contextSet(&context); | |||
| context.bufferSize = 512; | |||
| rack::settings::sampleRate = context.sampleRate = 48000; | |||
| context.engine = new rack::engine::Engine; | |||
| context.engine->setSampleRate(context.sampleRate); | |||
| context.history = new rack::history::State; | |||
| context.patch = new rack::patch::Manager; | |||
| context.patch->autosavePath = autosavePath; | |||
| context.patch->templatePath = templatePath; | |||
| 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(); | |||
| Application app; | |||
| Window win(app); | |||
| win.setResizable(true); | |||
| win.setTitle("CardinalRemote"); | |||
| ScopedPointer<CardinalRemoteUI> remoteUI; | |||
| { | |||
| const Window::ScopedGraphicsContext sgc(win); | |||
| remoteUI = new CardinalRemoteUI(win, templatePath); | |||
| } | |||
| win.show(); | |||
| app.exec(); | |||
| context.patch->clear(); | |||
| if (! autosavePath.empty()) | |||
| rack::system::removeRecursively(autosavePath); | |||
| INFO("Clearing asset paths"); | |||
| asset::bundlePath.clear(); | |||
| asset::systemDir.clear(); | |||
| @@ -67,40 +67,6 @@ START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| bool CardinalPluginContext::addIdleCallback(IdleCallback* const cb) const | |||
| { | |||
| if (ui == nullptr) | |||
| return false; | |||
| ui->addIdleCallback(cb); | |||
| return true; | |||
| } | |||
| void CardinalPluginContext::removeIdleCallback(IdleCallback* const cb) const | |||
| { | |||
| if (ui == nullptr) | |||
| return; | |||
| ui->removeIdleCallback(cb); | |||
| } | |||
| void handleHostParameterDrag(const CardinalPluginContext* pcontext, uint index, bool started) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(pcontext->ui != nullptr,); | |||
| if (started) | |||
| { | |||
| pcontext->ui->editParameter(index, true); | |||
| pcontext->ui->setParameterValue(index, pcontext->parameters[index]); | |||
| } | |||
| else | |||
| { | |||
| pcontext->ui->editParameter(index, false); | |||
| } | |||
| } | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| #ifdef DISTRHO_OS_WASM | |||
| struct WasmWelcomeDialog : rack::widget::OpaqueWidget | |||
| { | |||
| @@ -64,13 +64,14 @@ struct CardinalPluginContext : rack::Context { | |||
| uint32_t midiEventCount; | |||
| Plugin* const plugin; | |||
| #ifndef HEADLESS | |||
| NanoTopLevelWidget* tlw; | |||
| UI* ui; | |||
| #endif | |||
| CardinalPluginContext(Plugin* const p) | |||
| : bufferSize(p->getBufferSize()), | |||
| : bufferSize(p != nullptr ? p->getBufferSize() : 0), | |||
| processCounter(0), | |||
| sampleRate(p->getSampleRate()), | |||
| sampleRate(p != nullptr ? p->getSampleRate() : 0.0), | |||
| #if CARDINAL_VARIANT_MAIN | |||
| variant(kCardinalVariantMain), | |||
| #elif CARDINAL_VARIANT_FX | |||
| @@ -105,6 +106,7 @@ struct CardinalPluginContext : rack::Context { | |||
| midiEventCount(0), | |||
| plugin(p) | |||
| #ifndef HEADLESS | |||
| , tlw(nullptr) | |||
| , ui(nullptr) | |||
| #endif | |||
| { | |||
| @@ -169,6 +171,7 @@ public: | |||
| filebrowseraction(), | |||
| filebrowserhandle(nullptr) | |||
| { | |||
| context->tlw = this; | |||
| context->ui = this; | |||
| } | |||
| @@ -177,6 +180,7 @@ public: | |||
| if (filebrowserhandle != nullptr) | |||
| fileBrowserClose(filebrowserhandle); | |||
| context->tlw = nullptr; | |||
| context->ui = nullptr; | |||
| } | |||
| }; | |||
| @@ -30,10 +30,10 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow*) | |||
| { | |||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->tlw != nullptr, nullptr); | |||
| size_t dataSize; | |||
| return static_cast<const char*>(context->ui->getClipboard(dataSize)); | |||
| return static_cast<const char*>(context->tlw->getClipboard(dataSize)); | |||
| } | |||
| GLFWAPI void glfwSetClipboardString(GLFWwindow*, const char* const text) | |||
| @@ -42,9 +42,9 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow*, const char* const text) | |||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->tlw != nullptr,); | |||
| context->ui->setClipboard(nullptr, text, std::strlen(text)+1); | |||
| context->tlw->setClipboard(nullptr, text, std::strlen(text)+1); | |||
| } | |||
| GLFWAPI GLFWcursor* glfwCreateStandardCursor(const int shape) | |||
| @@ -91,18 +91,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow*, GLFWcursor* const cursor) | |||
| { | |||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->tlw != nullptr,); | |||
| context->ui->setCursor(cursor != nullptr ? cursor->cursorId : kMouseCursorArrow); | |||
| context->tlw->setCursor(cursor != nullptr ? cursor->cursorId : kMouseCursorArrow); | |||
| } | |||
| GLFWAPI double glfwGetTime(void) | |||
| { | |||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, 0.0); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, 0.0); | |||
| DISTRHO_SAFE_ASSERT_RETURN(context->tlw != nullptr, 0.0); | |||
| return context->ui->getApp().getTime(); | |||
| return context->tlw->getApp().getTime(); | |||
| } | |||
| GLFWAPI const char* glfwGetKeyName(const int key, int) | |||
| @@ -145,6 +145,7 @@ struct Window::Internal { | |||
| std::string lastWindowTitle; | |||
| DISTRHO_NAMESPACE::UI* ui = nullptr; | |||
| DGL_NAMESPACE::NanoTopLevelWidget* tlw = nullptr; | |||
| DISTRHO_NAMESPACE::WindowParameters params; | |||
| DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr; | |||
| DGL_NAMESPACE::Application hiddenApp; | |||
| @@ -238,6 +239,112 @@ Window::Window() { | |||
| #endif | |||
| } | |||
| void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw) | |||
| { | |||
| // if nanovg context failed, init only bare minimum | |||
| if (window->vg == nullptr) | |||
| { | |||
| if (tlw != nullptr) | |||
| { | |||
| window->internal->tlw = tlw; | |||
| window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight()); | |||
| } | |||
| else | |||
| { | |||
| window->internal->tlw = nullptr; | |||
| window->internal->callback = nullptr; | |||
| } | |||
| return; | |||
| } | |||
| if (tlw != 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->tlw = tlw; | |||
| window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight()); | |||
| // Set up NanoVG | |||
| window->internal->r_vg = tlw->getContext(); | |||
| #ifdef NANOVG_GLES2 | |||
| window->internal->r_fbVg = nvgCreateSharedGLES2(window->internal->r_vg, NVG_ANTIALIAS); | |||
| #else | |||
| window->internal->r_fbVg = nvgCreateSharedGL2(window->internal->r_vg, NVG_ANTIALIAS); | |||
| #endif | |||
| // 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; | |||
| } | |||
| #if defined NANOVG_GLES2 | |||
| nvgDeleteGLES2(window->internal->r_fbVg); | |||
| #else | |||
| nvgDeleteGL2(window->internal->r_fbVg); | |||
| #endif | |||
| window->internal->tlw = nullptr; | |||
| window->internal->callback = nullptr; | |||
| } | |||
| } | |||
| void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) | |||
| { | |||
| // if nanovg context failed, init only bare minimum | |||
| @@ -264,6 +371,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) | |||
| INFO("Renderer: %s %s", vendor, renderer); | |||
| INFO("OpenGL: %s", version); | |||
| window->internal->tlw = ui; | |||
| window->internal->ui = ui; | |||
| window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight()); | |||
| @@ -339,6 +447,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) | |||
| nvgDeleteGL2(window->internal->r_fbVg); | |||
| #endif | |||
| window->internal->tlw = nullptr; | |||
| window->internal->ui = nullptr; | |||
| window->internal->callback = nullptr; | |||
| } | |||
| @@ -384,8 +493,8 @@ void Window::setSize(math::Vec size) { | |||
| size = size.max(WINDOW_SIZE_MIN); | |||
| internal->size = size; | |||
| if (DISTRHO_NAMESPACE::UI* const ui = internal->ui) | |||
| ui->setSize(internal->size.x, internal->size.y); | |||
| if (DGL_NAMESPACE::NanoTopLevelWidget* const tlw = internal->ui) | |||
| tlw->setSize(internal->size.x, internal->size.y); | |||
| } | |||
| void WindowSetInternalSize(rack::window::Window* const window, math::Vec size) { | |||
| @@ -453,7 +562,7 @@ static void Window__writeImagePNG(void* context, void* data, int size) { | |||
| void Window::step() { | |||
| DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(internal->tlw != nullptr,); | |||
| if (vg == nullptr) | |||
| return; | |||
| @@ -480,12 +589,12 @@ void Window::step() { | |||
| windowTitle += system::getFilename(APP->patch->path); | |||
| } | |||
| if (windowTitle != internal->lastWindowTitle) { | |||
| internal->ui->getWindow().setTitle(windowTitle.c_str()); | |||
| internal->tlw->getWindow().setTitle(windowTitle.c_str()); | |||
| internal->lastWindowTitle = windowTitle; | |||
| } | |||
| // Get desired pixel ratio | |||
| float newPixelRatio = internal->ui->getScaleFactor(); | |||
| float newPixelRatio = internal->tlw->getScaleFactor(); | |||
| if (newPixelRatio != pixelRatio) { | |||
| pixelRatio = newPixelRatio; | |||
| APP->event->handleDirty(); | |||
| @@ -504,8 +613,8 @@ void Window::step() { | |||
| #endif | |||
| // Get framebuffer/window ratio | |||
| int winWidth = internal->ui->getWidth(); | |||
| int winHeight = internal->ui->getHeight(); | |||
| int winWidth = internal->tlw->getWidth(); | |||
| int winHeight = internal->tlw->getHeight(); | |||
| int fbWidth = winWidth;// * newPixelRatio; | |||
| int fbHeight = winHeight;// * newPixelRatio; | |||
| windowRatio = (float)fbWidth / winWidth; | |||
| @@ -599,9 +708,9 @@ void Window::screenshotModules(const std::string&, float) { | |||
| void Window::close() { | |||
| DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,); | |||
| DISTRHO_SAFE_ASSERT_RETURN(internal->tlw != nullptr,); | |||
| internal->ui->getWindow().close(); | |||
| internal->tlw->getWindow().close(); | |||
| } | |||
| @@ -610,7 +719,7 @@ void Window::cursorLock() { | |||
| if (!settings::allowCursorLock) | |||
| return; | |||
| emscripten_request_pointerlock(internal->ui->getWindow().getApp().getClassName(), false); | |||
| emscripten_request_pointerlock(internal->tlw->getWindow().getApp().getClassName(), false); | |||
| #endif | |||
| } | |||
| @@ -643,7 +752,7 @@ int Window::getMods() { | |||
| void Window::setFullScreen(const bool fullscreen) { | |||
| #ifdef DISTRHO_OS_WASM | |||
| if (fullscreen) | |||
| emscripten_request_fullscreen(internal->ui->getWindow().getApp().getClassName(), false); | |||
| emscripten_request_fullscreen(internal->tlw->getWindow().getApp().getClassName(), false); | |||
| else | |||
| emscripten_exit_fullscreen(); | |||
| #endif | |||