Signed-off-by: falkTX <falktx@falktx.com>tags/23.02
| @@ -29,6 +29,12 @@ | |||||
| // ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
| // from PluginContext.hpp | // from PluginContext.hpp | ||||
| #ifndef HEADLESS | |||||
| START_NAMESPACE_DGL | |||||
| class NanoTopLevelWidget; | |||||
| END_NAMESPACE_DGL | |||||
| #endif | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| static constexpr const uint32_t kModuleParameters = 24; | static constexpr const uint32_t kModuleParameters = 24; | ||||
| @@ -68,6 +74,7 @@ struct CardinalPluginContext : rack::Context { | |||||
| uint32_t midiEventCount; | uint32_t midiEventCount; | ||||
| Plugin* const plugin; | Plugin* const plugin; | ||||
| #ifndef HEADLESS | #ifndef HEADLESS | ||||
| DGL_NAMESPACE::NanoTopLevelWidget* tlw; | |||||
| UI* ui; | UI* ui; | ||||
| #endif | #endif | ||||
| CardinalPluginContext(Plugin* const p); | CardinalPluginContext(Plugin* const p); | ||||
| @@ -59,6 +59,115 @@ | |||||
| const std::string CARDINAL_VERSION = "22.12"; | 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 { | namespace rack { | ||||
| bool isStandalone() | bool isStandalone() | ||||
| @@ -118,6 +227,8 @@ std::string homeDir() | |||||
| } // namespace rack | } // namespace rack | ||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| namespace patchUtils | namespace patchUtils | ||||
| { | { | ||||
| @@ -289,6 +400,8 @@ void openBrowser(const std::string& url) | |||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| void async_dialog_filebrowser(const bool saving, | void async_dialog_filebrowser(const bool saving, | ||||
| const char* const defaultName, | const char* const defaultName, | ||||
| const char* const startDir, | 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 { | struct ScopedContext { | ||||
| ScopedContext(const CardinalBasePlugin* const plugin) | ScopedContext(const CardinalBasePlugin* const plugin) | ||||
| { | { | ||||
| @@ -1393,6 +1324,6 @@ Plugin* createPlugin() | |||||
| return new CardinalPlugin(); | return new CardinalPlugin(); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
| @@ -7,8 +7,11 @@ | |||||
| # -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
| # Carla stuff | # Carla stuff | ||||
| CWD = ../../carla/source | |||||
| ifneq ($(STATIC_BUILD),true) | |||||
| STATIC_PLUGIN_TARGET = true | STATIC_PLUGIN_TARGET = true | ||||
| CWD = ../../carla/source | |||||
| include $(CWD)/Makefile.deps.mk | include $(CWD)/Makefile.deps.mk | ||||
| CARLA_BUILD_DIR = ../../carla/build | 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)/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)/native-plugins.a | ||||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/audio_decoder.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 | 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)/lilv.a | ||||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/rtmempool.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 | 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)/ysfx.a | ||||
| CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/zita-resampler.a | CARLA_EXTRA_LIBS += $(CARLA_BUILD_DIR)/modules/$(CARLA_BUILD_TYPE)/zita-resampler.a | ||||
| endif # STATIC_BUILD | |||||
| # -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
| # Import base definitions | # 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 | USE_NANOVG_FBO = true | ||||
| WASM_EXCEPTIONS = true | |||||
| include ../../dpf/Makefile.base.mk | include ../../dpf/Makefile.base.mk | ||||
| # -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
| @@ -57,7 +69,11 @@ endif | |||||
| # -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
| # Extra libraries to link against | # Extra libraries to link against | ||||
| ifeq ($(NOPLUGINS),true) | |||||
| RACK_EXTRA_LIBS = ../../plugins/noplugins.a | |||||
| else | |||||
| RACK_EXTRA_LIBS = ../../plugins/plugins.a | RACK_EXTRA_LIBS = ../../plugins/plugins.a | ||||
| endif | |||||
| RACK_EXTRA_LIBS += ../rack.a | RACK_EXTRA_LIBS += ../rack.a | ||||
| RACK_EXTRA_LIBS += $(DEP_LIB_PATH)/libquickjs.a | RACK_EXTRA_LIBS += $(DEP_LIB_PATH)/libquickjs.a | ||||
| @@ -74,22 +90,51 @@ RACK_EXTRA_LIBS += $(DEP_LIB_PATH)/libzstd.a | |||||
| endif | 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_DEPENDENCIES = $(RACK_EXTRA_LIBS) $(CARLA_EXTRA_LIBS) | ||||
| EXTRA_LIBS = $(RACK_EXTRA_LIBS) $(CARLA_EXTRA_LIBS) $(STATIC_CARLA_PLUGIN_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_DEPENDENCIES += ../../deps/aubio/libaubio.a | ||||
| EXTRA_LIBS += ../../deps/aubio/libaubio.a | EXTRA_LIBS += ../../deps/aubio/libaubio.a | ||||
| EXTRA_LIBS += $(shell $(PKG_CONFIG) --libs fftw3f) | EXTRA_LIBS += $(shell $(PKG_CONFIG) --libs fftw3f) | ||||
| endif | 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 | # Extra flags for VCV stuff | ||||
| @@ -106,11 +151,11 @@ BASE_FLAGS += -DPRIVATE= | |||||
| BASE_FLAGS += -I.. | BASE_FLAGS += -I.. | ||||
| BASE_FLAGS += -I../../dpf/dgl/src/nanovg | BASE_FLAGS += -I../../dpf/dgl/src/nanovg | ||||
| BASE_FLAGS += -I../../include | BASE_FLAGS += -I../../include | ||||
| BASE_FLAGS += -I../../include/neon-compat | |||||
| BASE_FLAGS += -I../../include/simd-compat | |||||
| BASE_FLAGS += -I../Rack/include | BASE_FLAGS += -I../Rack/include | ||||
| ifeq ($(SYSDEPS),true) | ifeq ($(SYSDEPS),true) | ||||
| BASE_FLAGS += -DCARDINAL_SYSDEPS | 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 | else | ||||
| BASE_FLAGS += -DZSTDLIB_VISIBILITY= | BASE_FLAGS += -DZSTDLIB_VISIBILITY= | ||||
| BASE_FLAGS += -I../Rack/dep/include | 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/nanosvg/src | ||||
| BASE_FLAGS += -I../Rack/dep/oui-blendish | 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 | BASE_FLAGS += -pthread | ||||
| endif | endif | ||||
| endif | |||||
| ifeq ($(WINDOWS),true) | ifeq ($(WINDOWS),true) | ||||
| BASE_FLAGS += -D_USE_MATH_DEFINES | BASE_FLAGS += -D_USE_MATH_DEFINES | ||||
| BASE_FLAGS += -DWIN32_LEAN_AND_MEAN | BASE_FLAGS += -DWIN32_LEAN_AND_MEAN | ||||
| BASE_FLAGS += -D_WIN32_WINNT=0x0600 | |||||
| BASE_FLAGS += -I../../include/mingw-compat | BASE_FLAGS += -I../../include/mingw-compat | ||||
| BASE_FLAGS += -I../../include/mingw-std-threads | BASE_FLAGS += -I../../include/mingw-std-threads | ||||
| endif | 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 += -std=gnu11 | ||||
| BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing | BUILD_C_FLAGS += -fno-finite-math-only -fno-strict-aliasing | ||||
| BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing | BUILD_CXX_FLAGS += -fno-finite-math-only -fno-strict-aliasing | ||||
| ifneq ($(MACOS),true) | ifneq ($(MACOS),true) | ||||
| BUILD_CXX_FLAGS += -faligned-new -Wno-abi | BUILD_CXX_FLAGS += -faligned-new -Wno-abi | ||||
| ifeq ($(MOD_BUILD),true) | |||||
| BUILD_CXX_FLAGS += -std=gnu++17 | |||||
| endif | |||||
| endif | endif | ||||
| # Rack code is not tested for this flag, unset it | # Rack code is not tested for this flag, unset it | ||||
| BUILD_CXX_FLAGS += -U_GLIBCXX_ASSERTIONS -Wp,-U_GLIBCXX_ASSERTIONS | 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 | # FIXME lots of warnings from VCV side | ||||
| BASE_FLAGS += -Wno-unused-parameter | BASE_FLAGS += -Wno-unused-parameter | ||||
| BASE_FLAGS += -Wno-unused-variable | 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 | LINK_FLAGS += -lpthread | ||||
| else | else | ||||
| LINK_FLAGS += -pthread | LINK_FLAGS += -pthread | ||||
| @@ -181,7 +283,7 @@ EXTRA_LIBS += -lws2_32 -lwinmm | |||||
| endif | endif | ||||
| ifeq ($(SYSDEPS),true) | ifeq ($(SYSDEPS),true) | ||||
| EXTRA_LIBS += $(shell pkg-config --libs jansson libarchive samplerate speexdsp) | |||||
| EXTRA_LIBS += $(shell $(PKG_CONFIG) --libs jansson libarchive samplerate speexdsp) | |||||
| endif | endif | ||||
| ifeq ($(WITH_LTO),true) | ifeq ($(WITH_LTO),true) | ||||
| @@ -193,6 +295,13 @@ LINK_FLAGS += -Wno-stringop-overflow | |||||
| endif | endif | ||||
| endif | endif | ||||
| # -------------------------------------------------------------- | |||||
| # Extra flags for liblo | |||||
| BASE_FLAGS += -DHAVE_LIBLO | |||||
| BASE_FLAGS += $(LIBLO_FLAGS) | |||||
| LINK_FLAGS += $(LIBLO_LIBS) | |||||
| # -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
| # fallback path to resource files | # fallback path to resource files | ||||
| @@ -17,7 +17,7 @@ | |||||
| #include "RemoteUI.hpp" | #include "RemoteUI.hpp" | ||||
| // #include <asset.hpp> | |||||
| #include <asset.hpp> | |||||
| // #include <random.hpp> | // #include <random.hpp> | ||||
| #include <patch.hpp> | #include <patch.hpp> | ||||
| #include <settings.hpp> | #include <settings.hpp> | ||||
| @@ -26,66 +26,293 @@ | |||||
| #include <app/Scene.hpp> | #include <app/Scene.hpp> | ||||
| #include <engine/Engine.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) | 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() | CardinalRemoteUI::~CardinalRemoteUI() | ||||
| { | { | ||||
| rack::contextSet(&context); | |||||
| CardinalPluginContext& context(*static_cast<CardinalPluginContext*>(rack::contextGet())); | |||||
| context.nativeWindowId = 0; | context.nativeWindowId = 0; | ||||
| context.patch->clear(); | |||||
| if (! autosavePath.empty()) | |||||
| rack::system::removeRecursively(autosavePath); | |||||
| } | } | ||||
| void CardinalRemoteUI::onNanoDisplay() | void CardinalRemoteUI::onNanoDisplay() | ||||
| { | { | ||||
| rack::contextSet(&context); | |||||
| CardinalPluginContext& context(*static_cast<CardinalPluginContext*>(rack::contextGet())); | |||||
| context.window->step(); | 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 "NanoVG.hpp" | ||||
| #include "PluginContext.hpp" | #include "PluginContext.hpp" | ||||
| #include <app/common.hpp> | |||||
| class CardinalRemoteUI : public NanoTopLevelWidget | class CardinalRemoteUI : public NanoTopLevelWidget | ||||
| { | { | ||||
| CardinalPluginContext context; | |||||
| std::string autosavePath; | |||||
| rack::math::Vec lastMousePos; | |||||
| public: | public: | ||||
| explicit CardinalRemoteUI(Window& window, const std::string& templatePath); | explicit CardinalRemoteUI(Window& window, const std::string& templatePath); | ||||
| @@ -31,6 +32,12 @@ public: | |||||
| protected: | protected: | ||||
| void onNanoDisplay() override; | 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) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalRemoteUI) | ||||
| }; | }; | ||||
| @@ -20,13 +20,19 @@ | |||||
| #include "RemoteUI.hpp" | #include "RemoteUI.hpp" | ||||
| #include <asset.hpp> | #include <asset.hpp> | ||||
| #include <patch.hpp> | |||||
| #include <random.hpp> | #include <random.hpp> | ||||
| #include <settings.hpp> | #include <settings.hpp> | ||||
| #include <system.hpp> | #include <system.hpp> | ||||
| #include <app/Browser.hpp> | #include <app/Browser.hpp> | ||||
| #include <app/Scene.hpp> | |||||
| #include <engine/Engine.hpp> | |||||
| #include <ui/common.hpp> | #include <ui/common.hpp> | ||||
| #include "PluginContext.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| namespace plugin { | namespace plugin { | ||||
| void initStaticPlugins(); | 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[]) | int main(const int argc, const char* argv[]) | ||||
| { | { | ||||
| using namespace rack; | using namespace rack; | ||||
| @@ -42,7 +68,6 @@ int main(const int argc, const char* argv[]) | |||||
| settings::autoCheckUpdates = false; | settings::autoCheckUpdates = false; | ||||
| settings::autosaveInterval = 0; | settings::autosaveInterval = 0; | ||||
| settings::devMode = true; | settings::devMode = true; | ||||
| settings::discordUpdateActivity = false; | |||||
| settings::isPlugin = true; | settings::isPlugin = true; | ||||
| settings::skipLoadOnLaunch = true; | settings::skipLoadOnLaunch = true; | ||||
| settings::showTipsOnLaunch = false; | settings::showTipsOnLaunch = false; | ||||
| @@ -131,17 +156,68 @@ int main(const int argc, const char* argv[]) | |||||
| INFO("Initializing plugin browser DB"); | INFO("Initializing plugin browser DB"); | ||||
| app::browserInit(); | 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; | Application app; | ||||
| Window win(app); | Window win(app); | ||||
| win.setResizable(true); | |||||
| win.setTitle("CardinalRemote"); | win.setTitle("CardinalRemote"); | ||||
| ScopedPointer<CardinalRemoteUI> remoteUI; | ScopedPointer<CardinalRemoteUI> remoteUI; | ||||
| { | { | ||||
| const Window::ScopedGraphicsContext sgc(win); | |||||
| remoteUI = new CardinalRemoteUI(win, templatePath); | remoteUI = new CardinalRemoteUI(win, templatePath); | ||||
| } | } | ||||
| win.show(); | |||||
| app.exec(); | app.exec(); | ||||
| context.patch->clear(); | |||||
| if (! autosavePath.empty()) | |||||
| rack::system::removeRecursively(autosavePath); | |||||
| INFO("Clearing asset paths"); | INFO("Clearing asset paths"); | ||||
| asset::bundlePath.clear(); | asset::bundlePath.clear(); | ||||
| asset::systemDir.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 | #ifdef DISTRHO_OS_WASM | ||||
| struct WasmWelcomeDialog : rack::widget::OpaqueWidget | struct WasmWelcomeDialog : rack::widget::OpaqueWidget | ||||
| { | { | ||||
| @@ -64,13 +64,14 @@ struct CardinalPluginContext : rack::Context { | |||||
| uint32_t midiEventCount; | uint32_t midiEventCount; | ||||
| Plugin* const plugin; | Plugin* const plugin; | ||||
| #ifndef HEADLESS | #ifndef HEADLESS | ||||
| NanoTopLevelWidget* tlw; | |||||
| UI* ui; | UI* ui; | ||||
| #endif | #endif | ||||
| CardinalPluginContext(Plugin* const p) | CardinalPluginContext(Plugin* const p) | ||||
| : bufferSize(p->getBufferSize()), | |||||
| : bufferSize(p != nullptr ? p->getBufferSize() : 0), | |||||
| processCounter(0), | processCounter(0), | ||||
| sampleRate(p->getSampleRate()), | |||||
| sampleRate(p != nullptr ? p->getSampleRate() : 0.0), | |||||
| #if CARDINAL_VARIANT_MAIN | #if CARDINAL_VARIANT_MAIN | ||||
| variant(kCardinalVariantMain), | variant(kCardinalVariantMain), | ||||
| #elif CARDINAL_VARIANT_FX | #elif CARDINAL_VARIANT_FX | ||||
| @@ -105,6 +106,7 @@ struct CardinalPluginContext : rack::Context { | |||||
| midiEventCount(0), | midiEventCount(0), | ||||
| plugin(p) | plugin(p) | ||||
| #ifndef HEADLESS | #ifndef HEADLESS | ||||
| , tlw(nullptr) | |||||
| , ui(nullptr) | , ui(nullptr) | ||||
| #endif | #endif | ||||
| { | { | ||||
| @@ -169,6 +171,7 @@ public: | |||||
| filebrowseraction(), | filebrowseraction(), | ||||
| filebrowserhandle(nullptr) | filebrowserhandle(nullptr) | ||||
| { | { | ||||
| context->tlw = this; | |||||
| context->ui = this; | context->ui = this; | ||||
| } | } | ||||
| @@ -177,6 +180,7 @@ public: | |||||
| if (filebrowserhandle != nullptr) | if (filebrowserhandle != nullptr) | ||||
| fileBrowserClose(filebrowserhandle); | fileBrowserClose(filebrowserhandle); | ||||
| context->tlw = nullptr; | |||||
| context->ui = nullptr; | context->ui = nullptr; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -30,10 +30,10 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow*) | |||||
| { | { | ||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr); | 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; | 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) | 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); | CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,); | 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) | GLFWAPI GLFWcursor* glfwCreateStandardCursor(const int shape) | ||||
| @@ -91,18 +91,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow*, GLFWcursor* const cursor) | |||||
| { | { | ||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,); | 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) | GLFWAPI double glfwGetTime(void) | ||||
| { | { | ||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | ||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, 0.0); | 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) | GLFWAPI const char* glfwGetKeyName(const int key, int) | ||||
| @@ -145,6 +145,7 @@ struct Window::Internal { | |||||
| std::string lastWindowTitle; | std::string lastWindowTitle; | ||||
| DISTRHO_NAMESPACE::UI* ui = nullptr; | DISTRHO_NAMESPACE::UI* ui = nullptr; | ||||
| DGL_NAMESPACE::NanoTopLevelWidget* tlw = nullptr; | |||||
| DISTRHO_NAMESPACE::WindowParameters params; | DISTRHO_NAMESPACE::WindowParameters params; | ||||
| DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr; | DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr; | ||||
| DGL_NAMESPACE::Application hiddenApp; | DGL_NAMESPACE::Application hiddenApp; | ||||
| @@ -238,6 +239,112 @@ Window::Window() { | |||||
| #endif | #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) | void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui) | ||||
| { | { | ||||
| // if nanovg context failed, init only bare minimum | // 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("Renderer: %s %s", vendor, renderer); | ||||
| INFO("OpenGL: %s", version); | INFO("OpenGL: %s", version); | ||||
| window->internal->tlw = ui; | |||||
| window->internal->ui = ui; | window->internal->ui = ui; | ||||
| window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight()); | 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); | nvgDeleteGL2(window->internal->r_fbVg); | ||||
| #endif | #endif | ||||
| window->internal->tlw = nullptr; | |||||
| window->internal->ui = nullptr; | window->internal->ui = nullptr; | ||||
| window->internal->callback = nullptr; | window->internal->callback = nullptr; | ||||
| } | } | ||||
| @@ -384,8 +493,8 @@ void Window::setSize(math::Vec size) { | |||||
| size = size.max(WINDOW_SIZE_MIN); | size = size.max(WINDOW_SIZE_MIN); | ||||
| internal->size = size; | 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) { | 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() { | void Window::step() { | ||||
| DISTRHO_SAFE_ASSERT_RETURN(internal->ui != nullptr,); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(internal->tlw != nullptr,); | |||||
| if (vg == nullptr) | if (vg == nullptr) | ||||
| return; | return; | ||||
| @@ -480,12 +589,12 @@ void Window::step() { | |||||
| windowTitle += system::getFilename(APP->patch->path); | windowTitle += system::getFilename(APP->patch->path); | ||||
| } | } | ||||
| if (windowTitle != internal->lastWindowTitle) { | if (windowTitle != internal->lastWindowTitle) { | ||||
| internal->ui->getWindow().setTitle(windowTitle.c_str()); | |||||
| internal->tlw->getWindow().setTitle(windowTitle.c_str()); | |||||
| internal->lastWindowTitle = windowTitle; | internal->lastWindowTitle = windowTitle; | ||||
| } | } | ||||
| // Get desired pixel ratio | // Get desired pixel ratio | ||||
| float newPixelRatio = internal->ui->getScaleFactor(); | |||||
| float newPixelRatio = internal->tlw->getScaleFactor(); | |||||
| if (newPixelRatio != pixelRatio) { | if (newPixelRatio != pixelRatio) { | ||||
| pixelRatio = newPixelRatio; | pixelRatio = newPixelRatio; | ||||
| APP->event->handleDirty(); | APP->event->handleDirty(); | ||||
| @@ -504,8 +613,8 @@ void Window::step() { | |||||
| #endif | #endif | ||||
| // Get framebuffer/window ratio | // 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 fbWidth = winWidth;// * newPixelRatio; | ||||
| int fbHeight = winHeight;// * newPixelRatio; | int fbHeight = winHeight;// * newPixelRatio; | ||||
| windowRatio = (float)fbWidth / winWidth; | windowRatio = (float)fbWidth / winWidth; | ||||
| @@ -599,9 +708,9 @@ void Window::screenshotModules(const std::string&, float) { | |||||
| void Window::close() { | 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) | if (!settings::allowCursorLock) | ||||
| return; | return; | ||||
| emscripten_request_pointerlock(internal->ui->getWindow().getApp().getClassName(), false); | |||||
| emscripten_request_pointerlock(internal->tlw->getWindow().getApp().getClassName(), false); | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -643,7 +752,7 @@ int Window::getMods() { | |||||
| void Window::setFullScreen(const bool fullscreen) { | void Window::setFullScreen(const bool fullscreen) { | ||||
| #ifdef DISTRHO_OS_WASM | #ifdef DISTRHO_OS_WASM | ||||
| if (fullscreen) | if (fullscreen) | ||||
| emscripten_request_fullscreen(internal->ui->getWindow().getApp().getClassName(), false); | |||||
| emscripten_request_fullscreen(internal->tlw->getWindow().getApp().getClassName(), false); | |||||
| else | else | ||||
| emscripten_exit_fullscreen(); | emscripten_exit_fullscreen(); | ||||
| #endif | #endif | ||||