From 8722584c1e94752747e8524618046b13a77f7ecb Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 15 Apr 2023 14:34:15 +0200 Subject: [PATCH] Update dpf Signed-off-by: falkTX --- dpf/CMakeLists.txt | 9 +- dpf/Makefile.base.mk | 92 +- dpf/Makefile.plugins.mk | 281 ++-- dpf/cmake/DPF-plugin.cmake | 139 +- dpf/dgl/Color.hpp | 5 + dpf/dgl/EventHandlers.hpp | 2 +- dpf/dgl/Makefile | 170 +-- dpf/dgl/src/ApplicationPrivateData.cpp | 6 +- dpf/dgl/src/Color.cpp | 9 + dpf/dgl/src/EventHandlers.cpp | 2 +- dpf/dgl/src/pugl-custom/pugl.h | 492 ------- dpf/dgl/src/pugl-custom/pugl_haiku.cpp | 441 ------- dpf/dgl/src/pugl-custom/pugl_internal.h | 263 ---- dpf/dgl/src/pugl-custom/pugl_osx.m | 974 -------------- dpf/dgl/src/pugl-custom/pugl_win.cpp | 565 -------- dpf/dgl/src/pugl-custom/pugl_x11.c | 739 ----------- dpf/dgl/src/pugl-extra/extras.c | 29 - dpf/dgl/src/pugl-extra/extras.h | 50 - dpf/dgl/src/pugl-extra/haiku.cpp | 363 +++++- dpf/dgl/src/pugl-extra/haiku.h | 34 +- dpf/dgl/src/pugl-extra/haiku_gl.cpp | 87 ++ dpf/dgl/src/pugl-extra/haiku_stub.cpp | 24 + dpf/dgl/src/pugl-extra/mac.m | 48 - dpf/dgl/src/pugl-extra/wasm.c | 1141 +++++++++++++++++ dpf/dgl/src/pugl-extra/wasm.h | 45 + dpf/dgl/src/pugl-extra/wasm_gl.c | 228 ++++ dpf/dgl/src/pugl-extra/wasm_stub.c | 24 + dpf/dgl/src/pugl-extra/win.c | 118 -- dpf/dgl/src/pugl-extra/x11.c | 111 -- dpf/dgl/src/pugl.cpp | 46 +- dpf/dgl/src/pugl.hpp | 6 +- dpf/distrho/DistrhoDetails.hpp | 1031 +++++++++++++++ dpf/distrho/DistrhoPlugin.hpp | 936 +------------- dpf/distrho/DistrhoUI.hpp | 3 +- dpf/distrho/DistrhoUIMain.cpp | 4 + dpf/distrho/DistrhoUI_macOS.mm | 1 + dpf/distrho/DistrhoUtils.hpp | 21 +- dpf/distrho/extra/FileBrowserDialogImpl.hpp | 6 +- dpf/distrho/extra/ScopedDenormalDisable.hpp | 112 ++ dpf/distrho/extra/String.hpp | 16 +- dpf/distrho/extra/ValueSmoother.hpp | 204 +++ dpf/distrho/src/DistrhoPlugin.cpp | 2 +- dpf/distrho/src/DistrhoPluginCLAP.cpp | 67 +- dpf/distrho/src/DistrhoPluginChecks.h | 6 +- dpf/distrho/src/DistrhoPluginInternal.hpp | 2 +- dpf/distrho/src/DistrhoPluginJACK.cpp | 39 +- dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp | 18 +- dpf/distrho/src/DistrhoPluginLV2.cpp | 8 +- dpf/distrho/src/DistrhoPluginLV2export.cpp | 357 +++++- dpf/distrho/src/DistrhoPluginVST2.cpp | 2 +- dpf/distrho/src/DistrhoPluginVST3.cpp | 4 +- dpf/distrho/src/DistrhoUI.cpp | 3 +- dpf/distrho/src/DistrhoUIInternal.hpp | 5 +- dpf/distrho/src/DistrhoUILV2.cpp | 237 +++- dpf/distrho/src/DistrhoUIPrivateData.hpp | 89 +- dpf/distrho/src/DistrhoUIVST3.cpp | 2 +- dpf/distrho/src/jackbridge/JackBridge.cpp | 21 +- dpf/distrho/src/jackbridge/JackBridge.hpp | 3 +- dpf/distrho/src/jackbridge/NativeBridge.hpp | 97 +- dpf/distrho/src/jackbridge/RtAudioBridge.hpp | 38 +- dpf/distrho/src/jackbridge/SDL2Bridge.hpp | 73 +- dpf/distrho/src/jackbridge/WebBridge.hpp | 48 +- .../src/jackbridge/rtaudio/RtAudio.cpp | 7 +- dpf/utils/generate-ttl.sh | 6 +- 64 files changed, 4708 insertions(+), 5303 deletions(-) delete mode 100644 dpf/dgl/src/pugl-custom/pugl.h delete mode 100644 dpf/dgl/src/pugl-custom/pugl_haiku.cpp delete mode 100644 dpf/dgl/src/pugl-custom/pugl_internal.h delete mode 100644 dpf/dgl/src/pugl-custom/pugl_osx.m delete mode 100644 dpf/dgl/src/pugl-custom/pugl_win.cpp delete mode 100644 dpf/dgl/src/pugl-custom/pugl_x11.c delete mode 100644 dpf/dgl/src/pugl-extra/extras.c delete mode 100644 dpf/dgl/src/pugl-extra/extras.h create mode 100644 dpf/dgl/src/pugl-extra/haiku_gl.cpp create mode 100644 dpf/dgl/src/pugl-extra/haiku_stub.cpp delete mode 100644 dpf/dgl/src/pugl-extra/mac.m create mode 100644 dpf/dgl/src/pugl-extra/wasm.c create mode 100644 dpf/dgl/src/pugl-extra/wasm.h create mode 100644 dpf/dgl/src/pugl-extra/wasm_gl.c create mode 100644 dpf/dgl/src/pugl-extra/wasm_stub.c delete mode 100644 dpf/dgl/src/pugl-extra/win.c delete mode 100644 dpf/dgl/src/pugl-extra/x11.c create mode 100644 dpf/distrho/DistrhoDetails.hpp create mode 100644 dpf/distrho/extra/ScopedDenormalDisable.hpp create mode 100644 dpf/distrho/extra/ValueSmoother.hpp diff --git a/dpf/CMakeLists.txt b/dpf/CMakeLists.txt index f775962..5a3bbf9 100644 --- a/dpf/CMakeLists.txt +++ b/dpf/CMakeLists.txt @@ -32,7 +32,7 @@ if(DPF_LIBRARIES) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(CAIRO "cairo") - if(CAIRO_FOUND) + if(CAIRO_FOUND AND (NOT HAIKU)) dpf__add_dgl_cairo(FALSE) endif() endif() @@ -43,11 +43,14 @@ if(DPF_EXAMPLES) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(CAIRO "cairo") - if(CAIRO_FOUND) + if(CAIRO_FOUND AND (NOT HAIKU)) add_subdirectory("examples/CairoUI") endif() endif() - #add_subdirectory("examples/ExternalUI") + if((NOT WIN32) AND (NOT APPLE)) + add_subdirectory("examples/ExternalUI") + endif() + add_subdirectory("examples/EmbedExternalUI") add_subdirectory("examples/FileHandling") add_subdirectory("examples/Info") add_subdirectory("examples/Latency") diff --git a/dpf/Makefile.base.mk b/dpf/Makefile.base.mk index 4ee576e..45adc95 100644 --- a/dpf/Makefile.base.mk +++ b/dpf/Makefile.base.mk @@ -35,7 +35,9 @@ DPF_MAKEFILE_BASE_INCLUDED = true # --------------------------------------------------------------------------------------------------------------------- # Auto-detect target compiler if not defined -ifeq ($(shell echo '\#test' | grep -- '\#test'),\#test) +ifneq ($(shell echo -e escaped-by-default | grep -- '-e escaped-by-default'),-e escaped-by-default) +TARGET_COMPILER = $(shell echo -e '#ifdef __clang__\nclang\n#else\ngcc\n#endif' | $(CC) -E -P -x c - 2>/dev/null) +else ifeq ($(shell echo '\#escaped-by-default' | grep -- '\#escaped-by-default'),\#escaped-by-default) TARGET_COMPILER = $(shell echo '\#ifdef __clang__\nclang\n\#else\ngcc\n\#endif' | $(CC) -E -P -x c - 2>/dev/null) else TARGET_COMPILER = $(shell echo '#ifdef __clang__\nclang\n#else\ngcc\n#endif' | $(CC) -E -P -x c - 2>/dev/null) @@ -272,7 +274,10 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections endif ifeq ($(DEBUG),true) -BASE_FLAGS += -DDEBUG -O0 -g -fsanitize=address +BASE_FLAGS += -DDEBUG -O0 -g +ifneq ($(HAIKU),true) +BASE_FLAGS += -fsanitize=address +endif LINK_OPTS = ifeq ($(WASM),true) LINK_OPTS += -sASSERTIONS=1 @@ -347,9 +352,11 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Check for required libraries -HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) +ifneq ($(HAIKU),true) +HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true) +endif -ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) +ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) HAVE_OPENGL = true else HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) @@ -402,18 +409,34 @@ endif # Set Generic DGL stuff ifeq ($(HAIKU),true) + DGL_SYSTEM_LIBS += -lbe + else ifeq ($(MACOS),true) -DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo + +DGL_SYSTEM_LIBS += -framework Cocoa +DGL_SYSTEM_LIBS += -framework CoreVideo + else ifeq ($(WASM),true) + +# wasm builds cannot work using regular desktop OpenGL +ifeq (,$(USE_GLES2)$(USE_GLES3)) +USE_GLES2 = true +endif + else ifeq ($(WINDOWS),true) -DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 -# -lole32 + +DGL_SYSTEM_LIBS += -lcomdlg32 +DGL_SYSTEM_LIBS += -lgdi32 +# DGL_SYSTEM_LIBS += -lole32 + else + ifeq ($(HAVE_DBUS),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) endif + ifeq ($(HAVE_X11),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11) @@ -429,7 +452,8 @@ ifeq ($(HAVE_XRANDR),true) DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) endif -endif +endif # HAVE_X11 + endif # --------------------------------------------------------------------------------------------------------------------- @@ -444,7 +468,7 @@ CAIRO_LIBS = $(shell $(PKG_CONFIG) --libs cairo) HAVE_CAIRO_OR_OPENGL = true -endif +endif # HAVE_CAIRO # --------------------------------------------------------------------------------------------------------------------- # Set OpenGL specific stuff @@ -454,8 +478,8 @@ ifeq ($(HAVE_OPENGL),true) DGL_FLAGS += -DHAVE_OPENGL ifeq ($(HAIKU),true) -OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl) -OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl) +OPENGL_FLAGS = +OPENGL_LIBS = -lGL else ifeq ($(MACOS),true) OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations OPENGL_LIBS = -framework OpenGL @@ -476,12 +500,12 @@ endif HAVE_CAIRO_OR_OPENGL = true -endif +endif # HAVE_OPENGL # --------------------------------------------------------------------------------------------------------------------- # Set Stub specific stuff -ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) +ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) HAVE_STUB = true else HAVE_STUB = $(HAVE_X11) @@ -540,7 +564,7 @@ endif # --------------------------------------------------------------------------------------------------------------------- # Backwards-compatible HAVE_DGL -ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) +ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) HAVE_DGL = true else ifeq ($(HAVE_OPENGL),true) HAVE_DGL = $(HAVE_X11) @@ -633,6 +657,41 @@ else SHARED = -shared endif +# --------------------------------------------------------------------------------------------------------------------- +# Set CLAP binary directory + +ifeq ($(MACOS),true) +CLAP_BINARY_DIR = Contents/MacOS +else +CLAP_BINARY_DIR = +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set VST2 binary directory + +ifeq ($(MACOS),true) +VST2_BINARY_DIR = Contents/MacOS +else +VST2_BINARY_DIR = +endif + +# --------------------------------------------------------------------------------------------------------------------- +# Set VST3 binary directory, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html + +ifeq ($(LINUX),true) +VST3_BINARY_DIR = Contents/$(TARGET_PROCESSOR)-linux +else ifeq ($(MACOS),true) +VST3_BINARY_DIR = Contents/MacOS +else ifeq ($(WASM),true) +VST3_BINARY_DIR = Contents/wasm +else ifeq ($(WINDOWS)$(CPU_I386),truetrue) +VST3_BINARY_DIR = Contents/x86-win +else ifeq ($(WINDOWS)$(CPU_X86_64),truetrue) +VST3_BINARY_DIR = Contents/x86_64-win +else +VST3_BINARY_DIR = +endif + # --------------------------------------------------------------------------------------------------------------------- # Handle the verbosity switch @@ -713,6 +772,7 @@ MOD_ENVIRONMENT = \ CXX=${1}/host/usr/bin/${2}-g++ \ LD=${1}/host/usr/bin/${2}-ld \ PKG_CONFIG=${1}/host/usr/bin/pkg-config \ + PKG_CONFIG_PATH="${1}/staging/usr/lib/pkgconfig" \ STRIP=${1}/host/usr/bin/${2}-strip \ CFLAGS="-I${1}/staging/usr/include $(EXTRA_MOD_FLAGS)" \ CPPFLAGS= \ @@ -738,12 +798,12 @@ modpush: ifneq (,$(findstring modduo-,$(MAKECMDGOALS))) $(MAKECMDGOALS): - $(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo,arm-mod-linux-gnueabihf,arm) $(subst modduo-,,$(MAKECMDGOALS)) + $(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm) $(subst modduo-,,$(MAKECMDGOALS)) endif ifneq (,$(findstring modduox-,$(MAKECMDGOALS))) $(MAKECMDGOALS): - $(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox,aarch64-mod-linux-gnueabi,aarch64) $(subst modduox-,,$(MAKECMDGOALS)) + $(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64) $(subst modduox-,,$(MAKECMDGOALS)) endif ifneq (,$(findstring moddwarf-,$(MAKECMDGOALS))) diff --git a/dpf/Makefile.plugins.mk b/dpf/Makefile.plugins.mk index 1a3ea06..4233c99 100644 --- a/dpf/Makefile.plugins.mk +++ b/dpf/Makefile.plugins.mk @@ -6,11 +6,18 @@ # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! - ifeq ($(DPF_PATH),) -ifeq (,$(wildcard ../../Makefile.base.mk)) +ifneq (,$(wildcard dpf/Makefile.base.mk)) +BASE_PATH=. +DPF_PATH=dpf +else ifneq (,$(wildcard ../dpf/Makefile.base.mk)) +BASE_PATH=.. +DPF_PATH=../dpf +else ifneq (,$(wildcard ../../dpf/Makefile.base.mk)) +BASE_PATH=../.. DPF_PATH=../../dpf else +BASE_PATH=../.. DPF_PATH=../.. endif endif @@ -20,17 +27,24 @@ include $(DPF_PATH)/Makefile.base.mk # --------------------------------------------------------------------------------------------------------------------- # Basic setup -ifeq ($(DPF_TARGET_DIR),) -TARGET_DIR = ../../bin +ifeq ($(MODGUI_BUILD),true) +BUILD_DIR_SUFFIX = -modgui +endif + +ifneq ($(DPF_BUILD_DIR),) +BUILD_DIR = $(DPF_BUILD_DIR)$(BUILD_DIR_SUFFIX) else -TARGET_DIR = $(DPF_TARGET_DIR) +BUILD_DIR = $(BASE_PATH)/build$(BUILD_DIR_SUFFIX)/$(NAME) endif -ifeq ($(DPF_BUILD_DIR),) -BUILD_DIR = ../../build/$(NAME) + +ifneq ($(DPF_TARGET_DIR),) +TARGET_DIR = $(DPF_TARGET_DIR) else -BUILD_DIR = $(DPF_BUILD_DIR) +TARGET_DIR = $(BASE_PATH)/bin endif +DGL_BUILD_DIR = $(DPF_PATH)/build$(BUILD_DIR_SUFFIX) + BUILD_C_FLAGS += -I. BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl @@ -58,6 +72,10 @@ ifeq ($(HAVE_SDL2),true) BASE_FLAGS += -DHAVE_SDL2 endif +ifneq ($(MODGUI_CLASS_NAME),) +BASE_FLAGS += -DDISTRHO_PLUGIN_MODGUI_CLASS_NAME='"$(MODGUI_CLASS_NAME)"' +endif + # always needed ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) ifneq ($(STATIC_BUILD),true) @@ -80,14 +98,16 @@ endif else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) +JACK_FLAGS += -DHAVE_GETTIMEOFDAY + ifeq ($(MACOS),true) JACK_LIBS += -framework CoreAudio -framework CoreFoundation -framework CoreMIDI else ifeq ($(WINDOWS),true) JACK_LIBS += -lole32 -lwinmm # DirectSound -JACK_LIBS += -ldsound +# JACK_LIBS += -ldsound # WASAPI -# JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid +JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid else ifeq ($(HAVE_PULSEAUDIO),true) JACK_FLAGS += $(PULSEAUDIO_FLAGS) @@ -147,7 +167,7 @@ ifeq ($(HAVE_CAIRO),true) DGL_FLAGS += -DDGL_CAIRO -DHAVE_DGL DGL_FLAGS += $(CAIRO_FLAGS) DGL_LIBS += $(CAIRO_LIBS) -DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a +DGL_LIB = $(DGL_BUILD_DIR)/libdgl-cairo.a HAVE_DGL = true else HAVE_DGL = false @@ -159,7 +179,7 @@ ifeq ($(HAVE_OPENGL),true) DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL DGL_FLAGS += $(OPENGL_FLAGS) DGL_LIBS += $(OPENGL_LIBS) -DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a +DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl.a HAVE_DGL = true else HAVE_DGL = false @@ -171,7 +191,7 @@ ifeq ($(HAVE_OPENGL),true) DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL DGL_FLAGS += $(OPENGL_FLAGS) DGL_LIBS += $(OPENGL_LIBS) -DGL_LIB = $(DPF_PATH)/build/libdgl-opengl3.a +DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl3.a HAVE_DGL = true else HAVE_DGL = false @@ -183,7 +203,7 @@ ifeq ($(HAVE_VULKAN),true) DGL_FLAGS += -DDGL_VULKAN -DHAVE_DGL DGL_FLAGS += $(VULKAN_FLAGS) DGL_LIBS += $(VULKAN_LIBS) -DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a +DGL_LIB = $(DGL_BUILD_DIR)/libdgl-vulkan.a HAVE_DGL = true else HAVE_DGL = false @@ -197,7 +217,7 @@ endif ifeq ($(UI_TYPE),stub) ifeq ($(HAVE_STUB),true) -DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a +DGL_LIB = $(DGL_BUILD_DIR)/libdgl-stub.a HAVE_DGL = true else HAVE_DGL = false @@ -209,12 +229,22 @@ DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm # TODO split dsp and ui object build flags BASE_FLAGS += $(DGL_FLAGS) +# --------------------------------------------------------------------------------------------------------------------- +# Set CLAP filename, either single binary or inside a bundle + +ifeq ($(MACOS),true) +CLAP_FILENAME = $(NAME).clap/$(CLAP_BINARY_DIR)/$(NAME) +else ifeq ($(USE_CLAP_BUNDLE),true) +CLAP_FILENAME = $(NAME).clap/$(NAME).clap +else +CLAP_FILENAME = $(NAME).clap +endif + # --------------------------------------------------------------------------------------------------------------------- # Set VST2 filename, either single binary or inside a bundle ifeq ($(MACOS),true) -VST2_CONTENTS = $(NAME).vst/Contents -VST2_FILENAME = $(VST2_CONTENTS)/MacOS/$(NAME) +VST2_FILENAME = $(NAME).vst/$(VST2_BINARY_DIR)/$(NAME) else ifeq ($(USE_VST2_BUNDLE),true) VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) else @@ -225,39 +255,22 @@ endif # Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html ifeq ($(LINUX),true) -VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so +VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME)$(LIB_EXT) else ifeq ($(MACOS),true) -VST3_CONTENTS = $(NAME).vst3/Contents -VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME) -else ifeq ($(WASM),true) -VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3 -else ifeq ($(WINDOWS),true) -ifeq ($(CPU_I386),true) -VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3 -else ifeq ($(CPU_X86_64),true) -VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3 -endif -endif - -# --------------------------------------------------------------------------------------------------------------------- -# Set CLAP filename, either single binary or inside a bundle - -ifeq ($(MACOS),true) -CLAP_CONTENTS = $(NAME).clap/Contents -CLAP_FILENAME = $(CLAP_CONTENTS)/MacOS/$(NAME) -else ifeq ($(USE_CLAP_BUNDLE),true) -CLAP_FILENAME = $(NAME).clap/$(NAME).clap -else -CLAP_FILENAME = $(NAME).clap +VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME) +else ifneq ($(VST3_BINARY_DIR),) +VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME).vst3 endif # --------------------------------------------------------------------------------------------------------------------- # Set plugin binary file targets -ifeq ($(MACOS),true) -ifeq ($(HAVE_DGL),true) +ifeq ($(MACOS)$(HAVE_DGL),truetrue) MACOS_APP_BUNDLE = true endif + +ifeq ($(WINDOWS)$(HAVE_DGL),truetrue) +JACK_LIBS += -Wl,-subsystem,windows endif ifeq ($(MACOS_APP_BUNDLE),true) @@ -266,6 +279,7 @@ jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist else jack = $(TARGET_DIR)/$(NAME)$(APP_EXT) endif + ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT) @@ -281,15 +295,10 @@ shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT) static = $(TARGET_DIR)/$(NAME).a ifeq ($(MACOS),true) -vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist -vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/PkgInfo -vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Resources/empty.lproj -vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Info.plist -vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo -vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj -clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Info.plist -clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/PkgInfo -clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Resources/empty.lproj +BUNDLE_RESOURCES = Info.plist PkgInfo Resources/empty.lproj +vst2files += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).vst/Contents/%) +vst3files += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).vst3/Contents/%) +clapfiles += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).clap/Contents/%) endif ifneq ($(HAVE_DGL),true) @@ -364,37 +373,41 @@ all: # Common $(BUILD_DIR)/%.S.o: %.S - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ $(BUILD_DIR)/%.c.o: %.c - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ $(BUILD_DIR)/%.cc.o: %.cc - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ $(BUILD_DIR)/%.cpp.o: %.cpp - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ $(BUILD_DIR)/%.m.o: %.m - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ $(BUILD_DIR)/%.mm.o: %.mm - -@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" + -@mkdir -p "$(shell dirname $@)" @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ clean: rm -rf $(BUILD_DIR) +ifeq ($(DPF_BUILD_DIR),) + rm -rf $(BASE_PATH)/build-modgui/$(NAME) + rm -rf $(DPF_PATH)/build-modgui +endif rm -rf $(TARGET_DIR)/$(NAME) rm -rf $(TARGET_DIR)/$(NAME)-* rm -rf $(TARGET_DIR)/$(NAME).lv2 @@ -405,44 +418,52 @@ clean: # --------------------------------------------------------------------------------------------------------------------- # DGL -$(DPF_PATH)/build/libdgl-cairo.a: +DGL_POSSIBLE_DEPS = \ + $(DPF_PATH)/dgl/*.* \ + $(DPF_PATH)/dgl/src/*.* \ + $(DPF_PATH)/dgl/src/nanovg/*.* \ + $(DPF_PATH)/dgl/src/pugl-extra/*.* \ + $(DPF_PATH)/dgl/src/pugl-upstream/include/pugl/*.* \ + $(DPF_PATH)/dgl/src/pugl-upstream/src/*.* + +$(DGL_BUILD_DIR)/libdgl-cairo.a: $(DGL_POSSIBLE_DEPS) $(MAKE) -C $(DPF_PATH)/dgl cairo -$(DPF_PATH)/build/libdgl-opengl.a: +$(DGL_BUILD_DIR)/libdgl-opengl.a: $(DGL_POSSIBLE_DEPS) $(MAKE) -C $(DPF_PATH)/dgl opengl -$(DPF_PATH)/build/libdgl-opengl3.a: +$(DGL_BUILD_DIR)/libdgl-opengl3.a: $(DGL_POSSIBLE_DEPS) $(MAKE) -C $(DPF_PATH)/dgl opengl3 -$(DPF_PATH)/build/libdgl-stub.a: +$(DGL_BUILD_DIR)/libdgl-stub.a: $(DGL_POSSIBLE_DEPS) $(MAKE) -C $(DPF_PATH)/dgl stub -$(DPF_PATH)/build/libdgl-vulkan.a: +$(DGL_BUILD_DIR)/libdgl-vulkan.a: $(DGL_POSSIBLE_DEPS) $(MAKE) -C $(DPF_PATH)/dgl vulkan # --------------------------------------------------------------------------------------------------------------------- -$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ -$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUIMain.cpp ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ -$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUI_macOS.mm ($*)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ -$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoPluginMain.cpp (JACK)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@ -$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) +$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES) -@mkdir -p $(BUILD_DIR) @echo "Compiling DistrhoUIMain.cpp (DSSI)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI $(LIBLO_FLAGS) -c -o $@ @@ -459,7 +480,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating JACK standalone for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(JACK_LIBS) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(JACK_LIBS) -o $@ # --------------------------------------------------------------------------------------------------------------------- # LADSPA @@ -469,7 +490,7 @@ ladspa: $(ladspa_dsp) $(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating LADSPA plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_LADSPA) -o $@ # --------------------------------------------------------------------------------------------------------------------- # DSSI @@ -481,12 +502,12 @@ dssi_ui: $(dssi_ui) $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating DSSI plugin library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_DSSI) -o $@ $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating DSSI UI for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@ # --------------------------------------------------------------------------------------------------------------------- # LV2 @@ -502,17 +523,111 @@ $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@ $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@ $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) -@mkdir -p $(shell dirname $@) @echo "Creating LV2 plugin UI for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@ + +# --------------------------------------------------------------------------------------------------------------------- +# LV2 modgui + +ifeq ($(MODGUI_BUILD),true) +ifeq ($(MODGUI_CLASS_NAME),) +$(error MODGUI_CLASS_NAME undefined) +endif +endif + +# clear all possible flags coming from DPF, while keeping any extra flags specified for this build +MODGUI_IGNORED_FLAGS = -fdata-sections +MODGUI_IGNORED_FLAGS += -ffast-math +MODGUI_IGNORED_FLAGS += -ffunction-sections +MODGUI_IGNORED_FLAGS += -fno-gnu-unique +MODGUI_IGNORED_FLAGS += -fprefetch-loop-arrays +MODGUI_IGNORED_FLAGS += -fvisibility=hidden +MODGUI_IGNORED_FLAGS += -fvisibility-inlines-hidden +MODGUI_IGNORED_FLAGS += -fPIC +MODGUI_IGNORED_FLAGS += -ldl +MODGUI_IGNORED_FLAGS += -mfpmath=sse +MODGUI_IGNORED_FLAGS += -msse +MODGUI_IGNORED_FLAGS += -msse2 +MODGUI_IGNORED_FLAGS += -mtune=generic +MODGUI_IGNORED_FLAGS += -pipe +MODGUI_IGNORED_FLAGS += -std=gnu99 +MODGUI_IGNORED_FLAGS += -std=gnu++11 +MODGUI_IGNORED_FLAGS += -DDISTRHO_PLUGIN_MODGUI_CLASS_NAME='"$(MODGUI_CLASS_NAME)"' +MODGUI_IGNORED_FLAGS += -DDGL_OPENGL +MODGUI_IGNORED_FLAGS += -DGL_SILENCE_DEPRECATION=1 +MODGUI_IGNORED_FLAGS += -DHAVE_ALSA +MODGUI_IGNORED_FLAGS += -DHAVE_DGL +MODGUI_IGNORED_FLAGS += -DHAVE_JACK +MODGUI_IGNORED_FLAGS += -DHAVE_LIBLO +MODGUI_IGNORED_FLAGS += -DHAVE_OPENGL +MODGUI_IGNORED_FLAGS += -DHAVE_PULSEAUDIO +MODGUI_IGNORED_FLAGS += -DHAVE_RTAUDIO +MODGUI_IGNORED_FLAGS += -DHAVE_SDL2 +MODGUI_IGNORED_FLAGS += -DNDEBUG +MODGUI_IGNORED_FLAGS += -DPIC +MODGUI_IGNORED_FLAGS += -I. +MODGUI_IGNORED_FLAGS += -I$(DPF_PATH)/distrho +MODGUI_IGNORED_FLAGS += -I$(DPF_PATH)/dgl +MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/modduo-static/staging/usr/include +MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/modduox-static/staging/usr/include +MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/moddwarf/staging/usr/include +MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/modduo-static/staging/usr/lib +MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/modduox-static/staging/usr/lib +MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/moddwarf/staging/usr/lib +MODGUI_IGNORED_FLAGS += -MD +MODGUI_IGNORED_FLAGS += -MP +MODGUI_IGNORED_FLAGS += -O2 +MODGUI_IGNORED_FLAGS += -O3 +MODGUI_IGNORED_FLAGS += -Wall +MODGUI_IGNORED_FLAGS += -Wextra +MODGUI_IGNORED_FLAGS += -Wl,-O1,--as-needed,--gc-sections +MODGUI_IGNORED_FLAGS += -Wl,-dead_strip,-dead_strip_dylibs +MODGUI_IGNORED_FLAGS += -Wl,-x +MODGUI_IGNORED_FLAGS += -Wl,--gc-sections +MODGUI_IGNORED_FLAGS += -Wl,--no-undefined +MODGUI_IGNORED_FLAGS += -Wl,--strip-all +MODGUI_IGNORED_FLAGS += -Wno-deprecated-declarations +MODGUI_IGNORED_FLAGS += $(DGL_FLAGS) +MODGUI_CFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(BUILD_C_FLAGS)) -D__MOD_DEVICES__ +MODGUI_CXXFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(BUILD_CXX_FLAGS)) -D__MOD_DEVICES__ +MODGUI_LDFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(LINK_FLAGS)) + +$(TARGET_DIR)/$(NAME).lv2/modgui/module.js: $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) + -@mkdir -p $(shell dirname $@) + @echo "Creating LV2 plugin modgui for $(NAME)" + $(SILENT)$(CXX) $^ $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) \ + -sALLOW_MEMORY_GROWTH -sALLOW_TABLE_GROWTH -sDISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 -sLZ4=1 \ + -sMODULARIZE=1 -sMAIN_MODULE=2 \ + -sEXPORTED_FUNCTIONS="['_malloc','_free','_modgui_init','_modgui_param_set','_modgui_patch_set','_modgui_cleanup']" \ + -sEXPORTED_RUNTIME_METHODS=['addFunction','lengthBytesUTF8','stringToUTF8','UTF8ToString'] \ + -sEXPORT_NAME="Module_$(MODGUI_CLASS_NAME)" \ + -o $@ + +modgui: + $(MAKE) $(TARGET_DIR)/$(NAME).lv2/modgui/module.js \ + EXE_WRAPPER= \ + HAVE_OPENGL=true \ + MODGUI_BUILD=true \ + NOOPT=true \ + PKG_CONFIG=false \ + USE_GLES2=true \ + AR=emar \ + CC=emcc \ + CXX=em++ \ + CFLAGS="-O3 $(MODGUI_CFLAGS)" \ + CXXFLAGS="-O3 $(MODGUI_CXXFLAGS)" \ + LDFLAGS="-O3 $(MODGUI_LDFLAGS)" + +.PHONY: modgui # --------------------------------------------------------------------------------------------------------------------- # VST2 @@ -526,7 +641,7 @@ $(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST2 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@ # --------------------------------------------------------------------------------------------------------------------- # VST3 @@ -540,7 +655,7 @@ $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating VST3 plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@ # --------------------------------------------------------------------------------------------------------------------- # CLAP @@ -562,7 +677,7 @@ $(clap): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating CLAP plugin for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@ # --------------------------------------------------------------------------------------------------------------------- # Shared @@ -576,7 +691,7 @@ $(shared): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o endif -@mkdir -p $(shell dirname $@) @echo "Creating shared library for $(NAME)" - $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ + $(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@ # --------------------------------------------------------------------------------------------------------------------- # Static @@ -600,15 +715,7 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ -$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist - -@mkdir -p $(shell dirname $@) - $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ - -$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist - -@mkdir -p $(shell dirname $@) - $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ - -$(TARGET_DIR)/%.clap/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist +$(TARGET_DIR)/%/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist -@mkdir -p $(shell dirname $@) $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ diff --git a/dpf/cmake/DPF-plugin.cmake b/dpf/cmake/DPF-plugin.cmake index 8155277..ceb6906 100644 --- a/dpf/cmake/DPF-plugin.cmake +++ b/dpf/cmake/DPF-plugin.cmake @@ -75,10 +75,10 @@ include(CMakeParseArguments) # `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3`, `clap` # # `UI_TYPE` -# the user interface type: `opengl` (default), `cairo` +# the user interface type: `opengl` (default), `cairo`, `external` # -# `MONOLITHIC` -# build LV2 as a single binary for UI and DSP +# `FILES_COMMON` ... +# list of sources which are part of both DSP and UI # # `FILES_DSP` ... # list of sources which are part of the DSP @@ -87,13 +87,19 @@ include(CMakeParseArguments) # list of sources which are part of the UI # empty indicates the plugin does not have UI # -# `FILES_COMMON` ... -# list of sources which are part of both DSP and UI +# `MODGUI_CLASS_NAME` +# class name to use for modgui builds +# +# `MONOLITHIC` +# build LV2 as a single binary for UI and DSP +# +# `NO_SHARED_RESOURCES` +# do not build DPF shared resources (fonts, etc) # function(dpf_add_plugin NAME) set(options MONOLITHIC NO_SHARED_RESOURCES) - set(oneValueArgs UI_TYPE) - set(multiValueArgs TARGETS FILES_DSP FILES_UI FILES_COMMON) + set(oneValueArgs MODGUI_CLASS_NAME UI_TYPE) + set(multiValueArgs FILES_COMMON FILES_DSP FILES_UI TARGETS) cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if("${_dpf_plugin_UI_TYPE}" STREQUAL "") @@ -101,6 +107,7 @@ function(dpf_add_plugin NAME) endif() set(_dgl_library) + set(_dgl_external OFF) if(_dpf_plugin_FILES_UI) if(_dpf_plugin_UI_TYPE STREQUAL "cairo") dpf__add_dgl_cairo("${_dpf_plugin_NO_SHARED_RESOURCES}") @@ -108,11 +115,18 @@ function(dpf_add_plugin NAME) elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl") dpf__add_dgl_opengl("${_dpf_plugin_NO_SHARED_RESOURCES}") set(_dgl_library dgl-opengl) + elseif(_dpf_plugin_UI_TYPE STREQUAL "external") + set(_dgl_external ON) else() message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}") endif() endif() + set(_dgl_has_ui OFF) + if(_dgl_library OR _dgl_external) + set(_dgl_has_ui ON) + endif() + ### dpf__ensure_sources_non_empty(_dpf_plugin_FILES_COMMON) dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP) @@ -123,21 +137,26 @@ function(dpf_add_plugin NAME) target_include_directories("${NAME}" PUBLIC "${DPF_ROOT_DIR}/distrho") + if(_dpf_plugin_MODGUI_CLASS_NAME) + target_compile_definitions("${NAME}" PUBLIC "DISTRHO_PLUGIN_MODGUI_CLASS_NAME=\"${_dpf_plugin_MODGUI_CLASS_NAME}\"") + endif() + if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) target_link_libraries("${NAME}" PRIVATE "dl") endif() - if(_dgl_library) + if(_dgl_library AND NOT _dgl_external) # make sure that all code will see DGL_* definitions target_link_libraries("${NAME}" PUBLIC "${_dgl_library}-definitions" - dgl-system-libs-definitions) + dgl-system-libs-definitions + dgl-system-libs) endif() dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP}) target_link_libraries("${NAME}-dsp" PUBLIC "${NAME}") - if(_dgl_library) + if(_dgl_library AND NOT _dgl_external) dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI}) target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library}) if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) @@ -145,6 +164,14 @@ function(dpf_add_plugin NAME) endif() # add the files containing Objective-C classes dpf__add_plugin_specific_ui_sources("${NAME}-ui") + elseif(_dgl_external) + dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI}) + target_link_libraries("${NAME}-ui" PUBLIC "${NAME}") + if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) + target_link_libraries("${NAME}-ui" PRIVATE "dl") + endif() + # add the files containing Objective-C classes + dpf__add_plugin_specific_ui_sources("${NAME}-ui") else() add_library("${NAME}-ui" INTERFACE) endif() @@ -152,19 +179,19 @@ function(dpf_add_plugin NAME) ### foreach(_target ${_dpf_plugin_TARGETS}) if(_target STREQUAL "jack") - dpf__build_jack("${NAME}" "${_dgl_library}") + dpf__build_jack("${NAME}" "${_dgl_has_ui}") elseif(_target STREQUAL "ladspa") dpf__build_ladspa("${NAME}") elseif(_target STREQUAL "dssi") - dpf__build_dssi("${NAME}" "${_dgl_library}") + dpf__build_dssi("${NAME}" "${_dgl_has_ui}") elseif(_target STREQUAL "lv2") - dpf__build_lv2("${NAME}" "${_dgl_library}" "${_dpf_plugin_MONOLITHIC}") + dpf__build_lv2("${NAME}" "${_dgl_has_ui}" "${_dpf_plugin_MONOLITHIC}") elseif(_target STREQUAL "vst2") - dpf__build_vst2("${NAME}" "${_dgl_library}") + dpf__build_vst2("${NAME}" "${_dgl_has_ui}") elseif(_target STREQUAL "vst3") - dpf__build_vst3("${NAME}" "${_dgl_library}") + dpf__build_vst3("${NAME}" "${_dgl_has_ui}") elseif(_target STREQUAL "clap") - dpf__build_clap("${NAME}" "${_dgl_library}") + dpf__build_clap("${NAME}" "${_dgl_has_ui}") else() message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") endif() @@ -184,26 +211,27 @@ endfunction() # # Add build rules for a JACK/Standalone program. # -function(dpf__build_jack NAME DGL_LIBRARY) +function(dpf__build_jack NAME HAS_UI) dpf__create_dummy_source_list(_no_srcs) dpf__add_executable("${NAME}-jack" ${_no_srcs}) dpf__add_plugin_main("${NAME}-jack" "jack") - dpf__add_ui_main("${NAME}-jack" "jack" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-jack" "jack" "${HAS_UI}") target_link_libraries("${NAME}-jack" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-jack" PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" OUTPUT_NAME "${NAME}") target_compile_definitions("${NAME}" PUBLIC "HAVE_JACK") + target_compile_definitions("${NAME}-jack" PRIVATE "HAVE_GETTIMEOFDAY") find_package(PkgConfig) pkg_check_modules(SDL2 "sdl2") if(SDL2_FOUND) target_compile_definitions("${NAME}" PUBLIC "HAVE_SDL2") - target_include_directories("${NAME}-jack" PRIVATE ${SDL2_INCLUDE_DIRS}) - target_link_libraries("${NAME}-jack" PRIVATE ${SDL2_LIBRARIES}) - dpf__target_link_directories("${NAME}-jack" ${SDL2_LIBRARY_DIRS}) + target_include_directories("${NAME}-jack" PRIVATE ${SDL2_STATIC_INCLUDE_DIRS}) + target_link_libraries("${NAME}-jack" PRIVATE ${SDL2_STATIC_LIBRARIES}) + dpf__target_link_directories("${NAME}-jack" "${SDL2_STATIC_LIBRARY_DIRS}") endif() if(APPLE OR WIN32) @@ -216,13 +244,13 @@ function(dpf__build_jack NAME DGL_LIBRARY) target_compile_definitions("${NAME}" PUBLIC "HAVE_ALSA") target_include_directories("${NAME}-jack" PRIVATE ${ALSA_INCLUDE_DIRS}) target_link_libraries("${NAME}-jack" PRIVATE ${ALSA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - dpf__target_link_directories("${NAME}-jack" ${ALSA_LIBRARY_DIRS}) + dpf__target_link_directories("${NAME}-jack" "${ALSA_LIBRARY_DIRS}") endif() if(PULSEAUDIO_FOUND) target_compile_definitions("${NAME}" PUBLIC "HAVE_PULSEAUDIO") target_include_directories("${NAME}-jack" PRIVATE ${PULSEAUDIO_INCLUDE_DIRS}) target_link_libraries("${NAME}-jack" PRIVATE ${PULSEAUDIO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - dpf__target_link_directories("${NAME}-jack" ${PULSEAUDIO_LIBRARY_DIRS}) + dpf__target_link_directories("${NAME}-jack" "${PULSEAUDIO_LIBRARY_DIRS}") endif() if(ALSA_FOUND OR PULSEAUDIO_FOUND) target_compile_definitions("${NAME}" PUBLIC "HAVE_RTAUDIO") @@ -239,7 +267,10 @@ function(dpf__build_jack NAME DGL_LIBRARY) "${APPLE_COREFOUNDATION_FRAMEWORK}" "${APPLE_COREMIDI_FRAMEWORK}") elseif(WIN32) - target_link_libraries("${NAME}-jack" PRIVATE "dsound" "ole32" "winmm") + target_link_libraries("${NAME}-jack" PRIVATE "ksuser" "mfplat" "mfuuid" "ole32" "winmm" "wmcodecdspuuid") + if(HAS_UI AND MINGW) + set_target_properties("${NAME}-jack" PROPERTIES WIN32_EXECUTABLE TRUE) + endif() endif() endfunction() @@ -267,7 +298,7 @@ endfunction() # # Add build rules for a DSSI plugin. # -function(dpf__build_dssi NAME DGL_LIBRARY) +function(dpf__build_dssi NAME HAS_UI) find_package(PkgConfig) pkg_check_modules(LIBLO "liblo") if(NOT LIBLO_FOUND) @@ -288,9 +319,9 @@ function(dpf__build_dssi NAME DGL_LIBRARY) OUTPUT_NAME "${NAME}-dssi" PREFIX "") - if(DGL_LIBRARY) + if(HAS_UI) dpf__add_executable("${NAME}-dssi-ui" ${_no_srcs}) - dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${HAS_UI}") target_link_libraries("${NAME}-dssi-ui" PRIVATE "${NAME}-ui") set_target_properties("${NAME}-dssi-ui" PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}-dssi/$<0:>" @@ -299,7 +330,7 @@ function(dpf__build_dssi NAME DGL_LIBRARY) target_compile_definitions("${NAME}" PUBLIC "HAVE_LIBLO") target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS}) target_link_libraries("${NAME}-dssi-ui" PRIVATE ${LIBLO_LIBRARIES}) - dpf__target_link_directories("${NAME}-dssi-ui" ${LIBLO_LIBRARY_DIRS}) + dpf__target_link_directories("${NAME}-dssi-ui" "${LIBLO_LIBRARY_DIRS}") endif() endfunction() @@ -308,12 +339,12 @@ endfunction() # # Add build rules for an LV2 plugin. # -function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) +function(dpf__build_lv2 NAME HAS_UI MONOLITHIC) dpf__create_dummy_source_list(_no_srcs) dpf__add_module("${NAME}-lv2" ${_no_srcs}) dpf__add_plugin_main("${NAME}-lv2" "lv2") - if(DGL_LIBRARY AND MONOLITHIC) + if(HAS_UI AND MONOLITHIC) dpf__set_module_export_list("${NAME}-lv2" "lv2") else() dpf__set_module_export_list("${NAME}-lv2" "lv2-dsp") @@ -325,15 +356,15 @@ function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC) OUTPUT_NAME "${NAME}_dsp" PREFIX "") - if(DGL_LIBRARY) + if(HAS_UI) if(MONOLITHIC) - dpf__add_ui_main("${NAME}-lv2" "lv2" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-lv2" "lv2" "${HAS_UI}") target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-ui") set_target_properties("${NAME}-lv2" PROPERTIES OUTPUT_NAME "${NAME}") else() dpf__add_module("${NAME}-lv2-ui" ${_no_srcs}) - dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${HAS_UI}") dpf__set_module_export_list("${NAME}-lv2-ui" "lv2-ui") target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui") set_target_properties("${NAME}-lv2-ui" PROPERTIES @@ -361,12 +392,12 @@ endfunction() # # Add build rules for a VST2 plugin. # -function(dpf__build_vst2 NAME DGL_LIBRARY) +function(dpf__build_vst2 NAME HAS_UI) dpf__create_dummy_source_list(_no_srcs) dpf__add_module("${NAME}-vst2" ${_no_srcs}) dpf__add_plugin_main("${NAME}-vst2" "vst2") - dpf__add_ui_main("${NAME}-vst2" "vst2" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-vst2" "vst2" "${HAS_UI}") dpf__set_module_export_list("${NAME}-vst2" "vst2") target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-vst2" PROPERTIES @@ -439,14 +470,14 @@ endfunction() # # Add build rules for a VST3 plugin. # -function(dpf__build_vst3 NAME DGL_LIBRARY) +function(dpf__build_vst3 NAME HAS_UI) dpf__determine_vst3_package_architecture(vst3_arch) dpf__create_dummy_source_list(_no_srcs) dpf__add_module("${NAME}-vst3" ${_no_srcs}) dpf__add_plugin_main("${NAME}-vst3" "vst3") - dpf__add_ui_main("${NAME}-vst3" "vst3" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-vst3" "vst3" "${HAS_UI}") dpf__set_module_export_list("${NAME}-vst3" "vst3") target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-vst3" PROPERTIES @@ -481,12 +512,12 @@ endfunction() # # Add build rules for a VST2 plugin. # -function(dpf__build_clap NAME DGL_LIBRARY) +function(dpf__build_clap NAME HAS_UI) dpf__create_dummy_source_list(_no_srcs) dpf__add_module("${NAME}-clap" ${_no_srcs}) dpf__add_plugin_main("${NAME}-clap" "clap") - dpf__add_ui_main("${NAME}-clap" "clap" "${DGL_LIBRARY}") + dpf__add_ui_main("${NAME}-clap" "clap" "${HAS_UI}") dpf__set_module_export_list("${NAME}-clap" "clap") target_link_libraries("${NAME}-clap" PRIVATE "${NAME}-dsp" "${NAME}-ui") set_target_properties("${NAME}-clap" PROPERTIES @@ -532,6 +563,7 @@ function(dpf__add_dgl_cairo NO_SHARED_RESOURCES) "${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" + "${DPF_ROOT_DIR}/dgl/src/Layout.cpp" "${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp" "${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" "${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" @@ -597,6 +629,7 @@ function(dpf__add_dgl_opengl NO_SHARED_RESOURCES) "${DPF_ROOT_DIR}/dgl/src/Geometry.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" "${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp" + "${DPF_ROOT_DIR}/dgl/src/Layout.cpp" "${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp" "${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" "${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" @@ -661,34 +694,42 @@ function(dpf__add_dgl_system_libs) endif() add_library(dgl-system-libs INTERFACE) add_library(dgl-system-libs-definitions INTERFACE) - if(HAIKU) - target_link_libraries(dgl-system-libs INTERFACE "be") - elseif(WIN32) - target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32") - elseif(APPLE) + if(APPLE) find_library(APPLE_COCOA_FRAMEWORK "Cocoa") find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo") target_link_libraries(dgl-system-libs INTERFACE "${APPLE_COCOA_FRAMEWORK}" "${APPLE_COREVIDEO_FRAMEWORK}") + elseif(EMSCRIPTEN) + elseif(HAIKU) + target_link_libraries(dgl-system-libs INTERFACE "be") + elseif(WIN32) + target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32") else() + find_package(PkgConfig) + pkg_check_modules(DBUS "dbus-1") + if(DBUS_FOUND) + target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_DBUS") + target_include_directories(dgl-system-libs INTERFACE "${DBUS_INCLUDE_DIRS}") + target_link_libraries(dgl-system-libs INTERFACE "${DBUS_LIBRARIES}") + endif() find_package(X11 REQUIRED) + target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") - target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") if(X11_Xcursor_FOUND) - target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") + target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") endif() if(X11_Xext_FOUND) - target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") + target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") endif() if(X11_Xrandr_FOUND) - target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") + target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") endif() if(X11_XSync_FOUND) - target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") + target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") endif() endif() diff --git a/dpf/dgl/Color.hpp b/dpf/dgl/Color.hpp index 5ea28fe..f1b7bb3 100644 --- a/dpf/dgl/Color.hpp +++ b/dpf/dgl/Color.hpp @@ -94,6 +94,11 @@ struct Color { */ Color plus(float value) const noexcept; + /** + Create a new color based on this one but colors inverted. + */ + Color invert() const noexcept; + /** Create a color specified by hue, saturation and lightness. Values must in [0..1] range. diff --git a/dpf/dgl/EventHandlers.hpp b/dpf/dgl/EventHandlers.hpp index f56b5a6..98ee607 100644 --- a/dpf/dgl/EventHandlers.hpp +++ b/dpf/dgl/EventHandlers.hpp @@ -156,7 +156,7 @@ public: bool scrollEvent(const Widget::ScrollEvent& ev); protected: - State getState() const noexcept; + State getState() const noexcept; private: struct PrivateData; diff --git a/dpf/dgl/Makefile b/dpf/dgl/Makefile index c756bf0..8444c99 100644 --- a/dpf/dgl/Makefile +++ b/dpf/dgl/Makefile @@ -26,58 +26,66 @@ endif # --------------------------------------------------------------------------------------------------------------------- +ifeq ($(MODGUI_BUILD),true) +BUILD_DIR_SUFFIX = -modgui +endif + +BUILD_DIR = ../build$(BUILD_DIR_SUFFIX) + +# --------------------------------------------------------------------------------------------------------------------- + OBJS_common = \ - ../build/dgl/Application.cpp.o \ - ../build/dgl/ApplicationPrivateData.cpp.o \ - ../build/dgl/Color.cpp.o \ - ../build/dgl/EventHandlers.cpp.o \ - ../build/dgl/Geometry.cpp.o \ - ../build/dgl/ImageBase.cpp.o \ - ../build/dgl/ImageBaseWidgets.cpp.o \ - ../build/dgl/Layout.cpp.o \ - ../build/dgl/Resources.cpp.o \ - ../build/dgl/SubWidget.cpp.o \ - ../build/dgl/SubWidgetPrivateData.cpp.o \ - ../build/dgl/TopLevelWidget.cpp.o \ - ../build/dgl/TopLevelWidgetPrivateData.cpp.o \ - ../build/dgl/Widget.cpp.o \ - ../build/dgl/WidgetPrivateData.cpp.o \ - ../build/dgl/Window.cpp.o \ - ../build/dgl/WindowPrivateData.cpp.o + $(BUILD_DIR)/dgl/Application.cpp.o \ + $(BUILD_DIR)/dgl/ApplicationPrivateData.cpp.o \ + $(BUILD_DIR)/dgl/Color.cpp.o \ + $(BUILD_DIR)/dgl/EventHandlers.cpp.o \ + $(BUILD_DIR)/dgl/Geometry.cpp.o \ + $(BUILD_DIR)/dgl/ImageBase.cpp.o \ + $(BUILD_DIR)/dgl/ImageBaseWidgets.cpp.o \ + $(BUILD_DIR)/dgl/Layout.cpp.o \ + $(BUILD_DIR)/dgl/Resources.cpp.o \ + $(BUILD_DIR)/dgl/SubWidget.cpp.o \ + $(BUILD_DIR)/dgl/SubWidgetPrivateData.cpp.o \ + $(BUILD_DIR)/dgl/TopLevelWidget.cpp.o \ + $(BUILD_DIR)/dgl/TopLevelWidgetPrivateData.cpp.o \ + $(BUILD_DIR)/dgl/Widget.cpp.o \ + $(BUILD_DIR)/dgl/WidgetPrivateData.cpp.o \ + $(BUILD_DIR)/dgl/Window.cpp.o \ + $(BUILD_DIR)/dgl/WindowPrivateData.cpp.o # --------------------------------------------------------------------------------------------------------------------- OBJS_cairo = $(OBJS_common) \ - ../build/dgl/Cairo.cpp.cairo.o + $(BUILD_DIR)/dgl/Cairo.cpp.cairo.o ifeq ($(MACOS),true) -OBJS_cairo += ../build/dgl/pugl.mm.cairo.o +OBJS_cairo += $(BUILD_DIR)/dgl/pugl.mm.cairo.o else -OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o +OBJS_cairo += $(BUILD_DIR)/dgl/pugl.cpp.cairo.o endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl = $(OBJS_common) \ - ../build/dgl/OpenGL.cpp.opengl.o \ - ../build/dgl/NanoVG.cpp.opengl.o + $(BUILD_DIR)/dgl/OpenGL.cpp.opengl.o \ + $(BUILD_DIR)/dgl/NanoVG.cpp.opengl.o ifeq ($(MACOS),true) -OBJS_opengl += ../build/dgl/pugl.mm.opengl.o +OBJS_opengl += $(BUILD_DIR)/dgl/pugl.mm.opengl.o else -OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o +OBJS_opengl += $(BUILD_DIR)/dgl/pugl.cpp.opengl.o endif # --------------------------------------------------------------------------------------------------------------------- OBJS_opengl3 = $(OBJS_common) \ - ../build/dgl/OpenGL.cpp.opengl3.o \ - ../build/dgl/NanoVG.cpp.opengl3.o + $(BUILD_DIR)/dgl/OpenGL.cpp.opengl3.o \ + $(BUILD_DIR)/dgl/NanoVG.cpp.opengl3.o ifeq ($(MACOS),true) -OBJS_opengl3 += ../build/dgl/pugl.mm.opengl3.o +OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.mm.opengl3.o else -OBJS_opengl3 += ../build/dgl/pugl.cpp.opengl3.o +OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.cpp.opengl3.o endif # --------------------------------------------------------------------------------------------------------------------- @@ -85,170 +93,170 @@ endif OBJS_stub = $(OBJS_common) ifeq ($(MACOS),true) -OBJS_stub += ../build/dgl/pugl.mm.o +OBJS_stub += $(BUILD_DIR)/dgl/pugl.mm.o else -OBJS_stub += ../build/dgl/pugl.cpp.o +OBJS_stub += $(BUILD_DIR)/dgl/pugl.cpp.o endif # --------------------------------------------------------------------------------------------------------------------- OBJS_vulkan = $(OBJS_common) \ - ../build/dgl/Vulkan.cpp.vulkan.o + $(BUILD_DIR)/dgl/Vulkan.cpp.vulkan.o ifeq ($(MACOS),true) -OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o +OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.mm.vulkan.o else -OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o +OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.cpp.vulkan.o endif # --------------------------------------------------------------------------------------------------------------------- ifeq ($(HAVE_CAIRO),true) -TARGETS += ../build/libdgl-cairo.a +TARGETS += $(BUILD_DIR)/libdgl-cairo.a endif ifeq ($(HAVE_OPENGL),true) -TARGETS += ../build/libdgl-opengl.a +TARGETS += $(BUILD_DIR)/libdgl-opengl.a # Compat name, to be removed soon -TARGETS += ../build/libdgl.a +TARGETS += $(BUILD_DIR)/libdgl.a endif ifeq ($(HAVE_STUB),true) -TARGETS += ../build/libdgl-stub.a +TARGETS += $(BUILD_DIR)/libdgl-stub.a endif ifeq ($(HAVE_VULKAN),true) -TARGETS += ../build/libdgl-vulkan.a +TARGETS += $(BUILD_DIR)/libdgl-vulkan.a endif # --------------------------------------------------------------------------------------------------------------------- all: $(TARGETS) -cairo: ../build/libdgl-cairo.a -opengl: ../build/libdgl-opengl.a -opengl3: ../build/libdgl-opengl3.a -stub: ../build/libdgl-stub.a -vulkan: ../build/libdgl-vulkan.a +cairo: $(BUILD_DIR)/libdgl-cairo.a +opengl: $(BUILD_DIR)/libdgl-opengl.a +opengl3: $(BUILD_DIR)/libdgl-opengl3.a +stub: $(BUILD_DIR)/libdgl-stub.a +vulkan: $(BUILD_DIR)/libdgl-vulkan.a # --------------------------------------------------------------------------------------------------------------------- -../build/libdgl-cairo.a: $(OBJS_cairo) - -@mkdir -p ../build +$(BUILD_DIR)/libdgl-cairo.a: $(OBJS_cairo) + -@mkdir -p $(BUILD_DIR) @echo "Creating libdgl-cairo.a" $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ -../build/libdgl-opengl.a: $(OBJS_opengl) - -@mkdir -p ../build +$(BUILD_DIR)/libdgl-opengl.a: $(OBJS_opengl) + -@mkdir -p $(BUILD_DIR) @echo "Creating libdgl-opengl.a" $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ -../build/libdgl-opengl3.a: $(OBJS_opengl3) - -@mkdir -p ../build +$(BUILD_DIR)/libdgl-opengl3.a: $(OBJS_opengl3) + -@mkdir -p $(BUILD_DIR) @echo "Creating libdgl-opengl3.a" $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ -../build/libdgl-stub.a: $(OBJS_stub) - -@mkdir -p ../build +$(BUILD_DIR)/libdgl-stub.a: $(OBJS_stub) + -@mkdir -p $(BUILD_DIR) @echo "Creating libdgl-stub.a" $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ -../build/libdgl-vulkan.a: $(OBJS_vulkan) - -@mkdir -p ../build +$(BUILD_DIR)/libdgl-vulkan.a: $(OBJS_vulkan) + -@mkdir -p $(BUILD_DIR) @echo "Creating libdgl-vulkan.a" $(SILENT)rm -f $@ $(SILENT)$(AR) crs $@ $^ # Compat name, to be removed soon -../build/libdgl.a: ../build/libdgl-opengl.a +$(BUILD_DIR)/libdgl.a: $(BUILD_DIR)/libdgl-opengl.a @echo "Symlinking libdgl.a" $(SILENT)ln -sf $< $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.c.o: src/%.c - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.c.o: src/%.c + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $<" $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ -../build/dgl/%.cpp.o: src/%.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.cpp.o: src/%.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -../build/dgl/%.mm.o: src/%.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.mm.o: src/%.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/pugl.cpp.o: src/pugl.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/pugl.cpp.o: src/pugl.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -o $@ -../build/dgl/pugl.mm.o: src/pugl.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/pugl.mm.o: src/pugl.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $<" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.cairo.o: src/%.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.cpp.cairo.o: src/%.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ -../build/dgl/%.mm.cairo.o: src/%.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.mm.cairo.o: src/%.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (Cairo variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.opengl.o: src/%.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.cpp.opengl.o: src/%.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ -../build/dgl/%.mm.opengl.o: src/%.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.mm.opengl.o: src/%.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (OpenGL variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.opengl3.o: src/%.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.cpp.opengl3.o: src/%.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (OpenGL3 variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ -../build/dgl/%.mm.opengl3.o: src/%.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.mm.opengl3.o: src/%.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (OpenGL3 variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- -../build/dgl/%.cpp.vulkan.o: src/%.cpp - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.cpp.vulkan.o: src/%.cpp + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (Vulkan variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ -../build/dgl/%.mm.vulkan.o: src/%.mm - -@mkdir -p ../build/dgl +$(BUILD_DIR)/dgl/%.mm.vulkan.o: src/%.mm + -@mkdir -p $(BUILD_DIR)/dgl @echo "Compiling $< (Vulkan variant)" $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ # --------------------------------------------------------------------------------------------------------------------- clean: - rm -rf ../build/dgl ../build/libdgl*.* + rm -rf $(BUILD_DIR)/dgl $(BUILD_DIR)/libdgl*.* debug: $(MAKE) DEBUG=true diff --git a/dpf/dgl/src/ApplicationPrivateData.cpp b/dpf/dgl/src/ApplicationPrivateData.cpp index ca06a1b..0d3323d 100644 --- a/dpf/dgl/src/ApplicationPrivateData.cpp +++ b/dpf/dgl/src/ApplicationPrivateData.cpp @@ -67,9 +67,11 @@ Application::PrivateData::PrivateData(const bool standalone) DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); puglSetWorldHandle(world, this); -#ifndef __EMSCRIPTEN__ + #ifdef __EMSCRIPTEN__ + puglSetClassName(world, "canvas"); + #else puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); -#endif + #endif } Application::PrivateData::~PrivateData() diff --git a/dpf/dgl/src/Color.cpp b/dpf/dgl/src/Color.cpp index 4c8714f..df77968 100644 --- a/dpf/dgl/src/Color.cpp +++ b/dpf/dgl/src/Color.cpp @@ -163,6 +163,15 @@ Color Color::plus(const float value) const noexcept return color; } +Color Color::invert() const noexcept +{ + Color color(*this); + color.red = 1.f - color.red; + color.green = 1.f - color.green; + color.blue = 1.f - color.blue; + return color; +} + Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) { float m1, m2; diff --git a/dpf/dgl/src/EventHandlers.cpp b/dpf/dgl/src/EventHandlers.cpp index 2b75b5f..3b5536a 100644 --- a/dpf/dgl/src/EventHandlers.cpp +++ b/dpf/dgl/src/EventHandlers.cpp @@ -441,7 +441,7 @@ struct KnobEventHandler::PrivateData { } if (d_isZero(movDiff)) - return false; + return true; const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; valueTmp += (maximum - minimum) / divisor * movDiff; diff --git a/dpf/dgl/src/pugl-custom/pugl.h b/dpf/dgl/src/pugl-custom/pugl.h deleted file mode 100644 index 20a3814..0000000 --- a/dpf/dgl/src/pugl-custom/pugl.h +++ /dev/null @@ -1,492 +0,0 @@ -/* - Copyright 2012-2014 David Robillard - Copyright 2012-2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl.h API for Pugl, a minimal portable API for OpenGL. -*/ - -#ifndef PUGL_H_INCLUDED -#define PUGL_H_INCLUDED - -#include - -/* - This API is pure portable C and contains no platform specific elements, or - even a GL dependency. However, unfortunately GL includes vary across - platforms so they are included here to allow for pure portable programs. -*/ -#ifdef __APPLE__ -# include -#else -# ifdef _WIN32 -# include -# include /* Broken Windows GL headers require this */ -# endif -# include -#endif - -#ifdef __cplusplus -extern "C" { -#else -# include -#endif - -/** - @defgroup pugl Pugl - A minimal portable API for OpenGL. - @{ -*/ - -/** - A Pugl view. -*/ -typedef struct PuglViewImpl PuglView; - -/** - A native window handle. - - On X11, this is a Window. - On OSX, this is an NSView*. - On Windows, this is a HWND. -*/ -typedef intptr_t PuglNativeWindow; - -/** - Return status code. -*/ -typedef enum { - PUGL_SUCCESS = 0 -} PuglStatus; - -/** - Convenience symbols for ASCII control characters. -*/ -typedef enum { - PUGL_CHAR_BACKSPACE = 0x08, - PUGL_CHAR_ESCAPE = 0x1B, - PUGL_CHAR_DELETE = 0x7F -} PuglChar; - -/** - Special (non-Unicode) keyboard keys. -*/ -typedef enum { - PUGL_KEY_F1 = 1, - PUGL_KEY_F2, - PUGL_KEY_F3, - PUGL_KEY_F4, - PUGL_KEY_F5, - PUGL_KEY_F6, - PUGL_KEY_F7, - PUGL_KEY_F8, - PUGL_KEY_F9, - PUGL_KEY_F10, - PUGL_KEY_F11, - PUGL_KEY_F12, - PUGL_KEY_LEFT, - PUGL_KEY_UP, - PUGL_KEY_RIGHT, - PUGL_KEY_DOWN, - PUGL_KEY_PAGE_UP, - PUGL_KEY_PAGE_DOWN, - PUGL_KEY_HOME, - PUGL_KEY_END, - PUGL_KEY_INSERT, - PUGL_KEY_SHIFT, - PUGL_KEY_CTRL, - PUGL_KEY_ALT, - PUGL_KEY_SUPER -} PuglKey; - -/** - Keyboard modifier flags. -*/ -typedef enum { - PUGL_MOD_SHIFT = 1 << 0, /**< Shift key */ - PUGL_MOD_CTRL = 1 << 1, /**< Control key */ - PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */ - PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */ -} PuglMod; - -/** - Handle for opaque user data. -*/ -typedef void* PuglHandle; - -/** - A function called when the window is closed. -*/ -typedef void (*PuglCloseFunc)(PuglView* view); - -/** - A function called to draw the view contents with OpenGL. -*/ -typedef void (*PuglDisplayFunc)(PuglView* view); - -/** - A function called when a key is pressed or released. - @param view The view the event occured in. - @param press True if the key was pressed, false if released. - @param key Unicode point of the key pressed. - @return 0 if event was handled, otherwise send event to parent window. -*/ -typedef int (*PuglKeyboardFunc)(PuglView* view, bool press, uint32_t key); - -/** - A function called when the pointer moves. - @param view The view the event occured in. - @param x The window-relative x coordinate of the pointer. - @param y The window-relative y coordinate of the pointer. -*/ -typedef void (*PuglMotionFunc)(PuglView* view, int x, int y); - -/** - A function called when a mouse button is pressed or released. - @param view The view the event occured in. - @param button The button number (1 = left, 2 = middle, 3 = right). - @param press True if the key was pressed, false if released. - @param x The window-relative x coordinate of the pointer. - @param y The window-relative y coordinate of the pointer. -*/ -typedef void (*PuglMouseFunc)( - PuglView* view, int button, bool press, int x, int y); - -/** - A function called when the view is resized. - @param view The view being resized. - @param width The new view width. - @param height The new view height. -*/ -typedef void (*PuglReshapeFunc)(PuglView* view, int width, int height); - -/** - A function called outside of gl-context when the plugin schedules a resize via puglPostResize. - - @param view The view being resized. - @param width The new width to resize to (variable is initialized to current size) - @param height The new height to resize to (variable is initialized to current size) - @param set_hints If not null, set window-hints - */ -typedef void (*PuglResizeFunc)(PuglView* view, int *width, int *height, int *set_hints); - -/** - A function called on scrolling (e.g. mouse wheel or track pad). - - The distances used here are in "lines", a single tick of a clicking mouse - wheel. For example, @p dy = 1.0 scrolls 1 line up. Some systems and - devices support finer resolution and/or higher values for fast scrolls, - so programs should handle any value gracefully. - - @param view The view being scrolled. - @param x The window-relative x coordinate of the pointer. - @param y The window-relative y coordinate of the pointer. - @param dx The scroll x distance. - @param dx The scroll y distance. -*/ -typedef void (*PuglScrollFunc)(PuglView* view, int x, int y, float dx, float dy); - -/** - A function called when a special key is pressed or released. - - This callback allows the use of keys that do not have unicode points. - Note that some are non-printable keys. - - @param view The view the event occured in. - @param press True if the key was pressed, false if released. - @param key The key pressed. - @return 0 if event was handled, otherwise send event to parent window. -*/ -typedef int (*PuglSpecialFunc)(PuglView* view, bool press, PuglKey key); - -/** - A function called when a filename is selected via file-browser. - - @param view The view the event occured in. - @param filename The selected file name or NULL if the dialog was canceled. -*/ -typedef void (*PuglFileSelectedFunc)(PuglView* view, const char* filename); - -/** - @name Initialization - Configuration functions which must be called before creating a window. - @{ -*/ - -/** - Create a Pugl context. - - To create a window, call the various puglInit* functions as necessary, then - call puglCreateWindow(). -*/ -PuglView* -puglInit(void); - -/** - Set the parent window before creating a window (for embedding). -*/ -void -puglInitWindowParent(PuglView* view, PuglNativeWindow parent); - -/** - Set the window size before creating a window. -*/ -void -puglInitWindowSize(PuglView* view, int width, int height); - -/** - Set the minimum window size before creating a window. -*/ -void -puglInitWindowMinSize(PuglView* view, int width, int height); - -/** - Enable or disable resizing before creating a window. -*/ -void -puglInitUserResizable(PuglView* view, bool resizable); - -/** - Set transient parent before creating a window. - - On X11, parent_id must be a Window. - On OSX, parent_id must be an NSView*. -*/ -void -puglInitTransientFor(PuglView* view, uintptr_t parent); - -/** - @} -*/ - -/** - @name Windows - Window management functions. - @{ -*/ - -/** - Create a window with the settings given by the various puglInit functions. - - @return 1 (pugl does not currently support multiple windows). -*/ -int -puglCreateWindow(PuglView* view, const char* title); - -/** - Create a new GL window. - @param parent Parent window, or 0 for top level. - @param title Window title, or NULL. - @param width Window width in pixels. - @param height Window height in pixels. - @param resizable Whether window should be user resizable. -*/ -PuglView* -puglCreate(PuglNativeWindow parent, - const char* title, - int min_width, - int min_height, - int width, - int height, - bool resizable, - unsigned long transientId); - -/** - Show Window (external ui) -*/ -void -puglShowWindow(PuglView* view); - -/** - Hide Window (external ui) -*/ -void -puglHideWindow(PuglView* view); - -/** - Return the native window handle. -*/ -PuglNativeWindow -puglGetNativeWindow(PuglView* view); - -/** - @} -*/ - -/** - Set the handle to be passed to all callbacks. - - This is generally a pointer to a struct which contains all necessary state. - Everything needed in callbacks should be here, not in static variables. - - Note the lack of this facility makes GLUT unsuitable for plugins or - non-trivial programs; this mistake is largely why Pugl exists. -*/ -void -puglSetHandle(PuglView* view, PuglHandle handle); - -/** - Get the handle to be passed to all callbacks. -*/ -PuglHandle -puglGetHandle(PuglView* view); - -/** - Get the drawing context. - For Cairo contexts, this returns a pointer to a cairo_t. - For everything else, this is unused and returns NULL. -*/ -void* -puglGetContext(PuglView* view); - -/** - Return the timestamp (if any) of the currently-processing event. -*/ -uint32_t -puglGetEventTimestamp(PuglView* view); - -/** - Get the currently active modifiers (PuglMod flags). - - This should only be called from an event handler. -*/ -int -puglGetModifiers(PuglView* view); - -/** - Ignore synthetic repeated key events. -*/ -void -puglIgnoreKeyRepeat(PuglView* view, bool ignore); - -/** - @name Event Callbacks - Functions to set event callbacks for handling user input. - @{ -*/ - -/** - Set the function to call when the window is closed. -*/ -void -puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc); - -/** - Set the display function which should draw the UI using GL. -*/ -void -puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc); - -/** - Set the function to call on keyboard events. -*/ -void -puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc); - -/** - Set the function to call on mouse motion. -*/ -void -puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc); - -/** - Set the function to call on mouse button events. -*/ -void -puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc); - -/** - Set the function to call on scroll events. -*/ -void -puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc); - -/** - Set the function to call on special events. -*/ -void -puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc); - -/** - Set the function to call when the window size changes. -*/ -void -puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc); - -/** - Set callback function to change window size. -*/ -void -puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc); - -/** - Set the function to call on file-browser selections. -*/ -void -puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc); - -/** - @} -*/ - -/** - TODO document this. - */ -int -puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect); - -/** - Grab the input focus. -*/ -void -puglGrabFocus(PuglView* view); - -/** - Process all pending window events. - - This handles input events as well as rendering, so it should be called - regularly and rapidly enough to keep the UI responsive. -*/ -PuglStatus -puglProcessEvents(PuglView* view); - -/** - Request a redisplay on the next call to puglProcessEvents(). -*/ -void -puglPostRedisplay(PuglView* view); - -/** - Request a resize on the next call to puglProcessEvents(). -*/ -void -puglPostResize(PuglView* view); - -/** - Destroy a GL window. -*/ -void -puglDestroy(PuglView* view); - -/** - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* PUGL_H_INCLUDED */ diff --git a/dpf/dgl/src/pugl-custom/pugl_haiku.cpp b/dpf/dgl/src/pugl-custom/pugl_haiku.cpp deleted file mode 100644 index d7f8dca..0000000 --- a/dpf/dgl/src/pugl-custom/pugl_haiku.cpp +++ /dev/null @@ -1,441 +0,0 @@ -/* - Copyright 2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_haiku.cpp BeOS/HaikuOS Pugl Implementation. -*/ - -#include -#include - -#ifdef PUGL_CAIRO -#include -typedef BView BViewType; -#endif -#ifdef PUGL_OPENGL -#include -#include -typedef BGLView BViewType; -#endif - -#include "pugl_internal.h" - -class DWindow; - -struct PuglInternalsImpl { - BApplication* app; - BViewType* view; - DWindow* window; -}; - -static void -puglReshape(PuglView* view, int width, int height) -{ - puglEnterContext(view); - - if (view->reshapeFunc) { - view->reshapeFunc(view, width, height); - } else { - puglDefaultReshape(width, height); - } - - puglLeaveContext(view, false); - - view->width = width; - view->height = height; -} - -static void -puglDisplay(PuglView* view) -{ - puglEnterContext(view); - - view->redisplay = false; - if (view->displayFunc) { - view->displayFunc(view); - } - - puglLeaveContext(view, true); -} - -void -puglEnterContext(PuglView* view) -{ - PuglInternals* impl = view->impl; - -#ifdef PUGL_OPENGL - // FIXME without the first unlock we freeze - impl->view->UnlockGL(); - impl->view->LockGL(); -#endif -} - -void -puglLeaveContext(PuglView* view, bool flush) -{ - PuglInternals* impl = view->impl; - -#ifdef PUGL_OPENGL - if (flush) - impl->view->SwapBuffers(); - - impl->view->UnlockGL(); -#endif -} - -PuglInternals* -puglInitInternals() -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -class DView : public BViewType -{ -public: -#ifdef PUGL_CAIRO - DView(PuglView* const v) - : BView(nullptr, - B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE), - puglView(v) {} -#endif - -#ifdef PUGL_OPENGL - DView(PuglView* const v) - : BGLView(BRect(), // causes "bitmap bounds is much too large: BRect(0.0, 0.0, 4294967296.0, 4294967296.0)" - "DPF-GLView", - 0x0, // resize mode - B_FULL_UPDATE_ON_RESIZE|B_WILL_DRAW|B_NAVIGABLE_JUMP|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE, - BGL_RGB|BGL_DOUBLE|BGL_ALPHA|BGL_DEPTH|BGL_STENCIL), - puglView(v) - { - } -#endif - -protected: - void GetPreferredSize(float* width, float* height) override - { - d_stdout("%s %i", __func__, __LINE__); - if (width != nullptr) - *width = puglView->width; - if (height != nullptr) - *height = puglView->height; - d_stdout("%s %i", __func__, __LINE__); - } - - void Draw(BRect updateRect) override - { - d_stdout("%s %i", __func__, __LINE__); - puglDisplay(puglView); -#ifdef PUGL_OPENGL - BGLView::Draw(updateRect); - d_stdout("%s %i", __func__, __LINE__); -#endif - } - - void MessageReceived(BMessage* message) - { - d_stdout("MessageReceived %p", message); - BViewType::MessageReceived(message); - } - - void MouseDown(BPoint where) override - { - if (puglView->mouseFunc) { - // puglView->event_timestamp_ms = GetMessageTime(); - d_stdout("MouseDown mask %u", EventMask()); - puglView->mouseFunc(puglView, 1, true, where.x, where.y); - SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); - } - //BViewType::MouseDown(where); - } - - void MouseUp(BPoint where) override - { - if (puglView->mouseFunc) { - d_stdout("MouseUp mask %u", EventMask()); - // puglView->event_timestamp_ms = GetMessageTime(); - puglView->mouseFunc(puglView, 1, false, where.x, where.y); - } - //BViewType::MouseUp(where); - } - - void MouseMoved(BPoint where, uint32, const BMessage*) override - { - if (puglView->motionFunc) { - // puglView->event_timestamp_ms = GetMessageTime(); - puglView->motionFunc(puglView, where.x, where.y); - } - } - - void KeyDown(const char* bytes, int32 numBytes) override - { - d_stdout("KeyDown %i", numBytes); - if (numBytes != 1) - return; // TODO - - if (puglView->keyboardFunc) { - puglView->keyboardFunc(puglView, true, bytes[0]); - } - } - - void KeyUp(const char* bytes, int32 numBytes) override - { - d_stdout("KeyUp %i", numBytes); - if (numBytes != 1) - return; // TODO - - if (puglView->keyboardFunc) { - puglView->keyboardFunc(puglView, false, bytes[0]); - } - } - - void ScrollTo(BPoint where) override - { - d_stdout("ScrollTo mask %u", EventMask()); - BViewType::ScrollTo(where); - } - - void FrameResized(float newWidth, float newHeight) override - { - d_stdout("%s %i", __func__, __LINE__); - puglReshape(puglView, static_cast(newWidth), static_cast(newHeight)); -#ifdef PUGL_OPENGL - BGLView::FrameResized(newWidth, newHeight); -#endif - d_stdout("%s %i", __func__, __LINE__); - } - -private: - PuglView* const puglView; -}; - -class DWindow : public BWindow -{ -public: - DWindow(PuglView* const v) - : BWindow(BRect(1.0f), "DPF-Window", B_TITLED_WINDOW, 0x0), - puglView(v), - needsQuit(true) - { - } - - bool NeedsQuit() const - { - return needsQuit; - } - -protected: - bool QuitRequested() override - { - d_stdout("%s %i", __func__, __LINE__); - if (puglView->closeFunc) { - puglView->closeFunc(puglView); - puglView->redisplay = false; - } - needsQuit = false; - d_stdout("%s %i", __func__, __LINE__); - return true; - } - -private: - PuglView* const puglView; - bool needsQuit; -}; - -int -puglCreateWindow(PuglView* view, const char* title) -{ - PuglInternals* impl = view->impl; - - if (be_app == nullptr) - { - d_stdout("creating app"); - status_t status; - BApplication* const app = new BApplication("application/x-vnd.dpf-application", &status); - - if (status != B_OK) - { - d_stdout("app status error %u", status); - delete app; - return 1; - } - - impl->app = app; - } - else - { - d_stdout("using existing app"); - } - - if (view->parent == 0) { - impl->window = new DWindow(view); - impl->window->Lock(); - } - - impl->view = new DView(view); - - if (view->parent != 0) { - BView* const pview = (BView*)view->parent; - pview->AddChild(impl->view); - impl->view->LockGL(); - return 0; - } - - if (title != nullptr) { - impl->window->SetTitle(title); - } - - impl->window->AddChild(impl->view); - impl->view->LockGL(); - //puglEnterContext(view); - impl->window->Unlock(); - return 0; -} - -void -puglShowWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - if (impl->window != nullptr) - { - if (impl->window->LockLooper()) - { - impl->window->Show(); - impl->window->UnlockLooper(); - } - } - else - { - impl->view->Show(); - } -} - -void -puglHideWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - if (impl->window != nullptr) - { - if (impl->window->LockLooper()) - { - impl->window->Hide(); - impl->window->UnlockLooper(); - } - } - else - { - impl->view->Show(); - } -} - -void -puglDestroy(PuglView* view) -{ - PuglInternals* impl = view->impl; - - if (impl->window != nullptr) - { - // impl->window->Lock(); - puglLeaveContext(view, false); - impl->window->RemoveChild(impl->view); - // impl->window->Unlock(); - - if (impl->window->NeedsQuit()) - impl->window->Quit(); - } - - delete impl->view; - impl->view = nullptr; - impl->window = nullptr; - - if (impl->app != nullptr && impl->app->CountWindows() == 0) - { - d_stdout("deleting app"); - delete impl->app; - impl->app = nullptr; - } else - d_stdout("NOT deleting app"); -} - -PuglStatus -puglProcessEvents(PuglView* view) -{ - return PUGL_SUCCESS; -} - -void -puglPostRedisplay(PuglView* view) -{ - PuglInternals* impl = view->impl; - - view->redisplay = true; - - if (impl->window != nullptr) - { - if (impl->window->LockLooper()) - { - impl->view->Invalidate(); - impl->window->UnlockLooper(); - } - } - else - { - impl->view->Invalidate(); - } -} - -PuglNativeWindow -puglGetNativeWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - -#ifdef PUGL_OPENGL - // return (PuglNativeWindow)impl->view->EmbeddedView(); -#endif - - return (PuglNativeWindow)(BView*)impl->view; -} - -void* -puglGetContext(PuglView* view) -{ - return NULL; -} - -int -puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) -{ - PuglInternals* impl = view->impl; - - d_stdout("puglUpdateGeometryConstraints %i %i %i %i", min_width, min_height, view->width, view->height); - if (impl->window->LockLooper()) - { - impl->window->SetSizeLimits(min_width, - view->user_resizable ? 4096 : min_width, - min_height, - view->user_resizable ? 4096 : min_height); - - impl->window->UnlockLooper(); - return 0; - } - - return 1; - - // TODO - (void)aspect; -} diff --git a/dpf/dgl/src/pugl-custom/pugl_internal.h b/dpf/dgl/src/pugl-custom/pugl_internal.h deleted file mode 100644 index 609c97b..0000000 --- a/dpf/dgl/src/pugl-custom/pugl_internal.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - Copyright 2012-2014 David Robillard - Copyright 2012-2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_internal.h Private platform-independent definitions. - - Note this file contains function definitions, so it must be compiled into - the final binary exactly once. Each platform specific implementation file - including it once should achieve this. -*/ - -#include "pugl.h" - -typedef struct PuglInternalsImpl PuglInternals; - -struct PuglViewImpl { - PuglHandle handle; - PuglCloseFunc closeFunc; - PuglDisplayFunc displayFunc; - PuglKeyboardFunc keyboardFunc; - PuglMotionFunc motionFunc; - PuglMouseFunc mouseFunc; - PuglReshapeFunc reshapeFunc; - PuglResizeFunc resizeFunc; - PuglScrollFunc scrollFunc; - PuglSpecialFunc specialFunc; - PuglFileSelectedFunc fileSelectedFunc; - - PuglInternals* impl; - PuglNativeWindow parent; - uintptr_t transient_parent; - - int width; - int height; - int min_width; - int min_height; - int mods; - bool mouse_in_view; - bool ignoreKeyRepeat; - bool redisplay; - bool user_resizable; - bool pending_resize; - uint32_t event_timestamp_ms; -}; - -PuglInternals* puglInitInternals(void); - -PuglView* -puglInit(void) -{ - PuglView* view = (PuglView*)calloc(1, sizeof(PuglView)); - if (!view) { - return NULL; - } - - PuglInternals* impl = puglInitInternals(); - if (!impl) { - free(view); - return NULL; - } - - view->impl = impl; - view->width = 640; - view->height = 480; - - return view; -} - -void -puglInitWindowSize(PuglView* view, int width, int height) -{ - view->width = width; - view->height = height; -} - -void -puglInitWindowMinSize(PuglView* view, int width, int height) -{ - view->min_width = width; - view->min_height = height; -} - -void -puglInitWindowParent(PuglView* view, PuglNativeWindow parent) -{ - view->parent = parent; -} - -void -puglInitUserResizable(PuglView* view, bool resizable) -{ - view->user_resizable = resizable; -} - -void -puglInitTransientFor(PuglView* view, uintptr_t parent) -{ - view->transient_parent = parent; -} - -PuglView* -puglCreate(PuglNativeWindow parent, - const char* title, - int min_width, - int min_height, - int width, - int height, - bool resizable, - unsigned long transientId) -{ - PuglView* view = puglInit(); - if (!view) { - return NULL; - } - - puglInitWindowParent(view, parent); - puglInitWindowMinSize(view, min_width, min_height); - puglInitWindowSize(view, width, height); - puglInitUserResizable(view, resizable); - puglInitTransientFor(view, transientId); - - if (!puglCreateWindow(view, title)) { - free(view); - return NULL; - } - - return view; -} - -void -puglSetHandle(PuglView* view, PuglHandle handle) -{ - view->handle = handle; -} - -PuglHandle -puglGetHandle(PuglView* view) -{ - return view->handle; -} - -uint32_t -puglGetEventTimestamp(PuglView* view) -{ - return view->event_timestamp_ms; -} - -int -puglGetModifiers(PuglView* view) -{ - return view->mods; -} - -void -puglIgnoreKeyRepeat(PuglView* view, bool ignore) -{ - view->ignoreKeyRepeat = ignore; -} - -void -puglSetCloseFunc(PuglView* view, PuglCloseFunc closeFunc) -{ - view->closeFunc = closeFunc; -} - -void -puglSetDisplayFunc(PuglView* view, PuglDisplayFunc displayFunc) -{ - view->displayFunc = displayFunc; -} - -void -puglSetKeyboardFunc(PuglView* view, PuglKeyboardFunc keyboardFunc) -{ - view->keyboardFunc = keyboardFunc; -} - -void -puglSetMotionFunc(PuglView* view, PuglMotionFunc motionFunc) -{ - view->motionFunc = motionFunc; -} - -void -puglSetMouseFunc(PuglView* view, PuglMouseFunc mouseFunc) -{ - view->mouseFunc = mouseFunc; -} - -void -puglSetReshapeFunc(PuglView* view, PuglReshapeFunc reshapeFunc) -{ - view->reshapeFunc = reshapeFunc; -} - -void -puglSetResizeFunc(PuglView* view, PuglResizeFunc resizeFunc) -{ - view->resizeFunc = resizeFunc; -} - -void -puglSetScrollFunc(PuglView* view, PuglScrollFunc scrollFunc) -{ - view->scrollFunc = scrollFunc; -} - -void -puglSetSpecialFunc(PuglView* view, PuglSpecialFunc specialFunc) -{ - view->specialFunc = specialFunc; -} - -void -puglSetFileSelectedFunc(PuglView* view, PuglFileSelectedFunc fileSelectedFunc) -{ - view->fileSelectedFunc = fileSelectedFunc; -} - -void -puglEnterContext(PuglView* view); - -void -puglLeaveContext(PuglView* view, bool flush); - -static void -puglDefaultReshape(int width, int height) -{ -#ifdef PUGL_OPENGL -#ifdef ROBTK_HERE - glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#else - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, width, height, 0, 0, 1); - glViewport(0, 0, width, height); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -#endif -#endif // PUGL_OPENGL -} diff --git a/dpf/dgl/src/pugl-custom/pugl_osx.m b/dpf/dgl/src/pugl-custom/pugl_osx.m deleted file mode 100644 index da59538..0000000 --- a/dpf/dgl/src/pugl-custom/pugl_osx.m +++ /dev/null @@ -1,974 +0,0 @@ -/* - Copyright 2012 David Robillard - Copyright 2012-2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_osx.m OSX/Cocoa Pugl Implementation. -*/ - -#include - -#ifdef PUGL_CAIRO -#import -#import -#endif -#import - -#include "pugl_internal.h" - -@interface PuglWindow : NSWindow -{ -@public - PuglView* puglview; -} - -- (id) initWithContentRect:(NSRect)contentRect - styleMask:(unsigned int)aStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)flag; -- (void) setPuglview:(PuglView*)view; -- (BOOL) canBecomeKeyWindow; -- (BOOL) windowShouldClose:(id)sender; -@end - -@implementation PuglWindow - -- (id)initWithContentRect:(NSRect)contentRect - styleMask:(unsigned int)aStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)flag -{ - NSWindow* result = [super initWithContentRect:contentRect - styleMask:(NSClosableWindowMask | - NSTitledWindowMask | - NSResizableWindowMask) - backing:NSBackingStoreBuffered defer:NO]; - - [result setAcceptsMouseMovedEvents:YES]; - [result setLevel: CGShieldingWindowLevel() + 1]; - - return (PuglWindow*)result; - - // unused - (void)aStyle; (void)bufferingType; (void)flag; -} - -- (void)setPuglview:(PuglView*)view -{ - puglview = view; - [self setContentSize:NSMakeSize(view->width, view->height)]; -} - -- (BOOL)canBecomeKeyWindow -{ - return YES; -} - -- (BOOL)windowShouldClose:(id)sender -{ - if (puglview->closeFunc) - puglview->closeFunc(puglview); - return YES; - - // unused - (void)sender; -} - -@end - -static void -puglDisplay(PuglView* view) -{ - view->redisplay = false; - if (view->displayFunc) { - view->displayFunc(view); - } -} - -@protocol PuglGenericView -@required -- (PuglView *) puglview; -- (void) setPuglview:(PuglView *)pv; -- (NSTrackingArea *) puglTrackingArea; -- (void) setPuglTrackingArea:(NSTrackingArea *)area; -@end - -static unsigned -getModifiers(PuglView* view, NSEvent* ev) -{ - const unsigned modifierFlags = [ev modifierFlags]; - - view->event_timestamp_ms = fmod([ev timestamp] * 1000.0, UINT32_MAX); - - unsigned mods = 0; - mods |= (modifierFlags & NSShiftKeyMask) ? PUGL_MOD_SHIFT : 0; - mods |= (modifierFlags & NSControlKeyMask) ? PUGL_MOD_CTRL : 0; - mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT : 0; - mods |= (modifierFlags & NSCommandKeyMask) ? PUGL_MOD_SUPER : 0; - return mods; -} - -static int -getFixedAppKitButton(NSInteger button) -{ - switch (button) { - case 0: return 1; - case 1: return 3; - case 2: return 2; - default: return button; - } -} - -static void -cursorUpdate(NSView *self, NSEvent* event) -{ - [[NSCursor arrowCursor] set]; - (void)self; - (void)event; -} - -static void -updateTrackingAreas(NSView *self) -{ - static const int opts = NSTrackingMouseEnteredAndExited - | NSTrackingMouseMoved - | NSTrackingEnabledDuringMouseDrag - | NSTrackingInVisibleRect - | NSTrackingActiveAlways - | NSTrackingCursorUpdate; - - NSTrackingArea *trackingArea = [self puglTrackingArea]; - if (trackingArea != nil) { - [self removeTrackingArea:trackingArea]; - [trackingArea release]; - } - - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] - options:opts - owner:self - userInfo:nil]; - [self setPuglTrackingArea:trackingArea]; - [self addTrackingArea:trackingArea]; -} - -static void -viewWillMoveToWindow(NSView *self, NSWindow* newWindow) -{ - if (newWindow != nil) { - [newWindow setAcceptsMouseMovedEvents:YES]; - [newWindow makeFirstResponder:self]; - } -} - -static void -reshape(NSView *self) -{ - PuglView* puglview = [self puglview]; - - NSRect bounds = [self bounds]; - int width = bounds.size.width; - int height = bounds.size.height; - - puglEnterContext(puglview); - - if (puglview->reshapeFunc) { - puglview->reshapeFunc(puglview, width, height); - } else { - puglDefaultReshape(width, height); - } - - puglLeaveContext(puglview, false); - - puglview->width = width; - puglview->height = height; -} - -static void -mouseMoved(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->motionFunc) { - NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; - puglview->mods = getModifiers(puglview, event); - puglview->motionFunc(puglview, loc.x, loc.y); - } -} - -static void -mouseDown(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->mouseFunc) { - NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; - puglview->mods = getModifiers(puglview, event); - puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), true, loc.x, loc.y); - } -} - -static void -mouseUp(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->mouseFunc) { - NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; - puglview->mods = getModifiers(puglview, event); - puglview->mouseFunc(puglview, getFixedAppKitButton([event buttonNumber]), false, loc.x, loc.y); - } -} - -static void -scrollWheel(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->scrollFunc) { - NSPoint loc = [self convertPoint:[event locationInWindow] fromView:nil]; - puglview->mods = getModifiers(puglview, event); - puglview->scrollFunc(puglview, - loc.x, loc.y, - [event deltaX], [event deltaY]); - } -} - -static void -keyDown(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->keyboardFunc && !(puglview->ignoreKeyRepeat && [event isARepeat])) { - NSString* chars = [event characters]; - puglview->mods = getModifiers(puglview, event); - puglview->keyboardFunc(puglview, true, [chars characterAtIndex:0]); - } -} - -static void -keyUp(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->keyboardFunc) { - NSString* chars = [event characters]; - puglview->mods = getModifiers(puglview, event); - puglview->keyboardFunc(puglview, false, [chars characterAtIndex:0]); - } -} - -static void -flagsChanged(NSView *self, NSEvent *event) -{ - PuglView* puglview = [self puglview]; - - if (puglview->specialFunc) { - const unsigned mods = getModifiers(puglview, event); - if ((mods & PUGL_MOD_SHIFT) != (puglview->mods & PUGL_MOD_SHIFT)) { - puglview->specialFunc(puglview, mods & PUGL_MOD_SHIFT, PUGL_KEY_SHIFT); - } else if ((mods & PUGL_MOD_CTRL) != (puglview->mods & PUGL_MOD_CTRL)) { - puglview->specialFunc(puglview, mods & PUGL_MOD_CTRL, PUGL_KEY_CTRL); - } else if ((mods & PUGL_MOD_ALT) != (puglview->mods & PUGL_MOD_ALT)) { - puglview->specialFunc(puglview, mods & PUGL_MOD_ALT, PUGL_KEY_ALT); - } else if ((mods & PUGL_MOD_SUPER) != (puglview->mods & PUGL_MOD_SUPER)) { - puglview->specialFunc(puglview, mods & PUGL_MOD_SUPER, PUGL_KEY_SUPER); - } - puglview->mods = mods; - } -} - -#ifdef PUGL_OPENGL -@interface PuglOpenGLView : NSOpenGLView -{ -@public - PuglView* puglview; - NSTrackingArea* trackingArea; - bool doubleBuffered; -} - -- (PuglView *) puglview; -- (void) setPuglview:(PuglView *)pv; -- (NSTrackingArea *) puglTrackingArea; -- (void) setPuglTrackingArea:(NSTrackingArea *)area; - -- (BOOL) acceptsFirstMouse:(NSEvent*)e; -- (BOOL) acceptsFirstResponder; -- (BOOL) isFlipped; -- (BOOL) isOpaque; -- (BOOL) preservesContentInLiveResize; -- (id) initWithFrame:(NSRect)frame; -- (void) reshape; -- (void) drawRect:(NSRect)r; -- (void) cursorUpdate:(NSEvent*)e; -- (void) updateTrackingAreas; -- (void) viewWillMoveToWindow:(NSWindow*)newWindow; -- (void) mouseMoved:(NSEvent*)event; -- (void) mouseDragged:(NSEvent*)event; -- (void) rightMouseDragged:(NSEvent*)event; -- (void) otherMouseDragged:(NSEvent*)event; -- (void) mouseDown:(NSEvent*)event; -- (void) rightMouseDown:(NSEvent*)event; -- (void) otherMouseDown:(NSEvent*)event; -- (void) mouseUp:(NSEvent*)event; -- (void) rightMouseUp:(NSEvent*)event; -- (void) otherMouseUp:(NSEvent*)event; -- (void) scrollWheel:(NSEvent*)event; -- (void) keyDown:(NSEvent*)event; -- (void) keyUp:(NSEvent*)event; -- (void) flagsChanged:(NSEvent*)event; -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize; - -@end - -@implementation PuglOpenGLView -- (PuglView *) puglview { - return self->puglview; -} - -- (void) setPuglview:(PuglView *)pv { - self->puglview = pv; -} - -- (NSTrackingArea *) puglTrackingArea { - return self->trackingArea; -} - -- (void) setPuglTrackingArea:(NSTrackingArea *)area { - self->trackingArea = area; -} - -- (BOOL) acceptsFirstMouse:(NSEvent*)e -{ - return YES; - - // unused - (void)e; -} - -- (BOOL) acceptsFirstResponder -{ - return YES; -} - -- (BOOL) isFlipped -{ - return YES; -} - -- (BOOL) isOpaque -{ - return YES; -} - -- (BOOL) preservesContentInLiveResize -{ - return NO; -} - -- (id) initWithFrame:(NSRect)frame -{ - puglview = nil; - trackingArea = nil; - doubleBuffered = true; - - NSOpenGLPixelFormatAttribute pixelAttribs[] = { - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - NSOpenGLPFADepthSize, 16, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAAccelerated, - 0 - }; - - NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] - initWithAttributes:pixelAttribs]; - - if (pixelFormat) { - self = [super initWithFrame:frame pixelFormat:pixelFormat]; - [pixelFormat release]; - printf("Is doubleBuffered? TRUE\n"); - } else { - self = [super initWithFrame:frame]; - doubleBuffered = false; - printf("Is doubleBuffered? FALSE\n"); - } - - if (self) { - GLint swapInterval = 1; - [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; - - [self reshape]; - } - - return self; -} - -- (void) reshape -{ - if (!puglview) { - /* NOTE: Apparently reshape gets called when the GC gets around to - deleting the view (?), so we must have reset puglview to NULL when - this comes around. - */ - return; - } - - [[self openGLContext] update]; - - reshape(self); -} - -- (void) drawRect:(NSRect)r -{ - puglEnterContext(puglview); - puglDisplay(puglview); - puglLeaveContext(puglview, true); - - // unused - return; (void)r; -} - -- (void) cursorUpdate:(NSEvent*)e -{ - cursorUpdate(self, e); -} - -- (void) updateTrackingAreas -{ - updateTrackingAreas(self); - [super updateTrackingAreas]; -} - -- (void) viewWillMoveToWindow:(NSWindow*)newWindow -{ - viewWillMoveToWindow(self, newWindow); - [super viewWillMoveToWindow:newWindow]; -} - -- (void) mouseMoved:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) mouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) rightMouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) otherMouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) mouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) rightMouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) otherMouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) mouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) rightMouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) otherMouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) scrollWheel:(NSEvent*)event -{ - scrollWheel(self, event); -} - -- (void) keyDown:(NSEvent*)event -{ - keyDown(self, event); -} - -- (void) keyUp:(NSEvent*)event -{ - keyUp(self, event); -} - -- (void) flagsChanged:(NSEvent*)event -{ - flagsChanged(self, event); -} - -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize -{ - PuglView *pv = self->puglview; - - if (pv->width <= 1 && pv->height <= 1) - { - /* NOTE: if the view size was not initialized yet, don't perform an - autoresize; it fixes manual resizing in Reaper. - */ - return; - } - - [super resizeWithOldSuperviewSize:oldSize]; -} - -@end -#endif - -#ifdef PUGL_CAIRO -@interface PuglCairoView : NSView -{ - PuglView* puglview; - cairo_t* cr; - NSTrackingArea* trackingArea; -} - -- (PuglView *) puglview; -- (void) setPuglview:(PuglView *)pv; -- (NSTrackingArea *) puglTrackingArea; -- (void) setPuglTrackingArea:(NSTrackingArea *)area; - -- (cairo_t *) cairoContext; - -- (BOOL) acceptsFirstMouse:(NSEvent*)e; -- (BOOL) acceptsFirstResponder; -- (BOOL) isFlipped; -- (BOOL) isOpaque; -- (BOOL) preservesContentInLiveResize; -- (id) initWithFrame:(NSRect)frame; -- (void) reshape; -- (void) drawRect:(NSRect)r; -- (void) cursorUpdate:(NSEvent*)e; -- (void) updateTrackingAreas; -- (void) viewWillMoveToWindow:(NSWindow*)newWindow; -- (void) mouseMoved:(NSEvent*)event; -- (void) mouseDragged:(NSEvent*)event; -- (void) rightMouseDragged:(NSEvent*)event; -- (void) otherMouseDragged:(NSEvent*)event; -- (void) mouseDown:(NSEvent*)event; -- (void) rightMouseDown:(NSEvent*)event; -- (void) otherMouseDown:(NSEvent*)event; -- (void) mouseUp:(NSEvent*)event; -- (void) rightMouseUp:(NSEvent*)event; -- (void) otherMouseUp:(NSEvent*)event; -- (void) scrollWheel:(NSEvent*)event; -- (void) keyDown:(NSEvent*)event; -- (void) keyUp:(NSEvent*)event; -- (void) flagsChanged:(NSEvent*)event; -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize; -@end - -@implementation PuglCairoView -- (PuglView *) puglview { - return self->puglview; -} - -- (void) setPuglview:(PuglView *)pv { - self->puglview = pv; -} - -- (NSTrackingArea *) puglTrackingArea { - return self->trackingArea; -} - -- (void) setPuglTrackingArea:(NSTrackingArea *)area { - self->trackingArea = area; -} - -- (cairo_t *) cairoContext { - return cr; -} - -- (BOOL) acceptsFirstMouse:(NSEvent*)e -{ - return YES; - - // unused - (void)e; -} - -- (BOOL) acceptsFirstResponder -{ - return YES; -} - -- (BOOL) isFlipped -{ - return YES; -} - -- (BOOL) isOpaque -{ - return YES; -} - -- (BOOL) preservesContentInLiveResize -{ - return NO; -} - -- (id) initWithFrame:(NSRect)frame { - puglview = nil; - cr = NULL; - trackingArea = nil; - [super initWithFrame:frame]; - return self; -} - -- (void) reshape -{ - if (!puglview) { - /* NOTE: Apparently reshape gets called when the GC gets around to - deleting the view (?), so we must have reset puglview to NULL when - this comes around. - */ - return; - } - - reshape(self); -} - -- (void) drawRect:(NSRect)r { - CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; - NSRect bounds = [self bounds]; - cairo_surface_t* surface; - cairo_t* cairo; - - surface = cairo_quartz_surface_create_for_cg_context(ctx, bounds.size.width, bounds.size.height); - if (surface) { - cairo = cairo_create(surface); - if (cairo) { - self->cr = cairo; - puglEnterContext(puglview); - puglDisplay(puglview); - puglLeaveContext(puglview, true); - self->cr = NULL; - cairo_destroy(cairo); - } - cairo_surface_destroy(surface); - } -} - -- (void) cursorUpdate:(NSEvent*)e -{ - cursorUpdate(self, e); -} - -- (void) updateTrackingAreas -{ - updateTrackingAreas(self); - [super updateTrackingAreas]; -} - -- (void) viewWillMoveToWindow:(NSWindow*)newWindow -{ - viewWillMoveToWindow(self, newWindow); - [super viewWillMoveToWindow:newWindow]; -} - -- (void) mouseMoved:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) mouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) rightMouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) otherMouseDragged:(NSEvent*)event -{ - mouseMoved(self, event); -} - -- (void) mouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) rightMouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) otherMouseDown:(NSEvent*)event -{ - mouseDown(self, event); -} - -- (void) mouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) rightMouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) otherMouseUp:(NSEvent*)event -{ - mouseUp(self, event); -} - -- (void) scrollWheel:(NSEvent*)event -{ - scrollWheel(self, event); -} - -- (void) keyDown:(NSEvent*)event -{ - keyDown(self, event); -} - -- (void) keyUp:(NSEvent*)event -{ - keyUp(self, event); -} - -- (void) flagsChanged:(NSEvent*)event -{ - flagsChanged(self, event); -} - -- (void) resizeWithOldSuperviewSize:(NSSize)oldSize -{ - PuglView *pv = self->puglview; - - if (pv->width <= 1 && pv->height <= 1) - { - /* NOTE: if the view size was not initialized yet, don't perform an - autoresize; it fixes manual resizing in Reaper. - */ - return; - } - - [super resizeWithOldSuperviewSize:oldSize]; -} - -@end -#endif - -struct PuglInternalsImpl { - union { - NSView* view; -#ifdef PUGL_OPENGL - PuglOpenGLView* glview; -#endif -#ifdef PUGL_CAIRO - PuglCairoView* cairoview; -#endif - }; - id window; -}; - -PuglInternals* -puglInitInternals() -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -void -puglEnterContext(PuglView* view) -{ -#ifdef PUGL_OPENGL - [[view->impl->glview openGLContext] makeCurrentContext]; -#endif -} - -void -puglLeaveContext(PuglView* view, bool flush) -{ - if (flush) { -#ifdef PUGL_OPENGL - if (view->impl->glview->doubleBuffered) { - [[view->impl->glview openGLContext] flushBuffer]; - } else { - glFlush(); - } - //[NSOpenGLContext clearCurrentContext]; -#endif - } -} - -int -puglCreateWindow(PuglView* view, const char* title) -{ - PuglInternals* impl = view->impl; - - [NSAutoreleasePool new]; - [NSApplication sharedApplication]; - -#ifdef PUGL_OPENGL - impl->glview = [PuglOpenGLView new]; -#endif -#ifdef PUGL_CAIRO - impl->cairoview = [PuglCairoView new]; -#endif - - if (!impl->view) { - return 1; - } - - [impl->view setPuglview:view]; - - if (view->user_resizable) { - [impl->view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; - } - - if (view->parent) { - [impl->view retain]; - NSView* pview = (NSView*)view->parent; - [pview addSubview:impl->view]; - return 0; - } - - id window = [[PuglWindow new]retain]; - - if (title) { - NSString* titleString = [[NSString alloc] - initWithBytes:title - length:strlen(title) - encoding:NSUTF8StringEncoding]; - - [window setTitle:titleString]; - } - - [window setPuglview:view]; - [window setContentView:impl->view]; - [window makeFirstResponder:impl->view]; - [window makeKeyAndOrderFront:window]; - - // wait for first puglShowWindow - [window setIsVisible:NO]; - - [NSApp activateIgnoringOtherApps:YES]; - [window center]; - - impl->window = window; - - return 0; -} - -void -puglShowWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - if (impl->window) { - [impl->window setIsVisible:YES]; - } else { - [view->impl->view setHidden:NO]; - } -} - -void -puglHideWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - if (impl->window) { - [impl->window setIsVisible:NO]; - } else { - [impl->view setHidden:YES]; - } -} - -void -puglDestroy(PuglView* view) -{ - [view->impl->view setPuglview:NULL]; - - if (view->impl->window) { - [view->impl->window close]; - [view->impl->view release]; - [view->impl->window release]; - } else { - [view->impl->view release]; - } - - free(view->impl); - free(view); -} - -PuglStatus -puglProcessEvents(PuglView* view) -{ - return PUGL_SUCCESS; - - // unused - (void)view; -} - -void -puglPostRedisplay(PuglView* view) -{ - view->redisplay = true; - [view->impl->view setNeedsDisplay:YES]; -} - -PuglNativeWindow -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeWindow)view->impl->view; -} - -void* -puglGetContext(PuglView* view) -{ -#ifdef PUGL_CAIRO - return [view->impl->cairoview cairoContext]; -#endif - return NULL; - - // may be unused - (void)view; -} - -int -puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) -{ - // TODO - return 1; - - (void)view; - (void)min_width; - (void)min_height; - (void)aspect; -} diff --git a/dpf/dgl/src/pugl-custom/pugl_win.cpp b/dpf/dgl/src/pugl-custom/pugl_win.cpp deleted file mode 100644 index 90aa597..0000000 --- a/dpf/dgl/src/pugl-custom/pugl_win.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* - Copyright 2012-2014 David Robillard - Copyright 2012-2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_win.cpp Windows/WGL Pugl Implementation. -*/ - -#include -#include -#include - -#include -#include -#include -#ifdef PUGL_CAIRO -#include -#include -#endif -#ifdef PUGL_OPENGL -#include -#endif - -#include "pugl_internal.h" - -#ifndef WM_MOUSEWHEEL -# define WM_MOUSEWHEEL 0x020A -#endif -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif -#ifndef WHEEL_DELTA -# define WHEEL_DELTA 120 -#endif -#ifndef GWLP_USERDATA -# define GWLP_USERDATA (-21) -#endif - -#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50) - -HINSTANCE hInstance = NULL; - -struct PuglInternalsImpl { - HWND hwnd; -#ifdef PUGL_OPENGL - HDC hdc; - HGLRC hglrc; -#endif -#ifdef PUGL_CAIRO - cairo_t* buffer_cr; - cairo_surface_t* buffer_surface; -#endif - HDC paintHdc; - WNDCLASS wc; -}; - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); - -#if 0 -extern "C" { -BOOL WINAPI -DllMain(HINSTANCE hInst, DWORD, LPVOID) -{ - hInstance = hInst; - return 1; -} -} // extern "C" -#endif - -PuglInternals* -puglInitInternals() -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -void -puglEnterContext(PuglView* view) -{ -#ifdef PUGL_OPENGL - wglMakeCurrent(view->impl->hdc, view->impl->hglrc); -#endif -} - -void -puglLeaveContext(PuglView* view, bool flush) -{ -#ifdef PUGL_OPENGL - if (flush) { - glFlush(); - SwapBuffers(view->impl->hdc); - } - wglMakeCurrent(NULL, NULL); -#endif -} - -int -puglCreateWindow(PuglView* view, const char* title) -{ - PuglInternals* impl = view->impl; - - if (!title) { - title = "Window"; - } - - // FIXME: This is nasty, and pugl should not have static anything. - // Should class be a parameter? Does this make sense on other platforms? - static int wc_count = 0; - char classNameBuf[256]; - std::srand((std::time(NULL))); -#ifdef __WINE__ - std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); -#else - _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); -#endif - classNameBuf[sizeof(classNameBuf)-1] = '\0'; - - impl->wc.style = CS_OWNDC; - impl->wc.lpfnWndProc = wndProc; - impl->wc.cbClsExtra = 0; - impl->wc.cbWndExtra = 0; - impl->wc.hInstance = hInstance; - impl->wc.hIcon = LoadIcon(hInstance, IDI_APPLICATION); - impl->wc.hCursor = LoadCursor(hInstance, IDC_ARROW); - impl->wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); - impl->wc.lpszMenuName = NULL; - impl->wc.lpszClassName = strdup(classNameBuf); - - if (!RegisterClass(&impl->wc)) { - free((void*)impl->wc.lpszClassName); - free(impl); - return 1; - } - - int winFlags = WS_POPUPWINDOW | WS_CAPTION; - if (view->user_resizable) { - winFlags |= WS_SIZEBOX; - if (view->min_width > 0 && view->min_height > 0) { - // Adjust the minimum window size to accomodate requested view size - RECT mr = { 0, 0, view->min_width, view->min_height }; - AdjustWindowRectEx(&mr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); - view->min_width = mr.right - mr.left; - view->min_height = mr.bottom - mr.top; - } - } - - // Adjust the window size to accomodate requested view size - RECT wr = { 0, 0, view->width, view->height }; - AdjustWindowRectEx(&wr, view->parent ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); - - impl->hwnd = CreateWindowEx( - WS_EX_TOPMOST, - classNameBuf, title, - view->parent ? (WS_CHILD | WS_VISIBLE) : winFlags, - CW_USEDEFAULT, CW_USEDEFAULT, wr.right-wr.left, wr.bottom-wr.top, - (HWND)view->parent, NULL, hInstance, NULL); - - if (!impl->hwnd) { - UnregisterClass(impl->wc.lpszClassName, NULL); - free((void*)impl->wc.lpszClassName); - free(impl); - return 1; - } - - SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view); - -#ifdef PUGL_OPENGL - impl->hdc = GetDC(impl->hwnd); - - PIXELFORMATDESCRIPTOR pfd; - ZeroMemory(&pfd, sizeof(pfd)); - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 24; - pfd.cDepthBits = 16; - pfd.iLayerType = PFD_MAIN_PLANE; - - int format = ChoosePixelFormat(impl->hdc, &pfd); - SetPixelFormat(impl->hdc, format, &pfd); - - impl->hglrc = wglCreateContext(impl->hdc); - if (!impl->hglrc) { - ReleaseDC (impl->hwnd, impl->hdc); - DestroyWindow (impl->hwnd); - UnregisterClass (impl->wc.lpszClassName, NULL); - free((void*)impl->wc.lpszClassName); - free(impl); - return 1; - } -#endif - - return PUGL_SUCCESS; -} - -void -puglShowWindow(PuglView* view) -{ - ShowWindow(view->impl->hwnd, SW_SHOWNORMAL); -} - -void -puglHideWindow(PuglView* view) -{ - ShowWindow(view->impl->hwnd, SW_HIDE); -} - -void -puglDestroy(PuglView* view) -{ - if (!view) { - return; - } - - PuglInternals* const impl = view->impl; - -#ifdef PUGL_OPENGL - wglMakeCurrent(NULL, NULL); - wglDeleteContext(impl->hglrc); - ReleaseDC(impl->hwnd, impl->hdc); -#endif -#ifdef PUGL_CAIRO - cairo_destroy(impl->buffer_cr); - cairo_surface_destroy(impl->buffer_surface); -#endif - DestroyWindow(impl->hwnd); - UnregisterClass(impl->wc.lpszClassName, NULL); - free((void*)impl->wc.lpszClassName); - free(impl); - free(view); -} - -static void -puglReshape(PuglView* view, int width, int height) -{ - puglEnterContext(view); - - if (view->reshapeFunc) { - view->reshapeFunc(view, width, height); - } else { - puglDefaultReshape(width, height); - } - - view->width = width; - view->height = height; -} - -static void -puglDisplay(PuglView* view) -{ - PuglInternals* impl = view->impl; - bool success = true; - - puglEnterContext(view); - -#ifdef PUGL_CAIRO - cairo_t *wc = NULL; - cairo_t *bc = NULL; - cairo_surface_t *ws = NULL; - cairo_surface_t *bs = NULL; - - HDC hdc = impl->paintHdc; - bc = impl->buffer_cr; - bs = impl->buffer_surface; - int w = view->width; - int h = view->height; - int bw = bs ? cairo_image_surface_get_width(bs) : -1; - int bh = bs ? cairo_image_surface_get_height(bs) : -1; - ws = hdc ? cairo_win32_surface_create(hdc) : NULL; - wc = ws ? cairo_create(ws) : NULL; - if (wc && (!bc || bw != w || bh != h)) { - cairo_destroy(bc); - cairo_surface_destroy(bs); - bs = cairo_surface_create_similar_image(ws, CAIRO_FORMAT_ARGB32, w, h); - bc = bs ? cairo_create(bs) : NULL; - impl->buffer_cr = bc; - impl->buffer_surface = bs; - } - success = wc != NULL && bc != NULL; -#endif - - if (success) { - view->redisplay = false; - if (view->displayFunc) { - view->displayFunc(view); - } -#ifdef PUGL_CAIRO - cairo_set_source_surface(wc, bs, 0, 0); - cairo_paint(wc); -#endif - } - - puglLeaveContext(view, success); - -#ifdef PUGL_CAIRO - cairo_destroy(wc); - cairo_surface_destroy(ws); -#endif - - return; - (void)impl; -} - -static PuglKey -keySymToSpecial(int sym) -{ - switch (sym) { - case VK_F1: return PUGL_KEY_F1; - case VK_F2: return PUGL_KEY_F2; - case VK_F3: return PUGL_KEY_F3; - case VK_F4: return PUGL_KEY_F4; - case VK_F5: return PUGL_KEY_F5; - case VK_F6: return PUGL_KEY_F6; - case VK_F7: return PUGL_KEY_F7; - case VK_F8: return PUGL_KEY_F8; - case VK_F9: return PUGL_KEY_F9; - case VK_F10: return PUGL_KEY_F10; - case VK_F11: return PUGL_KEY_F11; - case VK_F12: return PUGL_KEY_F12; - case VK_LEFT: return PUGL_KEY_LEFT; - case VK_UP: return PUGL_KEY_UP; - case VK_RIGHT: return PUGL_KEY_RIGHT; - case VK_DOWN: return PUGL_KEY_DOWN; - case VK_PRIOR: return PUGL_KEY_PAGE_UP; - case VK_NEXT: return PUGL_KEY_PAGE_DOWN; - case VK_HOME: return PUGL_KEY_HOME; - case VK_END: return PUGL_KEY_END; - case VK_INSERT: return PUGL_KEY_INSERT; - case VK_SHIFT: return PUGL_KEY_SHIFT; - case VK_CONTROL: return PUGL_KEY_CTRL; - case VK_MENU: return PUGL_KEY_ALT; - case VK_LWIN: return PUGL_KEY_SUPER; - case VK_RWIN: return PUGL_KEY_SUPER; - } - return (PuglKey)0; -} - -static void -processMouseEvent(PuglView* view, int button, bool press, LPARAM lParam) -{ - view->event_timestamp_ms = GetMessageTime(); - if (press) { - SetCapture(view->impl->hwnd); - } else { - ReleaseCapture(); - } - - if (view->mouseFunc) { - view->mouseFunc(view, button, press, - GET_X_LPARAM(lParam), - GET_Y_LPARAM(lParam)); - } -} - -static void -setModifiers(PuglView* view) -{ - view->mods = 0; - view->mods |= (GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0; - view->mods |= (GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0; - view->mods |= (GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0; - view->mods |= (GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0; - view->mods |= (GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0; -} - -static LRESULT -handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam) -{ - PAINTSTRUCT ps; - PuglKey key; - RECT rect; - MINMAXINFO* mmi; - - setModifiers(view); - switch (message) { - case WM_CREATE: - case WM_SHOWWINDOW: - case WM_SIZE: - GetClientRect(view->impl->hwnd, &rect); - puglReshape(view, rect.right, rect.bottom); - break; - case WM_GETMINMAXINFO: - mmi = (MINMAXINFO*)lParam; - mmi->ptMinTrackSize.x = view->min_width; - mmi->ptMinTrackSize.y = view->min_height; - break; - case WM_PAINT: - view->impl->paintHdc = BeginPaint(view->impl->hwnd, &ps); - puglDisplay(view); - view->impl->paintHdc = NULL; - EndPaint(view->impl->hwnd, &ps); - break; - case WM_MOUSEMOVE: - if (view->motionFunc) { - view->event_timestamp_ms = GetMessageTime(); - view->motionFunc(view, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - } - break; - case WM_LBUTTONDOWN: - processMouseEvent(view, 1, true, lParam); - break; - case WM_MBUTTONDOWN: - processMouseEvent(view, 2, true, lParam); - break; - case WM_RBUTTONDOWN: - processMouseEvent(view, 3, true, lParam); - break; - case WM_LBUTTONUP: - processMouseEvent(view, 1, false, lParam); - break; - case WM_MBUTTONUP: - processMouseEvent(view, 2, false, lParam); - break; - case WM_RBUTTONUP: - processMouseEvent(view, 3, false, lParam); - break; - case WM_MOUSEWHEEL: - if (view->scrollFunc) { - view->event_timestamp_ms = GetMessageTime(); - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - view->scrollFunc( - view, pt.x, pt.y, - 0.0f, GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA); - } - break; - case WM_MOUSEHWHEEL: - if (view->scrollFunc) { - view->event_timestamp_ms = GetMessageTime(); - POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; - ScreenToClient(view->impl->hwnd, &pt); - view->scrollFunc( - view, pt.x, pt.y, - GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA, 0.0f); - } - break; - case WM_KEYDOWN: - if (view->ignoreKeyRepeat && (lParam & (1 << 30))) { - break; - } // else nobreak - case WM_KEYUP: - view->event_timestamp_ms = GetMessageTime(); - if ((key = keySymToSpecial(wParam))) { - if (view->specialFunc) { - view->specialFunc(view, message == WM_KEYDOWN, key); - } - } else if (view->keyboardFunc) { - static BYTE kbs[256]; - if (GetKeyboardState(kbs) != FALSE) { - char lb[2]; - UINT scanCode = (lParam >> 8) & 0xFFFFFF00; - if ( 1 == ToAscii(wParam, scanCode, kbs, (LPWORD)lb, 0)) { - view->keyboardFunc(view, message == WM_KEYDOWN, (char)lb[0]); - } - } - } - break; - case WM_QUIT: - case PUGL_LOCAL_CLOSE_MSG: - if (view->closeFunc) { - view->closeFunc(view); - view->redisplay = false; - } - break; - default: - return DefWindowProc( - view->impl->hwnd, message, wParam, lParam); - } - - return 0; -} - -void -puglGrabFocus(PuglView* /*view*/) -{ - // TODO -} - -PuglStatus -puglProcessEvents(PuglView* view) -{ - MSG msg; - while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) { - handleMessage(view, msg.message, msg.wParam, msg.lParam); - } - - if (view->redisplay) { - InvalidateRect(view->impl->hwnd, NULL, FALSE); - } - - return PUGL_SUCCESS; -} - -LRESULT CALLBACK -wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - PuglView* view = (PuglView*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - - switch (message) { - case WM_CREATE: - PostMessage(hwnd, WM_SHOWWINDOW, TRUE, 0); - return 0; - case WM_CLOSE: - PostMessage(hwnd, PUGL_LOCAL_CLOSE_MSG, wParam, lParam); - return 0; - case WM_DESTROY: - return 0; - default: - if (view && hwnd == view->impl->hwnd) { - return handleMessage(view, message, wParam, lParam); - } else { - return DefWindowProc(hwnd, message, wParam, lParam); - } - } -} - -void -puglPostRedisplay(PuglView* view) -{ - view->redisplay = true; -} - -PuglNativeWindow -puglGetNativeWindow(PuglView* view) -{ - return (PuglNativeWindow)view->impl->hwnd; -} - -void* -puglGetContext(PuglView* view) -{ -#ifdef PUGL_CAIRO - return view->impl->buffer_cr; -#endif - return NULL; - - // may be unused - (void)view; -} - -int -puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) -{ - // TODO - return 1; - - (void)view; - (void)min_width; - (void)min_height; - (void)aspect; -} diff --git a/dpf/dgl/src/pugl-custom/pugl_x11.c b/dpf/dgl/src/pugl-custom/pugl_x11.c deleted file mode 100644 index bf89706..0000000 --- a/dpf/dgl/src/pugl-custom/pugl_x11.c +++ /dev/null @@ -1,739 +0,0 @@ -/* - Copyright 2012-2014 David Robillard - Copyright 2011-2012 Ben Loftis, Harrison Consoles - Copyright 2013,2015 Robin Gareus - Copyright 2012-2019 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl_x11.c X11 Pugl Implementation. -*/ - -#include -#include -#include - -#ifdef PUGL_CAIRO -#include -#include -#endif -#ifdef PUGL_OPENGL -#include -#include -#endif -#include -#include -#include -#include - -#include "pugl_internal.h" - -#ifndef DGL_FILE_BROWSER_DISABLED -#define SOFD_HAVE_X11 -#include "../sofd/libsofd.h" -#include "../sofd/libsofd.c" -#endif - -/* work around buggy re-parent & focus issues on some systems - * where no keyboard events are passed through even if the - * app has mouse-focus and all other events are working. - */ -//#define PUGL_GRAB_FOCUS - -/* show messages during initalization - */ -//#define PUGL_VERBOSE - -struct PuglInternalsImpl { - Display* display; - int screen; - Window win; -#ifdef PUGL_CAIRO - cairo_t* xlib_cr; - cairo_t* buffer_cr; - cairo_surface_t* xlib_surface; - cairo_surface_t* buffer_surface; -#endif -#ifdef PUGL_OPENGL - GLXContext ctx; - Bool doubleBuffered; -#endif -}; - -#ifdef PUGL_OPENGL -/** - Attributes for single-buffered RGBA with at least - 4 bits per color and a 16 bit depth buffer. -*/ -static int attrListSgl[] = { - GLX_RGBA, - GLX_RED_SIZE, 4, - GLX_GREEN_SIZE, 4, - GLX_BLUE_SIZE, 4, - GLX_DEPTH_SIZE, 16, - GLX_STENCIL_SIZE, 8, - GLX_ARB_multisample, 1, - None -}; - -/** - Attributes for double-buffered RGBA with at least - 4 bits per color and a 16 bit depth buffer. -*/ -static int attrListDbl[] = { - GLX_RGBA, - GLX_DOUBLEBUFFER, True, - GLX_RED_SIZE, 4, - GLX_GREEN_SIZE, 4, - GLX_BLUE_SIZE, 4, - GLX_DEPTH_SIZE, 16, - GLX_STENCIL_SIZE, 8, - GLX_ARB_multisample, 1, - None -}; - -/** - Attributes for double-buffered RGBA with multi-sampling - (antialiasing) -*/ -static int attrListDblMS[] = { - GLX_RGBA, - GLX_DOUBLEBUFFER, True, - GLX_RED_SIZE, 4, - GLX_GREEN_SIZE, 4, - GLX_BLUE_SIZE, 4, - GLX_ALPHA_SIZE, 4, - GLX_DEPTH_SIZE, 16, - GLX_STENCIL_SIZE, 8, - GLX_SAMPLE_BUFFERS, 1, - GLX_SAMPLES, 4, - None -}; -#endif - -PuglInternals* -puglInitInternals(void) -{ - return (PuglInternals*)calloc(1, sizeof(PuglInternals)); -} - -void -puglEnterContext(PuglView* view) -{ -#ifdef PUGL_OPENGL - glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); -#endif -} - -void -puglLeaveContext(PuglView* view, bool flush) -{ -#ifdef PUGL_OPENGL - if (flush) { - glFlush(); - if (view->impl->doubleBuffered) { - glXSwapBuffers(view->impl->display, view->impl->win); - } - } - glXMakeCurrent(view->impl->display, None, NULL); -#endif -} - -int -puglCreateWindow(PuglView* view, const char* title) -{ - PuglInternals* impl = view->impl; - if (!impl) { - return 1; - } - - view->impl = impl; - impl->display = XOpenDisplay(NULL); - if (!impl->display) { - free(impl); - return 1; - } - impl->screen = DefaultScreen(impl->display); - - XVisualInfo* vi = NULL; - -#ifdef PUGL_OPENGL - impl->doubleBuffered = True; - vi = glXChooseVisual(impl->display, impl->screen, attrListDblMS); - - if (!vi) { - vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); -#ifdef PUGL_VERBOSE - printf("puGL: multisampling (antialiasing) is not available\n"); -#endif - } - - if (!vi) { - vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); - impl->doubleBuffered = False; - } -#endif -#ifdef PUGL_CAIRO - XVisualInfo pat; - int n; - pat.screen = impl->screen; - vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); -#endif - - if (!vi) { - XCloseDisplay(impl->display); - free(impl); - return 1; - } - -#ifdef PUGL_VERBOSE -#ifdef PUGL_OPENGL - int glxMajor, glxMinor; - glXQueryVersion(impl->display, &glxMajor, &glxMinor); - printf("puGL: GLX-Version : %d.%d\n", glxMajor, glxMinor); -#endif -#endif - -#ifdef PUGL_OPENGL - impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); - - if (!impl->ctx) { - XFree(vi); - XCloseDisplay(impl->display); - free(impl); - return 1; - } -#endif - - Window xParent = view->parent - ? (Window)view->parent - : RootWindow(impl->display, impl->screen); - - Colormap cmap = XCreateColormap( - impl->display, xParent, vi->visual, AllocNone); - - XSetWindowAttributes attr; - memset(&attr, 0, sizeof(XSetWindowAttributes)); - attr.border_pixel = BlackPixel(impl->display, impl->screen); - attr.colormap = cmap; - attr.event_mask = (ExposureMask | StructureNotifyMask | - EnterWindowMask | LeaveWindowMask | - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | FocusChangeMask); - - impl->win = XCreateWindow( - impl->display, xParent, - 0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, - CWBorderPixel | CWColormap | CWEventMask, &attr); - - if (!impl->win) { -#ifdef PUGL_OPENGL - glXDestroyContext(impl->display, impl->ctx); -#endif - XFree(vi); - XCloseDisplay(impl->display); - free(impl); - return 1; - } - -#ifdef PUGL_CAIRO - impl->xlib_surface = cairo_xlib_surface_create( - impl->display, impl->win, vi->visual, view->width, view->height); - if (impl->xlib_surface == NULL || cairo_surface_status(impl->xlib_surface) != CAIRO_STATUS_SUCCESS) { - printf("puGL: failed to create cairo surface\n"); - } - else { - impl->xlib_cr = cairo_create(impl->xlib_surface); - } - if (impl->xlib_cr == NULL || cairo_status(impl->xlib_cr) != CAIRO_STATUS_SUCCESS) { - cairo_destroy(impl->xlib_cr); - cairo_surface_destroy(impl->xlib_surface); - XDestroyWindow(impl->display, impl->win); - XFree(vi); - XCloseDisplay(impl->display); - free(impl); - printf("puGL: failed to create cairo context\n"); - return 1; - } -#endif - - if (view->width > 1 || view->height > 1) { - puglUpdateGeometryConstraints(view, view->min_width, view->min_height, view->min_width != view->width); - XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); - } - - if (title) { - XStoreName(impl->display, impl->win, title); - Atom netWmName = XInternAtom(impl->display, "_NET_WM_NAME", False); - Atom utf8String = XInternAtom(impl->display, "UTF8_STRING", False); - XChangeProperty(impl->display, impl->win, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); - } - - if (view->transient_parent > 0) { - XSetTransientForHint(impl->display, impl->win, (Window)view->transient_parent); - } - - if (view->parent) { - XMapRaised(impl->display, impl->win); - } else { - Atom wmDelete = XInternAtom(impl->display, "WM_DELETE_WINDOW", True); - XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); - } - -#ifdef PUGL_VERBOSE -#ifdef PUGL_OPENGL - if (glXIsDirect(impl->display, impl->ctx)) { - printf("puGL: DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); - } else { - printf("puGL: No DRI available\n"); - } -#endif -#endif - - XFree(vi); - return 0; -} - -void -puglDestroy(PuglView* view) -{ - if (!view) { - return; - } - - PuglInternals* const impl = view->impl; - -#ifndef DGL_FILE_BROWSER_DISABLED - x_fib_close(impl->display); -#endif - -#ifdef PUGL_OPENGL - glXDestroyContext(impl->display, impl->ctx); -#endif -#ifdef PUGL_CAIRO - cairo_destroy(impl->xlib_cr); - cairo_destroy(impl->buffer_cr); - cairo_surface_destroy(impl->xlib_surface); - cairo_surface_destroy(impl->buffer_surface); -#endif - XDestroyWindow(impl->display, impl->win); - XCloseDisplay(impl->display); - free(impl); - free(view); -} - -void -puglShowWindow(PuglView* view) -{ - XMapRaised(view->impl->display, view->impl->win); -} - -void -puglHideWindow(PuglView* view) -{ - XUnmapWindow(view->impl->display, view->impl->win); -} - -static void -puglReshape(PuglView* view, int width, int height) -{ - puglEnterContext(view); - - if (view->reshapeFunc) { - view->reshapeFunc(view, width, height); - } else { - puglDefaultReshape(width, height); - } - - puglLeaveContext(view, false); - - view->width = width; - view->height = height; -} - -static void -puglDisplay(PuglView* view) -{ - PuglInternals* impl = view->impl; - - puglEnterContext(view); - -#ifdef PUGL_CAIRO - cairo_t* bc = impl->buffer_cr; - cairo_surface_t* xs = impl->xlib_surface; - cairo_surface_t* bs = impl->buffer_surface; - int w = cairo_xlib_surface_get_width(xs); - int h = cairo_xlib_surface_get_height(xs); - - int bw = bs ? cairo_image_surface_get_width(bs) : -1; - int bh = bs ? cairo_image_surface_get_height(bs) : -1; - if (!bc || bw != w || bh != h) { - cairo_destroy(bc); - cairo_surface_destroy(bs); - bs = cairo_surface_create_similar_image(xs, CAIRO_FORMAT_ARGB32, w, h); - bc = bs ? cairo_create(bs) : NULL; - impl->buffer_cr = bc; - impl->buffer_surface = bs; - } - - if (!bc) { - puglLeaveContext(view, false); - return; - } -#endif - - view->redisplay = false; - if (view->displayFunc) { - view->displayFunc(view); - } - -#ifdef PUGL_CAIRO - cairo_t* xc = impl->xlib_cr; - cairo_set_source_surface(xc, impl->buffer_surface, 0, 0); - cairo_paint(xc); -#endif - - puglLeaveContext(view, true); - (void)impl; -} - -static void -puglResize(PuglView* view) -{ - int set_hints = 1; - view->pending_resize = false; - if (!view->resizeFunc) { return; } - /* ask the plugin about the new size */ - view->resizeFunc(view, &view->width, &view->height, &set_hints); - - if (set_hints) { - XSizeHints sizeHints; - memset(&sizeHints, 0, sizeof(sizeHints)); - sizeHints.flags = PMinSize|PMaxSize; - sizeHints.min_width = view->width; - sizeHints.min_height = view->height; - sizeHints.max_width = view->user_resizable ? 4096 : view->width; - sizeHints.max_height = view->user_resizable ? 4096 : view->height; - XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); - } - XResizeWindow(view->impl->display, view->impl->win, view->width, view->height); - XFlush(view->impl->display); - -#ifdef PUGL_VERBOSE - printf("puGL: window resize (%dx%d)\n", view->width, view->height); -#endif - - /* and call Reshape in glX context */ - puglReshape(view, view->width, view->height); -} - -static PuglKey -keySymToSpecial(KeySym sym) -{ - switch (sym) { - case XK_F1: return PUGL_KEY_F1; - case XK_F2: return PUGL_KEY_F2; - case XK_F3: return PUGL_KEY_F3; - case XK_F4: return PUGL_KEY_F4; - case XK_F5: return PUGL_KEY_F5; - case XK_F6: return PUGL_KEY_F6; - case XK_F7: return PUGL_KEY_F7; - case XK_F8: return PUGL_KEY_F8; - case XK_F9: return PUGL_KEY_F9; - case XK_F10: return PUGL_KEY_F10; - case XK_F11: return PUGL_KEY_F11; - case XK_F12: return PUGL_KEY_F12; - case XK_Left: return PUGL_KEY_LEFT; - case XK_Up: return PUGL_KEY_UP; - case XK_Right: return PUGL_KEY_RIGHT; - case XK_Down: return PUGL_KEY_DOWN; - case XK_Page_Up: return PUGL_KEY_PAGE_UP; - case XK_Page_Down: return PUGL_KEY_PAGE_DOWN; - case XK_Home: return PUGL_KEY_HOME; - case XK_End: return PUGL_KEY_END; - case XK_Insert: return PUGL_KEY_INSERT; - case XK_Shift_L: return PUGL_KEY_SHIFT; - case XK_Shift_R: return PUGL_KEY_SHIFT; - case XK_Control_L: return PUGL_KEY_CTRL; - case XK_Control_R: return PUGL_KEY_CTRL; - case XK_Alt_L: return PUGL_KEY_ALT; - case XK_Alt_R: return PUGL_KEY_ALT; - case XK_Super_L: return PUGL_KEY_SUPER; - case XK_Super_R: return PUGL_KEY_SUPER; - } - return (PuglKey)0; -} - -static void -setModifiers(PuglView* view, unsigned xstate, unsigned xtime) -{ - view->event_timestamp_ms = xtime; - - view->mods = 0; - view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; - view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; - view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; - view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; -} - -static void -dispatchKey(PuglView* view, XEvent* event, bool press) -{ - KeySym sym; - char str[5]; - PuglKey special; - const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); - - if (sym == XK_Escape && view->closeFunc && !press && !view->parent) { - view->closeFunc(view); - view->redisplay = false; - return; - } - if (n == 0 && sym == 0) { - goto send_event; - return; - } - if (n > 1) { - fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); - goto send_event; - return; - } - - special = keySymToSpecial(sym); - if (special && view->specialFunc) { - if (view->specialFunc(view, press, special) == 0) { - return; - } - } else if (!special && view->keyboardFunc) { - if (view->keyboardFunc(view, press, str[0]) == 0) { - return; - } - } - -send_event: - if (view->parent != 0) { - event->xkey.time = 0; // purposefully set an invalid time, used for feedback detection on bad hosts - event->xany.window = view->parent; - XSendEvent(view->impl->display, view->parent, False, NoEventMask, event); - } -} - -PuglStatus -puglProcessEvents(PuglView* view) -{ - int conf_width = -1; - int conf_height = -1; - - XEvent event; - while (XPending(view->impl->display) > 0) { - XNextEvent(view->impl->display, &event); - -#ifndef DGL_FILE_BROWSER_DISABLED - if (x_fib_handle_events(view->impl->display, &event)) { - const int status = x_fib_status(); - - if (status > 0) { - char* const filename = x_fib_filename(); - x_fib_close(view->impl->display); - if (view->fileSelectedFunc) { - view->fileSelectedFunc(view, filename); - } - free(filename); - } else if (status < 0) { - x_fib_close(view->impl->display); - if (view->fileSelectedFunc) { - view->fileSelectedFunc(view, NULL); - } - } - break; - } -#endif - - if (event.xany.window != view->impl->win && - (view->parent == 0 || event.xany.window != (Window)view->parent)) { - continue; - } - if ((event.type == KeyPress || event.type == KeyRelease) && event.xkey.time == 0) { - continue; - } - - switch (event.type) { - case UnmapNotify: - if (view->motionFunc) { - view->motionFunc(view, -1, -1); - } - break; - case MapNotify: - puglReshape(view, view->width, view->height); - break; - case ConfigureNotify: - if ((event.xconfigure.width != view->width) || - (event.xconfigure.height != view->height)) { - conf_width = event.xconfigure.width; - conf_height = event.xconfigure.height; - } - break; - case Expose: - if (event.xexpose.count != 0) { - break; - } - view->redisplay = true; - break; - case MotionNotify: - setModifiers(view, event.xmotion.state, event.xmotion.time); - if (view->motionFunc) { - view->motionFunc(view, event.xmotion.x, event.xmotion.y); - } - break; - case ButtonPress: - setModifiers(view, event.xbutton.state, event.xbutton.time); - if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { - if (view->scrollFunc) { - float dx = 0, dy = 0; - switch (event.xbutton.button) { - case 4: dy = 1.0f; break; - case 5: dy = -1.0f; break; - case 6: dx = -1.0f; break; - case 7: dx = 1.0f; break; - } - view->scrollFunc(view, event.xbutton.x, event.xbutton.y, dx, dy); - } - break; - } - // nobreak - case ButtonRelease: - setModifiers(view, event.xbutton.state, event.xbutton.time); - if (view->mouseFunc && - (event.xbutton.button < 4 || event.xbutton.button > 7)) { - view->mouseFunc(view, - event.xbutton.button, event.type == ButtonPress, - event.xbutton.x, event.xbutton.y); - } - break; - case KeyPress: - setModifiers(view, event.xkey.state, event.xkey.time); - dispatchKey(view, &event, true); - break; - case KeyRelease: { - setModifiers(view, event.xkey.state, event.xkey.time); - bool repeated = false; - if (view->ignoreKeyRepeat && - XEventsQueued(view->impl->display, QueuedAfterReading)) { - XEvent next; - XPeekEvent(view->impl->display, &next); - if (next.type == KeyPress && - next.xkey.time == event.xkey.time && - next.xkey.keycode == event.xkey.keycode) { - XNextEvent(view->impl->display, &event); - repeated = true; - } - } - if (!repeated) { - dispatchKey(view, &event, false); - } - } break; - case ClientMessage: { - char* type = XGetAtomName(view->impl->display, - event.xclient.message_type); - if (!strcmp(type, "WM_PROTOCOLS")) { - if (view->closeFunc) { - view->closeFunc(view); - view->redisplay = false; - } - } - XFree(type); - } break; -#ifdef PUGL_GRAB_FOCUS - case EnterNotify: - XSetInputFocus(view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); - break; -#endif - default: - break; - } - } - - if (conf_width != -1) { -#ifdef PUGL_CAIRO - // Resize surfaces/contexts before dispatching - view->redisplay = true; - cairo_xlib_surface_set_size(view->impl->xlib_surface, - conf_width, conf_height); -#endif - puglReshape(view, conf_width, conf_height); - } - - if (view->pending_resize) { - puglResize(view); - } - - if (view->redisplay) { - puglDisplay(view); - } - - return PUGL_SUCCESS; -} - -void -puglPostRedisplay(PuglView* view) -{ - view->redisplay = true; -} - -void -puglPostResize(PuglView* view) -{ - view->pending_resize = true; -} - -PuglNativeWindow -puglGetNativeWindow(PuglView* view) -{ - return view->impl->win; -} - -void* -puglGetContext(PuglView* view) -{ -#ifdef PUGL_CAIRO - return view->impl->buffer_cr; -#endif - return NULL; - - // may be unused - (void)view; -} - -int -puglUpdateGeometryConstraints(PuglView* view, int min_width, int min_height, bool aspect) -{ - XSizeHints sizeHints; - memset(&sizeHints, 0, sizeof(sizeHints)); - sizeHints.flags = PMinSize|PMaxSize; - sizeHints.min_width = min_width; - sizeHints.min_height = min_height; - sizeHints.max_width = view->user_resizable ? 4096 : min_width; - sizeHints.max_height = view->user_resizable ? 4096 : min_height; - if (aspect) { - sizeHints.flags |= PAspect; - sizeHints.min_aspect.x = min_width; - sizeHints.min_aspect.y = min_height; - sizeHints.max_aspect.x = min_width; - sizeHints.max_aspect.y = min_height; - } - XSetWMNormalHints(view->impl->display, view->impl->win, &sizeHints); - return 0; -} diff --git a/dpf/dgl/src/pugl-extra/extras.c b/dpf/dgl/src/pugl-extra/extras.c deleted file mode 100644 index 01e9019..0000000 --- a/dpf/dgl/src/pugl-extra/extras.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright (C) 2012-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file extras.c pugl extra implementations for DPF. -*/ - -#include "extras.h" - -#include "../pugl-upstream/src/implementation.h" - -const char* -puglGetWindowTitle(const PuglView* view) -{ - return view->title; -} diff --git a/dpf/dgl/src/pugl-extra/extras.h b/dpf/dgl/src/pugl-extra/extras.h deleted file mode 100644 index f9ff4d6..0000000 --- a/dpf/dgl/src/pugl-extra/extras.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright (C) 2012-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl.h pugl extra API for DPF. -*/ - -#ifndef PUGL_EXTRAS_PUGL_H -#define PUGL_EXTRAS_PUGL_H - -#include "../pugl-upstream/include/pugl/pugl.h" - -PUGL_BEGIN_DECLS - -PUGL_API const char* -puglGetWindowTitle(const PuglView* view); - -PUGL_API int -puglGetViewHint(const PuglView* view, PuglViewHint hint); - -PUGL_API void -puglRaiseWindow(PuglView* view); - -PUGL_API void -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height); - -PUGL_API void -puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect); - -#ifdef DISTRHO_OS_WINDOWS -PUGL_API void -puglWin32SetWindowResizable(PuglView* view, bool resizable); -#endif - -PUGL_END_DECLS - -#endif // PUGL_EXTRAS_PUGL_H diff --git a/dpf/dgl/src/pugl-extra/haiku.cpp b/dpf/dgl/src/pugl-extra/haiku.cpp index b01b662..13db156 100644 --- a/dpf/dgl/src/pugl-extra/haiku.cpp +++ b/dpf/dgl/src/pugl-extra/haiku.cpp @@ -1,28 +1,348 @@ -/* - Copyright 2012-2019 David Robillard - Copyright 2019-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file mac.cpp HaikuOS implementation. -*/ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC #include "haiku.h" -#include "pugl/detail/implementation.h" +#include "../pugl-upstream/src/internal.h" +class PuglHaikuView : public BView +{ + PuglView* const view; + +public: + PuglHaikuView(PuglView* const v) + : BView(NULL, B_FULL_UPDATE_ON_RESIZE|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE), + view(v) {} + +protected: + void GetPreferredSize(float* width, float* height) override + { + d_stdout("%s %i", __func__, __LINE__); + if (width != nullptr) + *width = view->frame.width; + if (height != nullptr) + *height = view->frame.height; + d_stdout("%s %i", __func__, __LINE__); + } +}; + +class PuglHaikuWindow : public BWindow +{ + PuglView* const view; + +public: + PuglHaikuWindow(PuglView* const v) + : BWindow(BRect(1.0f), "DPF-Window", B_TITLED_WINDOW, 0x0), + view(v) {} + +// protected: +// bool QuitRequested() override +// { +// d_stdout("%s %i", __func__, __LINE__); +// if (puglView->closeFunc) { +// puglView->closeFunc(puglView); +// puglView->redisplay = false; +// } +// needsQuit = false; +// d_stdout("%s %i", __func__, __LINE__); +// return true; +// } +}; + +BApplication* s_app = NULL; + +PuglWorldInternals* +puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags) +{ + if (!s_app) { + status_t status; + s_app = new BApplication("application/x-vnd.pugl-application", &status); + + if (status != B_OK) { + delete s_app; + return NULL; + } + } + + PuglWorldInternals* impl = + (PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); + + impl->app = s_app; + return impl; +} + +void* +puglGetNativeWorld(PuglWorld* const world) +{ + return world->impl->app; +} + +PuglInternals* +puglInitViewInternals(PuglWorld* const world) +{ + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + + return impl; +} + +PuglStatus +puglRealize(PuglView* const view) +{ + PuglInternals* const impl = view->impl; + PuglStatus st = PUGL_SUCCESS; + + // Ensure that we're unrealized and that a reasonable backend has been set + if (impl->view) { + return PUGL_FAILURE; + } + if (!view->backend || !view->backend->configure) { + return PUGL_BAD_BACKEND; + } + + // Set the size to the default if it has not already been set + if (view->frame.width <= 0.0 || view->frame.height <= 0.0) { + const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!defaultSize.width || !defaultSize.height) { + return PUGL_BAD_CONFIGURATION; + } + + view->frame.width = defaultSize.width; + view->frame.height = defaultSize.height; + } + + // Center top-level windows if a position has not been set + if (!view->parent && !view->frame.x && !view->frame.y) { + // TODO + } + + if (!view->parent) { + impl->window = new PuglHaikuWindow(view); + impl->window->Lock(); + } + + impl->view = new PuglHaikuView(view); + + if (view->parent) { + BView* const pview = (BView*)view->parent; + pview->AddChild(impl->view); + } else { + impl->window->AddChild(impl->view); + } + + // Configure and create the backend + if ((st = view->backend->configure(view)) || (st = view->backend->create(view))) { + view->backend->destroy(view); + return st; + } + + if (view->title) { + puglSetWindowTitle(view, view->title); + } + + if (view->transientParent) { + puglSetTransientParent(view, view->transientParent); + } + + puglDispatchSimpleEvent(view, PUGL_CREATE); + + if (impl->window) { + impl->window->Unlock(); + } + + return PUGL_SUCCESS; +} + +PuglStatus +puglShow(PuglView* const view) +{ + PuglInternals* const impl = view->impl; + if (impl->window) { + impl->window->Show(); + } else { + impl->view->Show(); + } + return PUGL_SUCCESS; +} + +PuglStatus +puglHide(PuglView* const view) +{ + PuglInternals* const impl = view->impl; + if (impl->window) { + impl->window->Hide(); + } else { + impl->view->Hide(); + } + return PUGL_SUCCESS; +} + +void +puglFreeViewInternals(PuglView* const view) +{ + if (view && view->impl) { + PuglInternals* const impl = view->impl; + if (view->backend) { + view->backend->destroy(view); + } + if (impl->view) { + if (impl->window) { + impl->window->RemoveChild(impl->view); + } + delete impl->view; + delete impl->window; + } + free(impl); + } +} + +void +puglFreeWorldInternals(PuglWorld* const world) +{ +// if (world->impl->app != nullptr && world->impl->app->CountWindows() == 0) { +// delete world->impl->app; +// s_app = NULL; +// } + free(world->impl); +} + +PuglStatus +puglGrabFocus(PuglView*) +{ + return PUGL_UNSUPPORTED; +} + +double +puglGetScaleFactor(const PuglView* const view) +{ + return 1.0; +} + +double +puglGetTime(const PuglWorld* const world) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) - + world->startTime; +} + +PuglStatus +puglUpdate(PuglWorld* const world, const double timeout) +{ + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglPostRedisplay(PuglView* const view) +{ + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglPostRedisplayRect(PuglView* const view, const PuglRect rect) +{ + return PUGL_UNSUPPORTED; +} + +PuglNativeView +puglGetNativeView(PuglView* const view) +{ + return 0; +} + +PuglStatus +puglSetWindowTitle(PuglView* const view, const char* const title) +{ + puglSetString(&view->title, title); + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const PuglSpan width, + const PuglSpan height) +{ + view->sizeHints[hint].width = width; + view->sizeHints[hint].height = height; + return PUGL_SUCCESS; +} + +PuglStatus +puglStartTimer(PuglView* const view, const uintptr_t id, const double timeout) +{ + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglStopTimer(PuglView* const view, const uintptr_t id) +{ + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglPaste(PuglView* const view) +{ + return PUGL_UNSUPPORTED; +} + +PuglStatus +puglAcceptOffer(PuglView* const view, + const PuglDataOfferEvent* const offer, + const uint32_t typeIndex) +{ + return PUGL_UNSUPPORTED; +} + +uint32_t +puglGetNumClipboardTypes(const PuglView* const view) +{ + return 0u; +} + +const char* +puglGetClipboardType(const PuglView* const view, const uint32_t typeIndex) +{ + return NULL; +} + +const void* +puglGetClipboard(PuglView* const view, + const uint32_t typeIndex, + size_t* const len) +{ + return NULL; +} + +PuglStatus +puglSetClipboard(PuglView* const view, + const char* const type, + const void* const data, + const size_t len) +{ + return PUGL_FAILURE; +} + +PuglStatus +puglSetCursor(PuglView* const view, const PuglCursor cursor) +{ + return PUGL_FAILURE; +} + +PuglStatus +puglSetTransientParent(PuglView* const view, const PuglNativeView parent) +{ + return PUGL_FAILURE; +} + +PuglStatus +puglSetPosition(PuglView* const view, const int x, const int y) +{ + return PUGL_FAILURE; +} + +#if 0 PuglStatus puglGrabFocus(PuglView* view) { @@ -79,3 +399,4 @@ void setVisible(const bool yesNo) bView->Hide(); } } +#endif diff --git a/dpf/dgl/src/pugl-extra/haiku.h b/dpf/dgl/src/pugl-extra/haiku.h index 9930a8d..f98c5d7 100644 --- a/dpf/dgl/src/pugl-extra/haiku.h +++ b/dpf/dgl/src/pugl-extra/haiku.h @@ -1,35 +1,25 @@ -/* - Copyright 2012-2019 David Robillard - Copyright 2019-2020 Filipe Coelho +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. +#ifndef PUGL_SRC_HAIKU_H +#define PUGL_SRC_HAIKU_H - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file haiku.h Shared definitions for HaikuOS implementation. -*/ +#include "../pugl-upstream/src/types.h" #include "pugl/pugl.h" #include #include -// using? interface/ struct PuglWorldInternalsImpl { - BApplication* app; + BApplication* app; }; struct PuglInternalsImpl { - BViewType* view; - BWindow* window; + PuglSurface* surface; + BView* view; + BWindow* window; }; + +#endif // PUGL_SRC_HAIKU_H diff --git a/dpf/dgl/src/pugl-extra/haiku_gl.cpp b/dpf/dgl/src/pugl-extra/haiku_gl.cpp new file mode 100644 index 0000000..1b8fc1f --- /dev/null +++ b/dpf/dgl/src/pugl-extra/haiku_gl.cpp @@ -0,0 +1,87 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#include "../pugl-upstream/src/stub.h" +#include "haiku.h" + +#include "pugl/pugl.h" + +#include +#include + +#include +#include + +typedef struct { + BGLView* view; +} PuglHaikuGlSurface; + +static PuglStatus +puglHaikuGlConfigure(PuglView* view) +{ + PuglInternals* const impl = view->impl; + + PuglHaikuGlSurface* const surface = + (PuglHaikuGlSurface*)calloc(1, sizeof(PuglHaikuGlSurface)); + impl->surface = surface; + + return PUGL_SUCCESS; +} + +PUGL_WARN_UNUSED_RESULT +static PuglStatus +puglHaikuGlEnter(PuglView* const view, const PuglExposeEvent* PUGL_UNUSED(expose)) +{ + PuglHaikuGlSurface* const surface = (PuglHaikuGlSurface*)view->impl->surface; + if (!surface || !surface->view) { + return PUGL_FAILURE; + } + + surface->view->LockGL(); + return PUGL_SUCCESS; +} + +PUGL_WARN_UNUSED_RESULT +static PuglStatus +puglHaikuGlLeave(PuglView* const view, const PuglExposeEvent* const expose) +{ + PuglHaikuGlSurface* const surface = (PuglHaikuGlSurface*)view->impl->surface; + if (!surface || !surface->view) { + return PUGL_FAILURE; + } + + if (expose) + surface->view->SwapBuffers(); + + surface->view->UnlockGL(); + return PUGL_SUCCESS; +} + +static PuglStatus +puglHaikuGlCreate(PuglView* view) +{ + return PUGL_SUCCESS; +} + +static void +puglHaikuGlDestroy(PuglView* view) +{ + PuglHaikuGlSurface* surface = (PuglHaikuGlSurface*)view->impl->surface; + if (surface) { + free(surface); + view->impl->surface = NULL; + } +} + +const PuglBackend* +puglGlBackend(void) +{ + static const PuglBackend backend = {puglHaikuGlConfigure, + puglHaikuGlCreate, + puglHaikuGlDestroy, + puglHaikuGlEnter, + puglHaikuGlLeave, + puglStubGetContext}; + return &backend; +} diff --git a/dpf/dgl/src/pugl-extra/haiku_stub.cpp b/dpf/dgl/src/pugl-extra/haiku_stub.cpp new file mode 100644 index 0000000..20e018f --- /dev/null +++ b/dpf/dgl/src/pugl-extra/haiku_stub.cpp @@ -0,0 +1,24 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#include "pugl/stub.h" + +#include "../pugl-upstream/src/stub.h" + +#include "pugl/pugl.h" + +const PuglBackend* +puglStubBackend(void) +{ + static const PuglBackend backend = { + puglStubConfigure, + puglStubCreate, + puglStubDestroy, + puglStubEnter, + puglStubLeave, + puglStubGetContext, + }; + + return &backend; +} diff --git a/dpf/dgl/src/pugl-extra/mac.m b/dpf/dgl/src/pugl-extra/mac.m deleted file mode 100644 index 853ac24..0000000 --- a/dpf/dgl/src/pugl-extra/mac.m +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright (C) 2012-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file mac.m MacOS extra implementation for DPF. -*/ - -#include "pugl/detail/mac.h" - -void -puglRaiseWindow(PuglView* view) -{ -} - -void -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) -{ - // NOTE: pugl mac code does nothing with x and y - const PuglRect frame = { 0.0, 0.0, (double)width, (double)height }; - puglSetFrame(view, frame); -} - -void -puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) -{ - // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio - view->minWidth = width; - view->minHeight = height; - - [view->impl->window setContentMinSize:sizePoints(view, width, height)]; - - if (aspect) { - [view->impl->window setContentAspectRatio:sizePoints(view, width, height)]; - } -} diff --git a/dpf/dgl/src/pugl-extra/wasm.c b/dpf/dgl/src/pugl-extra/wasm.c new file mode 100644 index 0000000..2dfde18 --- /dev/null +++ b/dpf/dgl/src/pugl-extra/wasm.c @@ -0,0 +1,1141 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#include "wasm.h" + +#include "../pugl-upstream/src/internal.h" + +#include + +#include + +#ifdef __cplusplus +# define PUGL_INIT_STRUCT \ + {} +#else +# define PUGL_INIT_STRUCT \ + { \ + 0 \ + } +#endif + +#ifdef __MOD_DEVICES__ +# define MOD_SCALE_FACTOR_MULT 1 +#endif + +// #define PUGL_WASM_AUTO_POINTER_LOCK +// #define PUGL_WASM_NO_KEYBOARD_INPUT +// #define PUGL_WASM_NO_MOUSEWHEEL_INPUT + +PuglWorldInternals* +puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags) +{ + PuglWorldInternals* impl = + (PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals)); + + impl->scaleFactor = emscripten_get_device_pixel_ratio(); +#ifdef __MOD_DEVICES__ + impl->scaleFactor *= MOD_SCALE_FACTOR_MULT; +#endif + + printf("DONE: %s %d | -> %f\n", __func__, __LINE__, impl->scaleFactor); + + return impl; +} + +void* +puglGetNativeWorld(PuglWorld*) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + return NULL; +} + +PuglInternals* +puglInitViewInternals(PuglWorld* const world) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + + impl->buttonPressTimeout = -1; + impl->supportsTouch = PUGL_DONT_CARE; // not yet known + +#ifdef PUGL_WASM_ASYNC_CLIPBOARD + impl->supportsClipboardRead = (PuglViewHintValue)EM_ASM_INT({ + if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.readText) === 'function' && window.isSecureContext) { + return 1; // PUGL_TRUE + } + return 0; // PUGL_FALSE + }); + + impl->supportsClipboardWrite = (PuglViewHintValue)EM_ASM_INT({ + if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.writeText) === 'function' && window.isSecureContext) { + return 1; // PUGL_TRUE + } + if (typeof(document.queryCommandSupported) !== 'undefined' && document.queryCommandSupported("copy")) { + return 1; // PUGL_TRUE + } + return 0; // PUGL_FALSE + }); +#endif + + return impl; +} + +static PuglStatus +puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event) +{ + PuglStatus st0 = PUGL_SUCCESS; + PuglStatus st1 = PUGL_SUCCESS; + + if (!(st0 = view->backend->enter(view, NULL))) { + st0 = view->eventFunc(view, event); + st1 = view->backend->leave(view, NULL); + } + + return st0 ? st0 : st1; +} + +static PuglMods +translateModifiers(const EM_BOOL ctrlKey, + const EM_BOOL shiftKey, + const EM_BOOL altKey, + const EM_BOOL metaKey) +{ + return (ctrlKey ? PUGL_MOD_CTRL : 0u) | + (shiftKey ? PUGL_MOD_SHIFT : 0u) | + (altKey ? PUGL_MOD_ALT : 0u) | + (metaKey ? PUGL_MOD_SUPER : 0u); +} + +#ifndef PUGL_WASM_NO_KEYBOARD_INPUT +static PuglKey +keyCodeToSpecial(const unsigned long code, const unsigned long location) +{ + switch (code) { + case 0x08: return PUGL_KEY_BACKSPACE; + case 0x1B: return PUGL_KEY_ESCAPE; + case 0x2E: return PUGL_KEY_DELETE; + case 0x70: return PUGL_KEY_F1; + case 0x71: return PUGL_KEY_F2; + case 0x72: return PUGL_KEY_F3; + case 0x73: return PUGL_KEY_F4; + case 0x74: return PUGL_KEY_F5; + case 0x75: return PUGL_KEY_F6; + case 0x76: return PUGL_KEY_F7; + case 0x77: return PUGL_KEY_F8; + case 0x78: return PUGL_KEY_F9; + case 0x79: return PUGL_KEY_F10; + case 0x7A: return PUGL_KEY_F11; + case 0x7B: return PUGL_KEY_F12; + case 0x25: return PUGL_KEY_LEFT; + case 0x26: return PUGL_KEY_UP; + case 0x27: return PUGL_KEY_RIGHT; + case 0x28: return PUGL_KEY_DOWN; + case 0x21: return PUGL_KEY_PAGE_UP; + case 0x22: return PUGL_KEY_PAGE_DOWN; + case 0x24: return PUGL_KEY_HOME; + case 0x23: return PUGL_KEY_END; + case 0x2D: return PUGL_KEY_INSERT; + case 0x10: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_SHIFT_R : PUGL_KEY_SHIFT_L; + case 0x11: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_CTRL_R : PUGL_KEY_CTRL_L; + case 0x12: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_ALT_R : PUGL_KEY_ALT_L; + case 0xE0: return location == DOM_KEY_LOCATION_RIGHT ? PUGL_KEY_SUPER_R : PUGL_KEY_SUPER_L; + case 0x5D: return PUGL_KEY_MENU; + case 0x14: return PUGL_KEY_CAPS_LOCK; + case 0x91: return PUGL_KEY_SCROLL_LOCK; + case 0x90: return PUGL_KEY_NUM_LOCK; + case 0x2C: return PUGL_KEY_PRINT_SCREEN; + case 0x13: return PUGL_KEY_PAUSE; + case '\r': return (PuglKey)'\r'; + default: break; + } + + return (PuglKey)0; +} + +static bool +decodeCharacterString(const unsigned long keyCode, + const EM_UTF8 key[EM_HTML5_SHORT_STRING_LEN_BYTES], + char str[8]) +{ + if (key[1] == 0) + { + str[0] = key[0]; + return true; + } + + return false; +} + +static EM_BOOL +puglKeyCallback(const int eventType, const EmscriptenKeyboardEvent* const keyEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + if (!view->visible) { + return EM_FALSE; + } + + if (keyEvent->repeat && view->hints[PUGL_IGNORE_KEY_REPEAT]) + return EM_TRUE; + + PuglStatus st0 = PUGL_SUCCESS; + PuglStatus st1 = PUGL_SUCCESS; + + const uint state = translateModifiers(keyEvent->ctrlKey, + keyEvent->shiftKey, + keyEvent->altKey, + keyEvent->metaKey); + + const PuglKey special = keyCodeToSpecial(keyEvent->keyCode, keyEvent->location); + + uint key = keyEvent->key[0] >= ' ' && keyEvent->key[0] <= '~' && keyEvent->key[1] == '\0' + ? keyEvent->key[0] + : keyEvent->keyCode; + + if (key >= 'A' && key <= 'Z' && !keyEvent->shiftKey) + key += 'a' - 'A'; + + PuglEvent event = {{PUGL_NOTHING, 0}}; + event.key.type = eventType == EMSCRIPTEN_EVENT_KEYDOWN ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE; + event.key.time = keyEvent->timestamp / 1e3; + // event.key.x = xevent.xkey.x; + // event.key.y = xevent.xkey.y; + // event.key.xRoot = xevent.xkey.x_root; + // event.key.yRoot = xevent.xkey.y_root; + event.key.key = special ? special : key; + event.key.keycode = keyEvent->keyCode; + event.key.state = state; + st0 = puglDispatchEventWithContext(view, &event); + + d_debug("key event \n" + "\tdown: %d\n" + "\trepeat: %d\n" + "\tlocation: %d\n" + "\tstate: 0x%x\n" + "\tkey[]: '%s'\n" + "\tcode[]: '%s'\n" + "\tlocale[]: '%s'\n" + "\tkeyCode: 0x%lx:'%c' [deprecated, use key]\n" + "\twhich: 0x%lx:'%c' [deprecated, use key, same as keycode?]\n" + "\tspecial: 0x%x", + eventType == EMSCRIPTEN_EVENT_KEYDOWN, + keyEvent->repeat, + keyEvent->location, + state, + keyEvent->key, + keyEvent->code, + keyEvent->locale, + keyEvent->keyCode, keyEvent->keyCode >= ' ' && keyEvent->keyCode <= '~' ? keyEvent->keyCode : 0, + keyEvent->which, keyEvent->which >= ' ' && keyEvent->which <= '~' ? keyEvent->which : 0, + special); + + if (event.type == PUGL_KEY_PRESS && !special && !(keyEvent->ctrlKey|keyEvent->altKey|keyEvent->metaKey)) { + char str[8] = PUGL_INIT_STRUCT; + + if (decodeCharacterString(keyEvent->keyCode, keyEvent->key, str)) { + d_debug("resulting string is '%s'", str); + + event.text.type = PUGL_TEXT; + event.text.character = event.key.key; + memcpy(event.text.string, str, sizeof(event.text.string)); + st1 = puglDispatchEventWithContext(view, &event); + } + } + + return (st0 ? st0 : st1) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; +} +#endif + +static EM_BOOL +puglMouseCallback(const int eventType, const EmscriptenMouseEvent* const mouseEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + if (!view->visible) { + return EM_FALSE; + } + + PuglEvent event = {{PUGL_NOTHING, 0}}; + + const double time = mouseEvent->timestamp / 1e3; + const PuglMods state = translateModifiers(mouseEvent->ctrlKey, + mouseEvent->shiftKey, + mouseEvent->altKey, + mouseEvent->metaKey); + + double scaleFactor = view->world->impl->scaleFactor; +#ifdef __MOD_DEVICES__ + if (!view->impl->isFullscreen) { + scaleFactor /= EM_ASM_DOUBLE({ + return parseFloat( + RegExp('^scale\\\((.*)\\\)$') + .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] + ); + }) * MOD_SCALE_FACTOR_MULT; + } +#endif + + // workaround missing pointer lock callback, see https://github.com/emscripten-core/emscripten/issues/9681 + EmscriptenPointerlockChangeEvent e; + if (emscripten_get_pointerlock_status(&e) == EMSCRIPTEN_RESULT_SUCCESS) + view->impl->pointerLocked = e.isActive; + +#ifdef __MOD_DEVICES__ + const long canvasX = mouseEvent->canvasX; + const long canvasY = mouseEvent->canvasY; +#else + const char* const className = view->world->className; + const double canvasX = mouseEvent->clientX - EM_ASM_DOUBLE({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().x; + }, className); + const double canvasY = mouseEvent->clientY - EM_ASM_DOUBLE({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().y; + }, className); +#endif + + switch (eventType) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + case EMSCRIPTEN_EVENT_MOUSEUP: + event.button.type = eventType == EMSCRIPTEN_EVENT_MOUSEDOWN ? PUGL_BUTTON_PRESS : PUGL_BUTTON_RELEASE; + event.button.time = time; + event.button.x = canvasX * scaleFactor; + event.button.y = canvasY * scaleFactor; + event.button.xRoot = mouseEvent->screenX * scaleFactor; + event.button.yRoot = mouseEvent->screenY * scaleFactor; + event.button.state = state; + switch (mouseEvent->button) { + case 1: + event.button.button = 2; + break; + case 2: + event.button.button = 1; + break; + default: + event.button.button = mouseEvent->button; + break; + } + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + event.motion.type = PUGL_MOTION; + event.motion.time = time; + if (view->impl->pointerLocked) { + // adjust local values for delta + const double movementX = mouseEvent->movementX * scaleFactor; + const double movementY = mouseEvent->movementY * scaleFactor; + view->impl->lastMotion.x += movementX; + view->impl->lastMotion.y += movementY; + view->impl->lastMotion.xRoot += movementX; + view->impl->lastMotion.yRoot += movementY; + // now set x, y, xRoot and yRoot + event.motion.x = view->impl->lastMotion.x; + event.motion.y = view->impl->lastMotion.y; + event.motion.xRoot = view->impl->lastMotion.xRoot; + event.motion.yRoot = view->impl->lastMotion.yRoot; + } else { + // cache values for possible pointer lock movement later + view->impl->lastMotion.x = event.motion.x = canvasX * scaleFactor; + view->impl->lastMotion.y = event.motion.y = canvasY * scaleFactor; + view->impl->lastMotion.xRoot = event.motion.xRoot = mouseEvent->screenX * scaleFactor; + view->impl->lastMotion.yRoot = event.motion.yRoot = mouseEvent->screenY * scaleFactor; + } + event.motion.state = state; + break; + case EMSCRIPTEN_EVENT_MOUSEENTER: + case EMSCRIPTEN_EVENT_MOUSELEAVE: + event.crossing.type = eventType == EMSCRIPTEN_EVENT_MOUSEENTER ? PUGL_POINTER_IN : PUGL_POINTER_OUT; + event.crossing.time = time; + event.crossing.x = canvasX * scaleFactor; + event.crossing.y = canvasY * scaleFactor; + event.crossing.xRoot = mouseEvent->screenX * scaleFactor; + event.crossing.yRoot = mouseEvent->screenY * scaleFactor; + event.crossing.state = state; + event.crossing.mode = PUGL_CROSSING_NORMAL; + break; + } + + if (event.type == PUGL_NOTHING) + return EM_FALSE; + + puglDispatchEventWithContext(view, &event); + +#ifdef PUGL_WASM_AUTO_POINTER_LOCK + switch (eventType) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + emscripten_request_pointerlock(view->world->className, false); + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + emscripten_exit_pointerlock(); + break; + } +#endif + + // note: we must always return false, otherwise canvas never gets keyboard input + return EM_FALSE; +} + +static void +puglTouchStartDelay(void* const userData) +{ + PuglView* const view = (PuglView*)userData; + PuglInternals* const impl = view->impl; + + impl->buttonPressTimeout = -1; + impl->nextButtonEvent.button.time += 2000; + puglDispatchEventWithContext(view, &impl->nextButtonEvent); +} + +static EM_BOOL +puglTouchCallback(const int eventType, const EmscriptenTouchEvent* const touchEvent, void* const userData) +{ + if (touchEvent->numTouches <= 0) { + return EM_FALSE; + } + + PuglView* const view = (PuglView*)userData; + PuglInternals* const impl = view->impl; + const char* const className = view->world->className; + + if (impl->supportsTouch == PUGL_DONT_CARE) { + impl->supportsTouch = PUGL_TRUE; + + // stop using mouse press events which conflict with touch + emscripten_set_mousedown_callback(className, view, false, NULL); + emscripten_set_mouseup_callback(className, view, false, NULL); + } + + if (!view->visible) { + return EM_FALSE; + } + + PuglEvent event = {{PUGL_NOTHING, 0}}; + + const double time = touchEvent->timestamp / 1e3; + const PuglMods state = translateModifiers(touchEvent->ctrlKey, + touchEvent->shiftKey, + touchEvent->altKey, + touchEvent->metaKey); + + double scaleFactor = view->world->impl->scaleFactor; +#ifdef __MOD_DEVICES__ + if (!view->impl->isFullscreen) { + scaleFactor /= EM_ASM_DOUBLE({ + return parseFloat( + RegExp('^scale\\\((.*)\\\)$') + .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] + ); + }) * MOD_SCALE_FACTOR_MULT; + } +#endif + + d_debug("touch %d|%s %d || %ld", + eventType, + eventType == EMSCRIPTEN_EVENT_TOUCHSTART ? "start" : + eventType == EMSCRIPTEN_EVENT_TOUCHEND ? "end" : "cancel", + touchEvent->numTouches, + impl->buttonPressTimeout); + + const EmscriptenTouchPoint* point = &touchEvent->touches[0]; + + if (impl->buttonPressTimeout != -1 || eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) { + // if we received an event while touch is active, trigger initial click now + if (impl->buttonPressTimeout != -1) { + emscripten_clear_timeout(impl->buttonPressTimeout); + impl->buttonPressTimeout = -1; + if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL) { + impl->nextButtonEvent.button.button = 0; + } + } + impl->nextButtonEvent.button.time = time; + puglDispatchEventWithContext(view, &impl->nextButtonEvent); + } + +#ifdef __MOD_DEVICES__ + const long canvasX = point->canvasX; + const long canvasY = point->canvasY; +#else + const double canvasX = point->clientX - EM_ASM_DOUBLE({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().x; + }, className); + const double canvasY = point->clientY - EM_ASM_DOUBLE({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().y; + }, className); +#endif + + switch (eventType) { + case EMSCRIPTEN_EVENT_TOUCHEND: + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + event.button.type = PUGL_BUTTON_RELEASE; + event.button.time = time; + event.button.button = eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL ? 1 : 0; + event.button.x = canvasX * scaleFactor; + event.button.y = canvasY * scaleFactor; + event.button.xRoot = point->screenX * scaleFactor; + event.button.yRoot = point->screenY * scaleFactor; + event.button.state = state; + break; + + case EMSCRIPTEN_EVENT_TOUCHSTART: + // this event can be used for a couple of things, store it until we know more + event.button.type = PUGL_BUTTON_PRESS; + event.button.time = time; + event.button.button = 1; // if no other event occurs soon, treat it as right-click + event.button.x = canvasX * scaleFactor; + event.button.y = canvasY * scaleFactor; + event.button.xRoot = point->screenX * scaleFactor; + event.button.yRoot = point->screenY * scaleFactor; + event.button.state = state; + memcpy(&impl->nextButtonEvent, &event, sizeof(PuglEvent)); + impl->buttonPressTimeout = emscripten_set_timeout(puglTouchStartDelay, 2000, view); + // fall through, moving "mouse" to touch position + + case EMSCRIPTEN_EVENT_TOUCHMOVE: + event.motion.type = PUGL_MOTION; + event.motion.time = time; + event.motion.x = canvasX * scaleFactor; + event.motion.y = canvasY * scaleFactor; + event.motion.xRoot = point->screenX * scaleFactor; + event.motion.yRoot = point->screenY * scaleFactor; + event.motion.state = state; + break; + } + + if (event.type == PUGL_NOTHING) + return EM_FALSE; + + puglDispatchEventWithContext(view, &event); + + // FIXME we must always return false?? + return EM_FALSE; +} + +static EM_BOOL +puglFocusCallback(const int eventType, const EmscriptenFocusEvent* /*const focusEvent*/, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + if (!view->visible) { + return EM_FALSE; + } + + d_debug("focus %d|%s", eventType, eventType == EMSCRIPTEN_EVENT_FOCUSIN ? "focus-in" : "focus-out"); + + PuglEvent event = {{eventType == EMSCRIPTEN_EVENT_FOCUSIN ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT, 0}}; + event.focus.mode = PUGL_CROSSING_NORMAL; + + puglDispatchEventWithContext(view, &event); + + // note: we must always return false, otherwise canvas never gets proper focus + return EM_FALSE; +} + +static EM_BOOL +puglPointerLockChangeCallback(const int eventType, const EmscriptenPointerlockChangeEvent* event, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + view->impl->pointerLocked = event->isActive; + return EM_TRUE; +} + +#ifndef PUGL_WASM_NO_MOUSEWHEEL_INPUT +static EM_BOOL +puglWheelCallback(const int eventType, const EmscriptenWheelEvent* const wheelEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + if (!view->visible) { + return EM_FALSE; + } + + double scaleFactor = view->world->impl->scaleFactor; +#ifdef __MOD_DEVICES__ + if (!view->impl->isFullscreen) { + scaleFactor /= EM_ASM_DOUBLE({ + return parseFloat( + RegExp('^scale\\\((.*)\\\)$') + .exec(document.getElementById("pedalboard-dashboard").style.transform)[1] + ); + }) * MOD_SCALE_FACTOR_MULT; + } +#endif + +#ifdef __MOD_DEVICES__ + const long canvasX = wheelEvent->mouse.canvasX; + const long canvasY = wheelEvent->mouse.canvasY; +#else + const char* const className = view->world->className; + const double canvasX = wheelEvent->mouse.canvasX - EM_ASM_INT({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().x; + }, className); + const double canvasY = wheelEvent->mouse.canvasY - EM_ASM_INT({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.getBoundingClientRect().y; + }, className); +#endif + + PuglEvent event = {{PUGL_SCROLL, 0}}; + event.scroll.time = wheelEvent->mouse.timestamp / 1e3; + event.scroll.x = canvasX; + event.scroll.y = canvasY; + event.scroll.xRoot = wheelEvent->mouse.screenX; + event.scroll.yRoot = wheelEvent->mouse.screenY; + event.scroll.state = translateModifiers(wheelEvent->mouse.ctrlKey, + wheelEvent->mouse.shiftKey, + wheelEvent->mouse.altKey, + wheelEvent->mouse.metaKey); + event.scroll.direction = PUGL_SCROLL_SMOOTH; + // FIXME handle wheelEvent->deltaMode + event.scroll.dx = wheelEvent->deltaX * 0.01 * scaleFactor; + event.scroll.dy = -wheelEvent->deltaY * 0.01 * scaleFactor; + + return puglDispatchEventWithContext(view, &event) == PUGL_SUCCESS ? EM_TRUE : EM_FALSE; +} +#endif + +static EM_BOOL +puglUiCallback(const int eventType, const EmscriptenUiEvent* const uiEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + const char* const className = view->world->className; + + // FIXME + const int width = EM_ASM_INT({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio); + return canvasWrapper.clientWidth; + }, className); + + const int height = EM_ASM_INT({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + return canvasWrapper.clientHeight; + }, className); + + if (!width || !height) + return EM_FALSE; + + double scaleFactor = emscripten_get_device_pixel_ratio(); +#ifdef __MOD_DEVICES__ + scaleFactor *= MOD_SCALE_FACTOR_MULT; +#endif + view->world->impl->scaleFactor = scaleFactor; + + PuglEvent event = {{PUGL_CONFIGURE, 0}}; + event.configure.x = view->frame.x; + event.configure.y = view->frame.y; + event.configure.width = width * scaleFactor; + event.configure.height = height * scaleFactor; + puglDispatchEvent(view, &event); + + emscripten_set_canvas_element_size(view->world->className, width * scaleFactor, height * scaleFactor); + return EM_TRUE; +} + +static EM_BOOL +puglFullscreenChangeCallback(const int eventType, const EmscriptenFullscreenChangeEvent* const fscEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + view->impl->isFullscreen = fscEvent->isFullscreen; + + double scaleFactor = emscripten_get_device_pixel_ratio(); +#ifdef __MOD_DEVICES__ + scaleFactor *= MOD_SCALE_FACTOR_MULT; +#endif + view->world->impl->scaleFactor = scaleFactor; + + if (fscEvent->isFullscreen) { + PuglEvent event = {{PUGL_CONFIGURE, 0}}; + event.configure.x = 0; + event.configure.y = 0; + event.configure.width = fscEvent->elementWidth * scaleFactor; + event.configure.height = fscEvent->elementHeight * scaleFactor; + puglDispatchEvent(view, &event); + + emscripten_set_canvas_element_size(view->world->className, + fscEvent->elementWidth * scaleFactor, + fscEvent->elementHeight * scaleFactor); + +#ifdef __MOD_DEVICES__ + EM_ASM({ + document.getElementById("pedalboard-dashboard").style.transform = "scale(1.0)"; + }); +#endif + return EM_TRUE; + } + + return puglUiCallback(0, NULL, userData); +} + +static EM_BOOL +puglVisibilityChangeCallback(const int eventType, const EmscriptenVisibilityChangeEvent* const visibilityChangeEvent, void* const userData) +{ + PuglView* const view = (PuglView*)userData; + + view->visible = visibilityChangeEvent->hidden == EM_FALSE; + PuglEvent event = {{ view->visible ? PUGL_MAP : PUGL_UNMAP, 0}}; + puglDispatchEvent(view, &event); + return EM_FALSE; +} + +PuglStatus +puglRealize(PuglView* const view) +{ + printf("TODO: %s %d\n", __func__, __LINE__); + PuglStatus st = PUGL_SUCCESS; + + // Ensure that we do not have a parent + if (view->parent) { + printf("TODO: %s %d\n", __func__, __LINE__); + return PUGL_FAILURE; + } + + if (!view->backend || !view->backend->configure) { + printf("TODO: %s %d\n", __func__, __LINE__); + return PUGL_BAD_BACKEND; + } + + const char* const className = view->world->className; + d_stdout("className is %s", className); + + // Set the size to the default if it has not already been set + if (view->frame.width <= 0.0 && view->frame.height <= 0.0) { + PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE]; + if (!defaultSize.width || !defaultSize.height) { + return PUGL_BAD_CONFIGURATION; + } + + view->frame.width = defaultSize.width; + view->frame.height = defaultSize.height; + } + + // Configure and create the backend + if ((st = view->backend->configure(view)) || (st = view->backend->create(view))) { + view->backend->destroy(view); + return st; + } + + if (view->title) { + puglSetWindowTitle(view, view->title); + } + + puglDispatchSimpleEvent(view, PUGL_CREATE); + + PuglEvent event = {{PUGL_CONFIGURE, 0}}; + event.configure.x = view->frame.x; + event.configure.y = view->frame.y; + event.configure.width = view->frame.width; + event.configure.height = view->frame.height; + puglDispatchEvent(view, &event); + + EM_ASM({ + var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement; + canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio); + }, className); + + emscripten_set_canvas_element_size(className, view->frame.width, view->frame.height); +#ifndef PUGL_WASM_NO_KEYBOARD_INPUT +// emscripten_set_keypress_callback(className, view, false, puglKeyCallback); + emscripten_set_keydown_callback(className, view, false, puglKeyCallback); + emscripten_set_keyup_callback(className, view, false, puglKeyCallback); +#endif + emscripten_set_touchstart_callback(className, view, false, puglTouchCallback); + emscripten_set_touchend_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); + emscripten_set_touchmove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); + emscripten_set_touchcancel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglTouchCallback); + emscripten_set_mousedown_callback(className, view, false, puglMouseCallback); + emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglMouseCallback); + emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglMouseCallback); + emscripten_set_mouseenter_callback(className, view, false, puglMouseCallback); + emscripten_set_mouseleave_callback(className, view, false, puglMouseCallback); + emscripten_set_focusin_callback(className, view, false, puglFocusCallback); + emscripten_set_focusout_callback(className, view, false, puglFocusCallback); +#ifndef PUGL_WASM_NO_MOUSEWHEEL_INPUT + emscripten_set_wheel_callback(className, view, false, puglWheelCallback); +#endif + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglPointerLockChangeCallback); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglUiCallback); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, view, false, puglFullscreenChangeCallback); + emscripten_set_visibilitychange_callback(view, false, puglVisibilityChangeCallback); + + printf("TODO: %s %d\n", __func__, __LINE__); + return PUGL_SUCCESS; +} + +PuglStatus +puglShow(PuglView* const view) +{ + view->visible = true; + view->impl->needsRepaint = true; + return puglPostRedisplay(view); +} + +PuglStatus +puglHide(PuglView* const view) +{ + view->visible = false; + return PUGL_FAILURE; +} + +void +puglFreeViewInternals(PuglView* const view) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + if (view && view->impl) { + if (view->backend) { + // unregister the window events, to make sure no callbacks to old views are triggered + emscripten_set_touchend_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_touchmove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_touchcancel_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, false, NULL); + emscripten_set_visibilitychange_callback(NULL, false, NULL); + view->backend->destroy(view); + } + free(view->impl->clipboardData); + free(view->impl->timers); + free(view->impl); + } +} + +void +puglFreeWorldInternals(PuglWorld* const world) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + free(world->impl); +} + +PuglStatus +puglGrabFocus(PuglView*) +{ + return PUGL_FAILURE; +} + +double +puglGetScaleFactor(const PuglView* const view) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + return view->world->impl->scaleFactor; +} + +double +puglGetTime(const PuglWorld*) +{ + return emscripten_get_now() / 1e3; +} + +PuglStatus +puglUpdate(PuglWorld* const world, const double timeout) +{ + for (size_t i = 0; i < world->numViews; ++i) { + PuglView* const view = world->views[i]; + + if (!view->visible) { + continue; + } + + puglDispatchSimpleEvent(view, PUGL_UPDATE); + + if (!view->impl->needsRepaint) { + continue; + } + + view->impl->needsRepaint = false; + + PuglEvent event = {{PUGL_EXPOSE, 0}}; + event.expose.x = view->frame.x; + event.expose.y = view->frame.y; + event.expose.width = view->frame.width; + event.expose.height = view->frame.height; + puglDispatchEvent(view, &event); + } + + return PUGL_SUCCESS; +} + +PuglStatus +puglPostRedisplay(PuglView* const view) +{ + view->impl->needsRepaint = true; + return PUGL_SUCCESS; +} + +PuglStatus +puglPostRedisplayRect(PuglView* const view, const PuglRect rect) +{ + view->impl->needsRepaint = true; + return PUGL_FAILURE; +} + +PuglNativeView +puglGetNativeView(PuglView* const view) +{ + return 0; +} + +PuglStatus +puglSetWindowTitle(PuglView* const view, const char* const title) +{ + puglSetString(&view->title, title); + emscripten_set_window_title(title); + return PUGL_SUCCESS; +} + +PuglStatus +puglSetSizeHint(PuglView* const view, + const PuglSizeHint hint, + const PuglSpan width, + const PuglSpan height) +{ + view->sizeHints[hint].width = width; + view->sizeHints[hint].height = height; + return PUGL_SUCCESS; +} + +static EM_BOOL +puglTimerLoopCallback(double timeout, void* const arg) +{ + PuglTimer* const timer = (PuglTimer*)arg; + PuglInternals* const impl = timer->view->impl; + + // only handle active timers + for (uint32_t i=0; inumTimers; ++i) + { + if (impl->timers[i].id == timer->id) + { + PuglEvent event = {{PUGL_TIMER, 0}}; + event.timer.id = timer->id; + puglDispatchEventWithContext(timer->view, &event); + return EM_TRUE; + } + } + + return EM_FALSE; + + // unused + (void)timeout; +} + +PuglStatus +puglStartTimer(PuglView* const view, const uintptr_t id, const double timeout) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + PuglInternals* const impl = view->impl; + const uint32_t timerIndex = impl->numTimers++; + + if (impl->timers == NULL) + impl->timers = (PuglTimer*)malloc(sizeof(PuglTimer)); + else + impl->timers = (PuglTimer*)realloc(impl->timers, sizeof(PuglTimer) * timerIndex); + + PuglTimer* const timer = &impl->timers[timerIndex]; + timer->view = view; + timer->id = id; + + emscripten_set_timeout_loop(puglTimerLoopCallback, timeout * 1e3, timer); + return PUGL_SUCCESS; +} + +PuglStatus +puglStopTimer(PuglView* const view, const uintptr_t id) +{ + printf("DONE: %s %d\n", __func__, __LINE__); + PuglInternals* const impl = view->impl; + + if (impl->timers == NULL || impl->numTimers == 0) + return PUGL_FAILURE; + + for (uint32_t i=0; inumTimers; ++i) + { + if (impl->timers[i].id == id) + { + memmove(impl->timers + i, impl->timers + (i + 1), sizeof(PuglTimer) * (impl->numTimers - 1)); + --impl->numTimers; + return PUGL_SUCCESS; + } + } + + return PUGL_FAILURE; +} + +#ifdef PUGL_WASM_ASYNC_CLIPBOARD +EM_JS(char*, puglGetAsyncClipboardData, (), { + var text = Asyncify.handleSleep(function(wakeUp) { + navigator.clipboard.readText() + .then(function(text) { + wakeUp(text); + }) + .catch(function() { + wakeUp(""); + }); + }); + if (!text.length) { + return null; + } + var length = lengthBytesUTF8(text) + 1; + var str = _malloc(length); + stringToUTF8(text, str, length); + return str; +}); +#endif + +PuglStatus +puglPaste(PuglView* const view) +{ +#ifdef PUGL_WASM_ASYNC_CLIPBOARD + // abort early if we already know it is not supported + if (view->impl->supportsClipboardRead == PUGL_FALSE) { + return PUGL_UNSUPPORTED; + } + + free(view->impl->clipboardData); + view->impl->clipboardData = puglGetAsyncClipboardData(); +#endif + + if (view->impl->clipboardData == NULL) { + return PUGL_FAILURE; + } + + const PuglDataOfferEvent offer = { + PUGL_DATA_OFFER, + 0, + emscripten_get_now() / 1e3, + }; + + PuglEvent offerEvent; + offerEvent.offer = offer; + puglDispatchEvent(view, &offerEvent); + return PUGL_SUCCESS; +} + +PuglStatus +puglAcceptOffer(PuglView* const view, + const PuglDataOfferEvent* const offer, + const uint32_t typeIndex) +{ + if (typeIndex != 0) { + return PUGL_UNSUPPORTED; + } + + const PuglDataEvent data = { + PUGL_DATA, + 0, + emscripten_get_now() / 1e3, + 0, + }; + + PuglEvent dataEvent; + dataEvent.data = data; + puglDispatchEvent(view, &dataEvent); + return PUGL_SUCCESS; +} + +uint32_t +puglGetNumClipboardTypes(const PuglView* const view) +{ + return view->impl->clipboardData != NULL ? 1u : 0u; +} + +const char* +puglGetClipboardType(const PuglView* const view, const uint32_t typeIndex) +{ + return (typeIndex == 0 && view->impl->clipboardData != NULL) + ? "text/plain" + : NULL; +} + +const void* +puglGetClipboard(PuglView* const view, + const uint32_t typeIndex, + size_t* const len) +{ + return view->impl->clipboardData; +} + +PuglStatus +puglSetClipboard(PuglView* const view, + const char* const type, + const void* const data, + const size_t len) +{ + // only utf8 text supported for now + if (type != NULL && strcmp(type, "text/plain") != 0) { + return PUGL_UNSUPPORTED; + } + + const char* const className = view->world->className; + const char* const text = (const char*)data; + +#ifdef PUGL_WASM_ASYNC_CLIPBOARD + // abort early if we already know it is not supported + if (view->impl->supportsClipboardWrite == PUGL_FALSE) { + return PUGL_UNSUPPORTED; + } +#else + puglSetString(&view->impl->clipboardData, text); +#endif + + EM_ASM({ + if (typeof(navigator.clipboard) !== 'undefined' && typeof(navigator.clipboard.writeText) === 'function' && window.isSecureContext) { + navigator.clipboard.writeText(UTF8ToString($1)); + } else { + var canvasClipboardObjName = UTF8ToString($0) + "_clipboard"; + var canvasClipboardElem = document.getElementById(canvasClipboardObjName); + + if (!canvasClipboardElem) { + canvasClipboardElem = document.createElement('textarea'); + canvasClipboardElem.id = canvasClipboardObjName; + canvasClipboardElem.style.position = 'fixed'; + canvasClipboardElem.style.whiteSpace = 'pre'; + canvasClipboardElem.style.zIndex = '-1'; + canvasClipboardElem.setAttribute('readonly', true); + document.body.appendChild(canvasClipboardElem); + } + + canvasClipboardElem.textContent = UTF8ToString($1); + canvasClipboardElem.select(); + document.execCommand("copy"); + } + }, className, text); + + // FIXME proper return status + return PUGL_SUCCESS; +} + +PuglStatus +puglSetCursor(PuglView* const view, const PuglCursor cursor) +{ + printf("TODO: %s %d\n", __func__, __LINE__); + return PUGL_FAILURE; +} + +PuglStatus +puglSetTransientParent(PuglView* const view, const PuglNativeView parent) +{ + printf("TODO: %s %d\n", __func__, __LINE__); + view->transientParent = parent; + return PUGL_FAILURE; +} + +PuglStatus +puglSetPosition(PuglView* const view, const int x, const int y) +{ + printf("TODO: %s %d\n", __func__, __LINE__); + + if (x > INT16_MAX || y > INT16_MAX) { + return PUGL_BAD_PARAMETER; + } + + view->frame.x = (PuglCoord)x; + view->frame.y = (PuglCoord)y; + return PUGL_FAILURE; +} diff --git a/dpf/dgl/src/pugl-extra/wasm.h b/dpf/dgl/src/pugl-extra/wasm.h new file mode 100644 index 0000000..e26ee33 --- /dev/null +++ b/dpf/dgl/src/pugl-extra/wasm.h @@ -0,0 +1,45 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#ifndef PUGL_SRC_WASM_H +#define PUGL_SRC_WASM_H + +#include "../pugl-upstream/src/types.h" + +#include "pugl/pugl.h" + +// #define PUGL_WASM_ASYNC_CLIPBOARD + +struct PuglTimer { + PuglView* view; + uintptr_t id; +}; + +struct PuglWorldInternalsImpl { + double scaleFactor; +}; + +struct LastMotionValues { + double x, y, xRoot, yRoot; +}; + +struct PuglInternalsImpl { + PuglSurface* surface; + bool isFullscreen; + bool needsRepaint; + bool pointerLocked; + uint32_t numTimers; + LastMotionValues lastMotion; + long buttonPressTimeout; + PuglEvent nextButtonEvent; +#ifdef PUGL_WASM_ASYNC_CLIPBOARD + PuglViewHintValue supportsClipboardRead; + PuglViewHintValue supportsClipboardWrite; +#endif + PuglViewHintValue supportsTouch; + char* clipboardData; + struct PuglTimer* timers; +}; + +#endif // PUGL_SRC_WASM_H diff --git a/dpf/dgl/src/pugl-extra/wasm_gl.c b/dpf/dgl/src/pugl-extra/wasm_gl.c new file mode 100644 index 0000000..98f5b5c --- /dev/null +++ b/dpf/dgl/src/pugl-extra/wasm_gl.c @@ -0,0 +1,228 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#include "../pugl-upstream/src/stub.h" +#include "wasm.h" + +#include "pugl/pugl.h" + +#include +#include + +#include + +// for performance reasons we can keep a single EGL context always active +#define PUGL_WASM_SINGLE_EGL_CONTEXT + +typedef struct { + EGLDisplay display; + EGLConfig config; + EGLContext context; + EGLSurface surface; +} PuglWasmGlSurface; + +static EGLint +puglWasmGlHintValue(const int value) +{ + return value == PUGL_DONT_CARE ? EGL_DONT_CARE : value; +} + +static int +puglWasmGlGetAttrib(const EGLDisplay display, + const EGLConfig config, + const EGLint attrib) +{ + EGLint value = 0; + eglGetConfigAttrib(display, config, attrib, &value); + return value; +} + +static PuglStatus +puglWasmGlConfigure(PuglView* view) +{ + PuglInternals* const impl = view->impl; + + const EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (display == EGL_NO_DISPLAY) { + return PUGL_CREATE_CONTEXT_FAILED; + } + + int major, minor; + if (eglInitialize(display, &major, &minor) != EGL_TRUE) { + return PUGL_CREATE_CONTEXT_FAILED; + } + + EGLConfig config; + int numConfigs; + + if (eglGetConfigs(display, &config, 1, &numConfigs) != EGL_TRUE || numConfigs != 1) { + eglTerminate(display); + return PUGL_CREATE_CONTEXT_FAILED; + } + + // clang-format off + const EGLint attrs[] = { + /* + GLX_X_RENDERABLE, True, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + EGL_SAMPLE_BUFFERS, view->hints[PUGL_MULTI_SAMPLE] ? 1 : 0, + */ + EGL_SAMPLES, puglWasmGlHintValue(view->hints[PUGL_SAMPLES]), + EGL_RED_SIZE, puglWasmGlHintValue(view->hints[PUGL_RED_BITS]), + EGL_GREEN_SIZE, puglWasmGlHintValue(view->hints[PUGL_GREEN_BITS]), + EGL_BLUE_SIZE, puglWasmGlHintValue(view->hints[PUGL_BLUE_BITS]), + EGL_ALPHA_SIZE, puglWasmGlHintValue(view->hints[PUGL_ALPHA_BITS]), + EGL_DEPTH_SIZE, puglWasmGlHintValue(view->hints[PUGL_DEPTH_BITS]), + EGL_STENCIL_SIZE, puglWasmGlHintValue(view->hints[PUGL_STENCIL_BITS]), + EGL_NONE + }; + // clang-format on + + if (eglChooseConfig(display, attrs, &config, 1, &numConfigs) != EGL_TRUE || numConfigs != 1) { + eglTerminate(display); + return PUGL_CREATE_CONTEXT_FAILED; + } + + PuglWasmGlSurface* const surface = + (PuglWasmGlSurface*)calloc(1, sizeof(PuglWasmGlSurface)); + impl->surface = surface; + + surface->display = display; + surface->config = config; + surface->context = EGL_NO_SURFACE; + surface->surface = EGL_NO_CONTEXT; + + view->hints[PUGL_RED_BITS] = + puglWasmGlGetAttrib(display, config, EGL_RED_SIZE); + view->hints[PUGL_GREEN_BITS] = + puglWasmGlGetAttrib(display, config, EGL_GREEN_SIZE); + view->hints[PUGL_BLUE_BITS] = + puglWasmGlGetAttrib(display, config, EGL_BLUE_SIZE); + view->hints[PUGL_ALPHA_BITS] = + puglWasmGlGetAttrib(display, config, EGL_ALPHA_SIZE); + view->hints[PUGL_DEPTH_BITS] = + puglWasmGlGetAttrib(display, config, EGL_DEPTH_SIZE); + view->hints[PUGL_STENCIL_BITS] = + puglWasmGlGetAttrib(display, config, EGL_STENCIL_SIZE); + view->hints[PUGL_SAMPLES] = + puglWasmGlGetAttrib(display, config, EGL_SAMPLES); + + // double-buffering is always enabled for EGL + view->hints[PUGL_DOUBLE_BUFFER] = 1; + + return PUGL_SUCCESS; +} + +PUGL_WARN_UNUSED_RESULT +static PuglStatus +puglWasmGlEnter(PuglView* view, const PuglExposeEvent* PUGL_UNUSED(expose)) +{ + PuglWasmGlSurface* const surface = (PuglWasmGlSurface*)view->impl->surface; + if (!surface || !surface->context || !surface->surface) { + return PUGL_FAILURE; + } + +#ifndef PUGL_WASM_SINGLE_EGL_CONTEXT + return eglMakeCurrent(surface->display, surface->surface, surface->surface, surface->context) ? PUGL_SUCCESS : PUGL_FAILURE; +#else + return PUGL_SUCCESS; +#endif +} + +PUGL_WARN_UNUSED_RESULT +static PuglStatus +puglWasmGlLeave(PuglView* view, const PuglExposeEvent* expose) +{ + PuglWasmGlSurface* const surface = (PuglWasmGlSurface*)view->impl->surface; + + if (expose) { // note: swap buffers always enabled for EGL + eglSwapBuffers(surface->display, surface->surface); + } + +#ifndef PUGL_WASM_SINGLE_EGL_CONTEXT + return eglMakeCurrent(surface->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ? PUGL_SUCCESS : PUGL_FAILURE; +#else + return PUGL_SUCCESS; +#endif +} + +static PuglStatus +puglWasmGlCreate(PuglView* view) +{ + PuglWasmGlSurface* const surface = (PuglWasmGlSurface*)view->impl->surface; + const EGLDisplay display = surface->display; + const EGLConfig config = surface->config; + + const EGLint attrs[] = { + EGL_CONTEXT_CLIENT_VERSION, + view->hints[PUGL_CONTEXT_VERSION_MAJOR], + + EGL_CONTEXT_MAJOR_VERSION, + view->hints[PUGL_CONTEXT_VERSION_MAJOR], + + /* + EGL_CONTEXT_MINOR_VERSION, + view->hints[PUGL_CONTEXT_VERSION_MINOR], + + EGL_CONTEXT_OPENGL_DEBUG, + (view->hints[PUGL_USE_DEBUG_CONTEXT] ? EGL_TRUE : EGL_FALSE), + + EGL_CONTEXT_OPENGL_PROFILE_MASK, + (view->hints[PUGL_USE_COMPAT_PROFILE] + ? EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT + : EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT), + */ + + EGL_NONE + }; + + surface->context = eglCreateContext(display, config, EGL_NO_CONTEXT, attrs); + + if (surface->context == EGL_NO_CONTEXT) { + return PUGL_CREATE_CONTEXT_FAILED; + } + + surface->surface = eglCreateWindowSurface(display, config, 0, NULL); + + if (surface->surface == EGL_NO_SURFACE) { + return PUGL_CREATE_CONTEXT_FAILED; + } + +#ifdef PUGL_WASM_SINGLE_EGL_CONTEXT + eglMakeCurrent(surface->display, surface->surface, surface->surface, surface->context); +#endif + + return PUGL_SUCCESS; +} + +static void +puglWasmGlDestroy(PuglView* view) +{ + PuglWasmGlSurface* surface = (PuglWasmGlSurface*)view->impl->surface; + if (surface) { + const EGLDisplay display = surface->display; + if (surface->surface != EGL_NO_SURFACE) + eglDestroySurface(display, surface->surface); + if (surface->context != EGL_NO_CONTEXT) + eglDestroyContext(display, surface->context); + eglTerminate(display); + free(surface); + view->impl->surface = NULL; + } +} + +const PuglBackend* +puglGlBackend(void) +{ + static const PuglBackend backend = {puglWasmGlConfigure, + puglWasmGlCreate, + puglWasmGlDestroy, + puglWasmGlEnter, + puglWasmGlLeave, + puglStubGetContext}; + return &backend; +} diff --git a/dpf/dgl/src/pugl-extra/wasm_stub.c b/dpf/dgl/src/pugl-extra/wasm_stub.c new file mode 100644 index 0000000..20e018f --- /dev/null +++ b/dpf/dgl/src/pugl-extra/wasm_stub.c @@ -0,0 +1,24 @@ +// Copyright 2012-2022 David Robillard +// Copyright 2021-2022 Filipe Coelho +// SPDX-License-Identifier: ISC + +#include "pugl/stub.h" + +#include "../pugl-upstream/src/stub.h" + +#include "pugl/pugl.h" + +const PuglBackend* +puglStubBackend(void) +{ + static const PuglBackend backend = { + puglStubConfigure, + puglStubCreate, + puglStubDestroy, + puglStubEnter, + puglStubLeave, + puglStubGetContext, + }; + + return &backend; +} diff --git a/dpf/dgl/src/pugl-extra/win.c b/dpf/dgl/src/pugl-extra/win.c deleted file mode 100644 index dca0db0..0000000 --- a/dpf/dgl/src/pugl-extra/win.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright (C) 2012-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file win.c Windows extra implementation for DPF. -*/ - -#include "pugl/detail/win.h" - -#include "pugl/detail/implementation.h" - -void -puglRaiseWindow(PuglView* view) -{ - SetForegroundWindow(view->impl->hwnd); - SetActiveWindow(view->impl->hwnd); - return PUGL_SUCCESS; -} - -void -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) -{ - view->frame.width = width; - view->frame.height = height; - - // NOTE the following code matches upstream pugl, except we add SWP_NOMOVE flag - if (view->impl->hwnd) { - RECT rect = { (long)frame.x, - (long)frame.y, - (long)frame.x + (long)frame.width, - (long)frame.y + (long)frame.height }; - - AdjustWindowRectEx(&rect, puglWinGetWindowFlags(view), - FALSE, - puglWinGetWindowExFlags(view)); - - SetWindowPos(view->impl->hwnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); - } -} - -void -puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) -{ - // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio, but stilL TODO on pugl - Display* display = view->world->impl->display; - - view->minWidth = width; - view->minHeight = height; - - if (aspect) { - view->minAspectX = width; - view->minAspectY = height; - view->maxAspectX = width; - view->maxAspectY = height; - } -} - -void -puglWin32RestoreWindow(PuglView* view) -{ - PuglInternals* impl = view->impl; - - ShowWindow(impl->hwnd, SW_RESTORE); - SetFocus(impl->hwnd); -} - -void -puglWin32ShowWindowCentered(PuglView* view) -{ - PuglInternals* impl = view->impl; - - RECT rectChild, rectParent; - - if (impl->transientParent != 0 && - GetWindowRect(impl->hwnd, &rectChild) && - GetWindowRect(impl->transientParent, &rectParent)) - { - SetWindowPos(impl->hwnd, (HWND)impl->transientParent, - rectParent.left + (rectChild.right-rectChild.left)/2, - rectParent.top + (rectChild.bottom-rectChild.top)/2, - 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); - } - else - { - ShowWindow(hwnd, SW_SHOWNORMAL); - } - - SetFocus(impl->hwnd); -} - -void -puglWin32SetWindowResizable(PuglView* view, bool resizable) -{ - PuglInternals* impl = view->impl; - - const int winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX - : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; - SetWindowLong(impl->hwnd, GWL_STYLE, winFlags); -} diff --git a/dpf/dgl/src/pugl-extra/x11.c b/dpf/dgl/src/pugl-extra/x11.c deleted file mode 100644 index 595473d..0000000 --- a/dpf/dgl/src/pugl-extra/x11.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright (C) 2012-2020 Filipe Coelho - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file x11.c X11 extra implementation for DPF. -*/ - -// NOTE can't import this file twice! -#ifndef PUGL_DETAIL_X11_H_INCLUDED -#include "../pugl-upstream/src/x11.h" -#endif - -#include "../pugl-upstream/src/implementation.h" - -#include -#include - -void -puglRaiseWindow(PuglView* view) -{ - XRaiseWindow(view->impl->display, view->impl->win); -} - -void -puglSetWindowSize(PuglView* view, unsigned int width, unsigned int height) -{ - view->frame.width = width; - view->frame.height = height; - - if (view->impl->win) { -#if 0 - if (! fResizable) - { - XSizeHints sizeHints; - memset(&sizeHints, 0, sizeof(sizeHints)); - - sizeHints.flags = PSize|PMinSize|PMaxSize; - sizeHints.width = static_cast(width); - sizeHints.height = static_cast(height); - sizeHints.min_width = static_cast(width); - sizeHints.min_height = static_cast(height); - sizeHints.max_width = static_cast(width); - sizeHints.max_height = static_cast(height); - - XSetWMNormalHints(xDisplay, xWindow, &sizeHints); - } -#endif - - XResizeWindow(view->world->impl->display, view->impl->win, width, height); - } -} - -void -puglUpdateGeometryConstraints(PuglView* view, unsigned int width, unsigned int height, bool aspect) -{ - // NOTE this is a combination of puglSetMinSize and puglSetAspectRatio - Display* display = view->world->impl->display; - - view->minWidth = width; - view->minHeight = height; - - if (aspect) { - view->minAspectX = width; - view->minAspectY = height; - view->maxAspectX = width; - view->maxAspectY = height; - } - -#if 0 - if (view->impl->win) { - XSizeHints sizeHints = getSizeHints(view); - XSetNormalHints(display, view->impl->win, &sizeHints); - // NOTE old code used this instead - // XSetWMNormalHints(display, view->impl->win, &sizeHints); - } -#endif -} - -void -puglExtraSetWindowTypeAndPID(PuglView* view) -{ - PuglInternals* const impl = view->impl; - - const pid_t pid = getpid(); - const Atom _nwp = XInternAtom(impl->display, "_NET_WM_PID", False); - XChangeProperty(impl->display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); - - const Atom _wt = XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE", False); - - // Setting the window to both dialog and normal will produce a decorated floating dialog - // Order is important: DIALOG needs to come before NORMAL - const Atom _wts[2] = { - XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_DIALOG", False), - XInternAtom(impl->display, "_NET_WM_WINDOW_TYPE_NORMAL", False) - }; - - XChangeProperty(impl->display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); -} diff --git a/dpf/dgl/src/pugl.cpp b/dpf/dgl/src/pugl.cpp index 080fef9..4c84b97 100644 --- a/dpf/dgl/src/pugl.cpp +++ b/dpf/dgl/src/pugl.cpp @@ -37,7 +37,14 @@ #include #include -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +# include +# include +# ifdef DGL_OPENGL +# include +# include +# endif +#elif defined(DISTRHO_OS_MAC) # import # include # include @@ -119,7 +126,13 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +# include "pugl-extra/haiku.cpp" +# include "pugl-extra/haiku_stub.cpp" +# ifdef DGL_OPENGL +# include "pugl-extra/haiku_gl.cpp" +# endif +#elif defined(DISTRHO_OS_MAC) # ifndef DISTRHO_MACOS_NAMESPACE_MACRO # define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) @@ -146,10 +159,10 @@ START_NAMESPACE_DGL # endif # pragma clang diagnostic pop #elif defined(DISTRHO_OS_WASM) -# include "pugl-upstream/src/wasm.c" -# include "pugl-upstream/src/wasm_stub.c" +# include "pugl-extra/wasm.c" +# include "pugl-extra/wasm_stub.c" # ifdef DGL_OPENGL -# include "pugl-upstream/src/wasm_gl.c" +# include "pugl-extra/wasm_gl.c" # endif #elif defined(DISTRHO_OS_WINDOWS) # include "pugl-upstream/src/win.c" @@ -237,7 +250,8 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) void puglRaiseWindow(PuglView* const view) { -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (NSWindow* const window = view->impl->window ? view->impl->window : [view->impl->wrapperView window]) [window orderFrontRegardless]; @@ -257,7 +271,10 @@ void puglRaiseWindow(PuglView* const view) double puglGetScaleFactorFromParent(const PuglView* const view) { const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0; -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) + // TODO + return 1.0; +#elif defined(DISTRHO_OS_MAC) // some of these can return 0 as backingScaleFactor, pick the most relevant valid one const NSWindow* possibleWindows[] = { parent != 0 ? [(NSView*)parent window] : nullptr, @@ -296,7 +313,8 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co view->sizeHints[PUGL_FIXED_ASPECT].height = height; } -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (view->impl->window) { PuglStatus status; @@ -328,7 +346,8 @@ void puglSetResizable(PuglView* const view, const bool resizable) { puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) if (PuglWindow* const window = view->impl->window) { const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) @@ -361,7 +380,8 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) view->sizeHints[PUGL_DEFAULT_SIZE].width = view->frame.width = static_cast(width); view->sizeHints[PUGL_DEFAULT_SIZE].height = view->frame.height = static_cast(height); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) +#elif defined(DISTRHO_OS_MAC) // mostly matches upstream pugl, simplified PuglInternals* const impl = view->impl; @@ -456,7 +476,11 @@ void puglFallbackOnResize(PuglView* const view) // -------------------------------------------------------------------------------------------------------------------- -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) + +// -------------------------------------------------------------------------------------------------------------------- + +#elif defined(DISTRHO_OS_MAC) // -------------------------------------------------------------------------------------------------------------------- // macOS specific, add another view's window as child diff --git a/dpf/dgl/src/pugl.hpp b/dpf/dgl/src/pugl.hpp index d5d540b..244adc5 100644 --- a/dpf/dgl/src/pugl.hpp +++ b/dpf/dgl/src/pugl.hpp @@ -73,7 +73,11 @@ void puglOnDisplayPrepare(PuglView* view); // DGL specific, build-specific fallback resize void puglFallbackOnResize(PuglView* view); -#if defined(DISTRHO_OS_MAC) +#if defined(DISTRHO_OS_HAIKU) + +// nothing here yet + +#elif defined(DISTRHO_OS_MAC) // macOS specific, add another view's window as child PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child); diff --git a/dpf/distrho/DistrhoDetails.hpp b/dpf/distrho/DistrhoDetails.hpp new file mode 100644 index 0000000..a0f5ead --- /dev/null +++ b/dpf/distrho/DistrhoDetails.hpp @@ -0,0 +1,1031 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2023 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_DETAILS_HPP_INCLUDED +#define DISTRHO_DETAILS_HPP_INCLUDED + +#include "extra/String.hpp" + +START_NAMESPACE_DISTRHO + +/* -------------------------------------------------------------------------------------------------------------------- + * Audio Port Hints */ + +/** + @defgroup AudioPortHints Audio Port Hints + + Various audio port hints. + @see AudioPort::hints + @{ + */ + +/** + Audio port can be used as control voltage (LV2 and JACK standalone only). + */ +static constexpr const uint32_t kAudioPortIsCV = 0x1; + +/** + Audio port should be used as sidechan (LV2 and VST3 only). + This hint should not be used with CV style ports. + @note non-sidechain audio ports must exist in the plugin if this flag is set. + */ +static constexpr const uint32_t kAudioPortIsSidechain = 0x2; + +/** + CV port has bipolar range (-1 to +1, or -5 to +5 if scaled). + This is merely a hint to tell the host what value range to expect. + */ +static constexpr const uint32_t kCVPortHasBipolarRange = 0x10; + +/** + CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled). + This is merely a hint to tell the host what value range to expect. + */ +static constexpr const uint32_t kCVPortHasNegativeUnipolarRange = 0x20; + +/** + CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled). + This is merely a hint to tell the host what value range to expect. + */ +static constexpr const uint32_t kCVPortHasPositiveUnipolarRange = 0x40; + +/** + CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar). + One other range flag is required if this flag is set. + + When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform. + */ +static constexpr const uint32_t kCVPortHasScaledRange = 0x80; + +/** + CV port is optional, allowing hosts that do no CV ports to load the plugin. + When loaded in hosts that don't support CV, the float* buffer for this port will be null. + */ +static constexpr const uint32_t kCVPortIsOptional = 0x100; + +/** @} */ + +/* -------------------------------------------------------------------------------------------------------------------- + * Parameter Hints */ + +/** + @defgroup ParameterHints Parameter Hints + + Various parameter hints. + @see Parameter::hints + @{ + */ + +/** + Parameter is automatable (real-time safe). + @see Plugin::setParameterValue(uint32_t, float) + */ +static constexpr const uint32_t kParameterIsAutomatable = 0x01; + +/** It was a typo, sorry.. */ +DISTRHO_DEPRECATED_BY("kParameterIsAutomatable") +static constexpr const uint32_t kParameterIsAutomable = kParameterIsAutomatable; + +/** + Parameter value is boolean.@n + It's always at either minimum or maximum value. + */ +static constexpr const uint32_t kParameterIsBoolean = 0x02; + +/** + Parameter value is integer. + */ +static constexpr const uint32_t kParameterIsInteger = 0x04; + +/** + Parameter value is logarithmic. + */ +static constexpr const uint32_t kParameterIsLogarithmic = 0x08; + +/** + Parameter is of output type.@n + When unset, parameter is assumed to be of input type. + + Parameter inputs are changed by the host and typically should not be changed by the plugin.@n + One exception is when changing programs, see Plugin::loadProgram().@n + The other exception is with parameter change requests, see Plugin::requestParameterValueChange().@n + Outputs are changed by the plugin and never modified by the host. + + If you are targetting VST2, make sure to order your parameters so that all inputs are before any outputs. + */ +static constexpr const uint32_t kParameterIsOutput = 0x10; + +/** + Parameter value is a trigger.@n + This means the value resets back to its default after each process/run call.@n + Cannot be used for output parameters. + + @note Only officially supported under LV2. For other formats DPF simulates the behaviour. +*/ +static constexpr const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; + +/** + Parameter should be hidden from the host and user-visible GUIs.@n + It is still saved and handled as any regular parameter, just not visible to the user + (for example in a host generated GUI) + */ +static constexpr const uint32_t kParameterIsHidden = 0x40; + +/** @} */ + +/* -------------------------------------------------------------------------------------------------------------------- + * State Hints */ + +/** + @defgroup StateHints State Hints + + Various state hints. + @see State::hints + @{ + */ + +/** + State is visible and readable by hosts that support string-type plugin parameters. + */ +static constexpr const uint32_t kStateIsHostReadable = 0x01; + +/** + State is writable by the host, allowing users to arbitrarily change the state.@n + For obvious reasons a writable state is also readable by the host. + */ +static constexpr const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable; + +/** + State is a filename path instead of a regular string.@n + The readable and writable hints are required for filenames to work, and thus are automatically set. + */ +static constexpr const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable; + +/** + State is a base64 encoded string. + */ +static constexpr const uint32_t kStateIsBase64Blob = 0x08; + +/** + State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes. + */ +static constexpr const uint32_t kStateIsOnlyForDSP = 0x10; + +/** + State is for UI side only.@n + If the DSP and UI are separate and the UI is not available, this property won't be saved. + */ +static constexpr const uint32_t kStateIsOnlyForUI = 0x20; + +/** @} */ + +/* -------------------------------------------------------------------------------------------------------------------- + * Base Plugin structs */ + +/** + @defgroup BasePluginStructs Base Plugin Structs + @{ + */ + +/** + Parameter designation.@n + Allows a parameter to be specially designated for a task, like bypass. + + Each designation is unique, there must be only one parameter that uses it.@n + The use of designated parameters is completely optional. + + @note Designated parameters have strict ranges. + @see ParameterRanges::adjustForDesignation() + */ +enum ParameterDesignation { + /** + Null or unset designation. + */ + kParameterDesignationNull = 0, + + /** + Bypass designation.@n + When on (> 0.5f), it means the plugin must run in a bypassed state. + */ + kParameterDesignationBypass = 1 +}; + +/** + Predefined Port Groups Ids. + + This enumeration provides a few commonly used groups for convenient use in plugins. + For preventing conflicts with user code, negative values are used here. + When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. + + @see PortGroup + */ +enum PredefinedPortGroupsIds { + /** + Null or unset port group. + */ + kPortGroupNone = (uint32_t)-1, + + /** + A single channel audio group. + */ + kPortGroupMono = (uint32_t)-2, + + /** + A 2-channel discrete stereo audio group, + where the 1st audio port is the left channel and the 2nd port is the right channel. + */ + kPortGroupStereo = (uint32_t)-3 +}; + +/** + Audio Port. + + Can be used as CV port by specifying kAudioPortIsCV in hints,@n + but this is only supported in LV2 and JACK standalone formats. + */ +struct AudioPort { + /** + Hints describing this audio port. + @see AudioPortHints + */ + uint32_t hints; + + /** + The name of this audio port.@n + An audio port name can contain any character, but hosts might have a hard time with non-ascii ones.@n + The name doesn't have to be unique within a plugin instance, but it's recommended. + */ + String name; + + /** + The symbol of this audio port.@n + An audio port symbol is a short restricted name used as a machine and human readable identifier.@n + The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. + @note Audio port and parameter symbols MUST be unique within a plugin instance. + */ + String symbol; + + /** + The group id that this audio/cv port belongs to. + No group is assigned by default. + + You can use a group from PredefinedPortGroups or roll your own.@n + When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. + @see PortGroup, Plugin::initPortGroup + */ + uint32_t groupId; + + /** + Default constructor for a regular audio port. + */ + AudioPort() noexcept + : hints(0x0), + name(), + symbol(), + groupId(kPortGroupNone) {} +}; + +/** + Parameter ranges.@n + This is used to set the default, minimum and maximum values of a parameter. + + By default a parameter has 0.0 as minimum, 1.0 as maximum and 0.0 as default.@n + When changing this struct values you must ensure maximum > minimum and default is within range. + */ +struct ParameterRanges { + /** + Default value. + */ + float def; + + /** + Minimum value. + */ + float min; + + /** + Maximum value. + */ + float max; + + /** + Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum. + */ + constexpr ParameterRanges() noexcept + : def(0.0f), + min(0.0f), + max(1.0f) {} + + /** + Constructor using custom values. + */ + constexpr ParameterRanges(float df, float mn, float mx) noexcept + : def(df), + min(mn), + max(mx) {} + + /** + Fix the default value within range. + */ + void fixDefault() noexcept + { + fixValue(def); + } + + /** + Fix a value within range. + */ + void fixValue(float& value) const noexcept + { + if (value < min) + value = min; + else if (value > max) + value = max; + } + + /** + Get a fixed value within range. + */ + float getFixedValue(const float& value) const noexcept + { + if (value <= min) + return min; + if (value >= max) + return max; + return value; + } + + /** + Get a value normalized to 0.0<->1.0. + */ + float getNormalizedValue(const float& value) const noexcept + { + const float normValue = (value - min) / (max - min); + + if (normValue <= 0.0f) + return 0.0f; + if (normValue >= 1.0f) + return 1.0f; + return normValue; + } + + /** + Get a value normalized to 0.0<->1.0. + Overloaded function using double precision values. + */ + double getNormalizedValue(const double& value) const noexcept + { + const double normValue = (value - min) / (max - min); + + if (normValue <= 0.0) + return 0.0; + if (normValue >= 1.0) + return 1.0; + return normValue; + } + + /** + Get a value normalized to 0.0<->1.0, fixed within range. + */ + float getFixedAndNormalizedValue(const float& value) const noexcept + { + if (value <= min) + return 0.0f; + if (value >= max) + return 1.0f; + + const float normValue = (value - min) / (max - min); + + if (normValue <= 0.0f) + return 0.0f; + if (normValue >= 1.0f) + return 1.0f; + + return normValue; + } + + /** + Get a value normalized to 0.0<->1.0, fixed within range. + Overloaded function using double precision values. + */ + double getFixedAndNormalizedValue(const double& value) const noexcept + { + if (value <= min) + return 0.0; + if (value >= max) + return 1.0; + + const double normValue = (value - min) / (max - min); + + if (normValue <= 0.0) + return 0.0; + if (normValue >= 1.0) + return 1.0; + + return normValue; + } + + /** + Get a proper value previously normalized to 0.0<->1.0. + */ + float getUnnormalizedValue(const float& value) const noexcept + { + if (value <= 0.0f) + return min; + if (value >= 1.0f) + return max; + + return value * (max - min) + min; + } + + /** + Get a proper value previously normalized to 0.0<->1.0. + Overloaded function using double precision values. + */ + double getUnnormalizedValue(const double& value) const noexcept + { + if (value <= 0.0) + return min; + if (value >= 1.0) + return max; + + return value * (max - min) + min; + } +}; + +/** + Parameter enumeration value.@n + A string representation of a plugin parameter value.@n + Used together can be used to give meaning to parameter values, working as an enumeration. + */ +struct ParameterEnumerationValue { + /** + Parameter value. + */ + float value; + + /** + String representation of this value. + */ + String label; + + /** + Default constructor, using 0.0 as value and empty label. + */ + ParameterEnumerationValue() noexcept + : value(0.0f), + label() {} + + /** + Constructor using custom values. + */ + ParameterEnumerationValue(float v, const char* l) noexcept + : value(v), + label(l) {} + +#if __cplusplus >= 201703L + /** + Constructor using custom values, constexpr compatible variant. + */ + constexpr ParameterEnumerationValue(float v, const std::string_view& l) noexcept + : value(v), + label(l) {} +#endif +}; + +/** + Details around parameter enumeration values.@n + Wraps ParameterEnumerationValues and provides a few extra details to the host about these values. + */ +struct ParameterEnumerationValues { + /** + Number of elements allocated in @values. + */ + uint8_t count; + + /** + Whether the host is to be restricted to only use enumeration values. + + @note This mode is only a hint! Not all hosts and plugin formats support this mode. + */ + bool restrictedMode; + + /** + Array of @ParameterEnumerationValue items.@n + When assining this pointer manually, it must be allocated on the heap with `new ParameterEnumerationValue[count]`.@n + The array pointer will be automatically deleted later unless @p deleteLater is set to false. + */ + ParameterEnumerationValue* values; + + /** + Whether to take ownership of the @p values pointer.@n + Defaults to true unless stated otherwise. + */ + bool deleteLater; + + /** + Default constructor, for zero enumeration values. + */ + constexpr ParameterEnumerationValues() noexcept + : count(0), + restrictedMode(false), + values(nullptr), + deleteLater(true) {} + + /** + Constructor using custom values.@n + When using this constructor the pointer to @values MUST have been statically declared.@n + It will not be automatically deleted later. + */ + constexpr ParameterEnumerationValues(uint32_t c, bool r, ParameterEnumerationValue* v) noexcept + : count(c), + restrictedMode(r), + values(v), + deleteLater(false) {} + + // constexpr + ~ParameterEnumerationValues() noexcept + { + if (deleteLater) + delete[] values; + } +}; + +/** + Parameter. + */ +struct Parameter { + /** + Hints describing this parameter. + @see ParameterHints + */ + uint32_t hints; + + /** + The name of this parameter.@n + A parameter name can contain any character, but hosts might have a hard time with non-ascii ones.@n + The name doesn't have to be unique within a plugin instance, but it's recommended. + */ + String name; + + /** + The short name of this parameter.@n + Used when displaying the parameter name in a very limited space. + @note This value is optional, the full name is used when the short one is missing. + */ + String shortName; + + /** + The symbol of this parameter.@n + A parameter symbol is a short restricted name used as a machine and human readable identifier.@n + The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. + @note Parameter symbols MUST be unique within a plugin instance. + */ + String symbol; + + /** + The unit of this parameter.@n + This means something like "dB", "kHz" and "ms".@n + Can be left blank if a unit does not apply to this parameter. + */ + String unit; + + /** + An extensive description/comment about the parameter. + @note This value is optional and only used for LV2. + */ + String description; + + /** + Ranges of this parameter.@n + The ranges describe the default, minimum and maximum values. + */ + ParameterRanges ranges; + + /** + Enumeration details.@n + Can be used to give meaning to parameter values, working as an enumeration. + */ + ParameterEnumerationValues enumValues; + + /** + Designation for this parameter. + */ + ParameterDesignation designation; + + /** + MIDI CC to use by default on this parameter.@n + A value of 0 or 32 (bank change) is considered invalid.@n + Must also be less or equal to 120. + @note This value is only a hint! Hosts might map it automatically or completely ignore it. + */ + uint8_t midiCC; + + /** + The group id that this parameter belongs to. + No group is assigned by default. + + You can use a group from PredefinedPortGroups or roll your own.@n + When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. + @see PortGroup, Plugin::initPortGroup + */ + uint32_t groupId; + + /** + Default constructor for a null parameter. + */ + Parameter() noexcept + : hints(0x0), + name(), + shortName(), + symbol(), + unit(), + ranges(), + enumValues(), + designation(kParameterDesignationNull), + midiCC(0), + groupId(kPortGroupNone) {} + + /** + Constructor using custom values. + */ + Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept + : hints(h), + name(n), + shortName(), + symbol(s), + unit(u), + ranges(def, min, max), + enumValues(), + designation(kParameterDesignationNull), + midiCC(0), + groupId(kPortGroupNone) {} + +#ifdef DISTRHO_PROPER_CPP11_SUPPORT + /** + Constructor using custom values and enumeration. + Assumes enumeration details should have `restrictedMode` on. + */ + Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max, + uint8_t evcount, ParameterEnumerationValue ev[]) noexcept + : hints(h), + name(n), + shortName(), + symbol(s), + unit(u), + ranges(def, min, max), + enumValues(evcount, true, ev), + designation(kParameterDesignationNull), + midiCC(0), + groupId(kPortGroupNone) {} +#endif + +#if __cplusplus >= 201703L + /** + Constructor for constexpr compatible data. + */ + constexpr Parameter(uint32_t h, + const std::string_view& n, + const std::string_view& sn, + const std::string_view& sym, + const std::string_view& u, + const std::string_view& desc) noexcept + : hints(h), + name(n), + shortName(sn), + symbol(sym), + unit(u), + description(desc), + ranges(), + enumValues(), + designation(kParameterDesignationNull), + midiCC(0), + groupId(kPortGroupNone) {} +#endif + + /** + Initialize a parameter for a specific designation. + */ + void initDesignation(ParameterDesignation d) noexcept + { + designation = d; + + switch (d) + { + case kParameterDesignationNull: + break; + case kParameterDesignationBypass: + hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger; + name = "Bypass"; + shortName = "Bypass"; + symbol = "dpf_bypass"; + unit = ""; + midiCC = 0; + groupId = kPortGroupNone; + ranges.def = 0.0f; + ranges.min = 0.0f; + ranges.max = 1.0f; + break; + } + } +}; + +#if __cplusplus >= 202001L /* TODO */ +/** + Bypass parameter definition in constexpr form. + */ +static constexpr const Parameter kParameterBypass = { + kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger, + "Bypass", "Bypass", "dpf_bypass", "", "", {}, {}, 0, kPortGroupNone, +}; +#endif + +/** + Port Group.@n + Allows to group together audio/cv ports or parameters. + + Each unique group MUST have an unique symbol and a name. + A group can be applied to both inputs and outputs (at the same time). + The same group cannot be used in audio ports and parameters. + + When both audio and parameter groups are used, audio groups MUST be defined first. + That is, group indexes start with audio ports, then parameters. + + An audio port group logically combines ports which should be considered part of the same stream.@n + For example, two audio ports in a group may form a stereo stream. + + A parameter group provides meta-data to the host to indicate that some parameters belong together. + + The use of port groups is completely optional. + + @see Plugin::initPortGroup, AudioPort::group, Parameter::group + */ +struct PortGroup { + /** + The name of this port group.@n + A port group name can contain any character, but hosts might have a hard time with non-ascii ones.@n + The name doesn't have to be unique within a plugin instance, but it's recommended. + */ + String name; + + /** + The symbol of this port group.@n + A port group symbol is a short restricted name used as a machine and human readable identifier.@n + The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. + @note Port group symbols MUST be unique within a plugin instance. + */ + String symbol; +}; + +/** + State. + + In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n + By default states are completely internal to the plugin and not visible by the host.@n + Flags can be set to allow hosts to see and/or change them. + + TODO API under construction + */ +struct State { + /** + Hints describing this state. + @note Changing these hints can break compatibility with previously saved data. + @see StateHints + */ + uint32_t hints; + + /** + The key or "symbol" of this state.@n + A state key is a short restricted name used as a machine and human readable identifier. + @note State keys MUST be unique within a plugin instance. + TODO define rules for allowed characters, must be usable as URI non-encoded parameters + */ + String key; + + /** + The default value of this state.@n + Can be left empty if considered a valid initial state. + */ + String defaultValue; + + /** + String representation of this state. + */ + String label; + + /** + An extensive description/comment about this state. + @note This value is optional and only used for LV2. + */ + String description; + + #ifdef __MOD_DEVICES__ + /** + The file types that a filename path state supports, written as a comma-separated string without whitespace. + Currently supported file types are: + - audioloop: Audio Loops, meant to be used for looper-style plugins + - audiorecording: Audio Recordings, triggered by plugins and stored in the unit + - audiosample: One-shot Audio Samples, meant to be used for sampler-style plugins + - audiotrack: Audio Tracks, meant to be used as full-performance/song or backtrack + - cabsim: Speaker Cabinets, meant as small IR audio files + - h2drumkit: Hydrogen Drumkits, must use h2drumkit file extension + - ir: Impulse Responses + - midiclip: MIDI Clips, to be used in sync with host tempo, must have mid or midi file extension + - midisong: MIDI Songs, meant to be used as full-performance/song or backtrack + - sf2: SF2 Instruments, must have sf2 or sf3 file extension + - sfz: SFZ Instruments, must have sfz file extension + + @note This is a custom extension only valid in builds MOD Audio. + */ + String fileTypes; + #endif + + /** + Default constructor for a null state. + */ + State() noexcept + : hints(0x0), + key(), + defaultValue(), + label(), + description() {} +}; + +/** + MIDI event. + */ +struct MidiEvent { + /** + Size of internal data. + */ + static constexpr const uint32_t kDataSize = 4; + + /** + Time offset in frames. + */ + uint32_t frame; + + /** + Number of bytes used. + */ + uint32_t size; + + /** + MIDI data.@n + If size > kDataSize, dataExt is used (otherwise null). + + When dataExt is used, the event holder is responsible for + keeping the pointer valid during the entirety of the run function. + */ + uint8_t data[kDataSize]; + const uint8_t* dataExt; +}; + +/** + Time position.@n + The @a playing and @a frame values are always valid.@n + BBT values are only valid when @a bbt.valid is true. + + This struct is inspired by the [JACK Transport API](https://jackaudio.org/api/structjack__position__t.html). + */ +struct TimePosition { + /** + Wherever the host transport is playing/rolling. + */ + bool playing; + + /** + Current host transport position in frames. + @note This value is not always monotonic, + with some plugin hosts assigning it based on a source that can accumulate rounding errors. + */ + uint64_t frame; + + /** + Bar-Beat-Tick time position. + */ + struct BarBeatTick { + /** + Wherever the host transport is using BBT.@n + If false you must not read from this struct. + */ + bool valid; + + /** + Current bar.@n + Should always be > 0.@n + The first bar is bar '1'. + */ + int32_t bar; + + /** + Current beat within bar.@n + Should always be > 0 and <= @a beatsPerBar.@n + The first beat is beat '1'. + */ + int32_t beat; + + /** + Current tick within beat.@n + Should always be >= 0 and < @a ticksPerBeat.@n + The first tick is tick '0'. + @note Fraction part of tick is only available on some plugin formats. + */ + double tick; + + /** + Number of ticks that have elapsed between frame 0 and the first beat of the current measure. + */ + double barStartTick; + + /** + Time signature "numerator". + */ + float beatsPerBar; + + /** + Time signature "denominator". + */ + float beatType; + + /** + Number of ticks within a beat.@n + Usually a moderately large integer with many denominators, such as 1920.0. + */ + double ticksPerBeat; + + /** + Number of beats per minute. + */ + double beatsPerMinute; + + /** + Default constructor for a null BBT time position. + */ + BarBeatTick() noexcept + : valid(false), + bar(0), + beat(0), + tick(0), + barStartTick(0.0), + beatsPerBar(0.0f), + beatType(0.0f), + ticksPerBeat(0.0), + beatsPerMinute(0.0) {} + + /** + Reinitialize this position using the default null initialization. + */ + void clear() noexcept + { + valid = false; + bar = 0; + beat = 0; + tick = 0; + barStartTick = 0.0; + beatsPerBar = 0.0f; + beatType = 0.0f; + ticksPerBeat = 0.0; + beatsPerMinute = 0.0; + } + } bbt; + + /** + Default constructor for a time position. + */ + TimePosition() noexcept + : playing(false), + frame(0), + bbt() {} + + /** + Reinitialize this position using the default null initialization. + */ + void clear() noexcept + { + playing = false; + frame = 0; + bbt.clear(); + } +}; + +/** @} */ + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_DETAILS_HPP_INCLUDED diff --git a/dpf/distrho/DistrhoPlugin.hpp b/dpf/distrho/DistrhoPlugin.hpp index 79b65a8..546212a 100644 --- a/dpf/distrho/DistrhoPlugin.hpp +++ b/dpf/distrho/DistrhoPlugin.hpp @@ -17,944 +17,12 @@ #ifndef DISTRHO_PLUGIN_HPP_INCLUDED #define DISTRHO_PLUGIN_HPP_INCLUDED -#include "extra/String.hpp" +#include "DistrhoDetails.hpp" #include "extra/LeakDetector.hpp" #include "src/DistrhoPluginChecks.h" START_NAMESPACE_DISTRHO -/* ------------------------------------------------------------------------------------------------------------ - * Audio Port Hints */ - -/** - @defgroup AudioPortHints Audio Port Hints - - Various audio port hints. - @see AudioPort::hints - @{ - */ - -/** - Audio port can be used as control voltage (LV2 and JACK standalone only). - */ -static const uint32_t kAudioPortIsCV = 0x1; - -/** - Audio port should be used as sidechan (LV2 and VST3 only). - This hint should not be used with CV style ports. - @note non-sidechain audio ports must exist in the plugin if this flag is set. - */ -static const uint32_t kAudioPortIsSidechain = 0x2; - -/** - CV port has bipolar range (-1 to +1, or -5 to +5 if scaled). - This is merely a hint to tell the host what value range to expect. - */ -static const uint32_t kCVPortHasBipolarRange = 0x10; - -/** - CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled). - This is merely a hint to tell the host what value range to expect. - */ -static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20; - -/** - CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled). - This is merely a hint to tell the host what value range to expect. - */ -static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40; - -/** - CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar). - One other range flag is required if this flag is set. - - When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform. - */ -static const uint32_t kCVPortHasScaledRange = 0x80; - -/** - CV port is optional, allowing hosts that do no CV ports to load the plugin. - When loaded in hosts that don't support CV, the float* buffer for this port will be null. - */ -static const uint32_t kCVPortIsOptional = 0x100; - -/** @} */ - -/* ------------------------------------------------------------------------------------------------------------ - * Parameter Hints */ - -/** - @defgroup ParameterHints Parameter Hints - - Various parameter hints. - @see Parameter::hints - @{ - */ - -/** - Parameter is automatable (real-time safe). - @see Plugin::setParameterValue(uint32_t, float) - */ -static const uint32_t kParameterIsAutomatable = 0x01; - -/** It was a typo, sorry.. */ -DISTRHO_DEPRECATED_BY("kParameterIsAutomatable") -static const uint32_t kParameterIsAutomable = kParameterIsAutomatable; - -/** - Parameter value is boolean.@n - It's always at either minimum or maximum value. - */ -static const uint32_t kParameterIsBoolean = 0x02; - -/** - Parameter value is integer. - */ -static const uint32_t kParameterIsInteger = 0x04; - -/** - Parameter value is logarithmic. - */ -static const uint32_t kParameterIsLogarithmic = 0x08; - -/** - Parameter is of output type.@n - When unset, parameter is assumed to be of input type. - - Parameter inputs are changed by the host and typically should not be changed by the plugin.@n - One exception is when changing programs, see Plugin::loadProgram().@n - The other exception is with parameter change requests, see Plugin::requestParameterValueChange().@n - Outputs are changed by the plugin and never modified by the host. - - If you are targetting VST2, make sure to order your parameters so that all inputs are before any outputs. - */ -static const uint32_t kParameterIsOutput = 0x10; - -/** - Parameter value is a trigger.@n - This means the value resets back to its default after each process/run call.@n - Cannot be used for output parameters. - - @note Only officially supported under LV2. For other formats DPF simulates the behaviour. -*/ -static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean; - -/** @} */ - -/* ------------------------------------------------------------------------------------------------------------ - * State Hints */ - -/** - @defgroup StateHints State Hints - - Various state hints. - @see State::hints - @{ - */ - -/** - State is visible and readable by hosts that support string-type plugin parameters. - */ -static const uint32_t kStateIsHostReadable = 0x01; - -/** - State is writable by the host, allowing users to arbitrarily change the state.@n - For obvious reasons a writable state is also readable by the host. - */ -static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable; - -/** - State is a filename path instead of a regular string.@n - The readable and writable hints are required for filenames to work, and thus are automatically set. - */ -static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable; - -/** - State is a base64 encoded string. - */ -static const uint32_t kStateIsBase64Blob = 0x08; - -/** - State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes. - */ -static const uint32_t kStateIsOnlyForDSP = 0x10; - -/** - State is for UI side only.@n - If the DSP and UI are separate and the UI is not available, this property won't be saved. - */ -static const uint32_t kStateIsOnlyForUI = 0x20; - -/** @} */ - -/* ------------------------------------------------------------------------------------------------------------ - * Base Plugin structs */ - -/** - @defgroup BasePluginStructs Base Plugin Structs - @{ - */ - -/** - Parameter designation.@n - Allows a parameter to be specially designated for a task, like bypass. - - Each designation is unique, there must be only one parameter that uses it.@n - The use of designated parameters is completely optional. - - @note Designated parameters have strict ranges. - @see ParameterRanges::adjustForDesignation() - */ -enum ParameterDesignation { - /** - Null or unset designation. - */ - kParameterDesignationNull = 0, - - /** - Bypass designation.@n - When on (> 0.5f), it means the plugin must run in a bypassed state. - */ - kParameterDesignationBypass = 1 -}; - -/** - Predefined Port Groups Ids. - - This enumeration provides a few commonly used groups for convenient use in plugins. - For preventing conflicts with user code, negative values are used here. - When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. - - @see PortGroup - */ -enum PredefinedPortGroupsIds { - /** - Null or unset port group. - */ - kPortGroupNone = (uint32_t)-1, - - /** - A single channel audio group. - */ - kPortGroupMono = (uint32_t)-2, - - /** - A 2-channel discrete stereo audio group, - where the 1st audio port is the left channel and the 2nd port is the right channel. - */ - kPortGroupStereo = (uint32_t)-3 -}; - -/** - Audio Port. - - Can be used as CV port by specifying kAudioPortIsCV in hints,@n - but this is only supported in LV2 and JACK standalone formats. - */ -struct AudioPort { - /** - Hints describing this audio port. - @see AudioPortHints - */ - uint32_t hints; - - /** - The name of this audio port.@n - An audio port name can contain any character, but hosts might have a hard time with non-ascii ones.@n - The name doesn't have to be unique within a plugin instance, but it's recommended. - */ - String name; - - /** - The symbol of this audio port.@n - An audio port symbol is a short restricted name used as a machine and human readable identifier.@n - The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. - @note Audio port and parameter symbols MUST be unique within a plugin instance. - */ - String symbol; - - /** - The group id that this audio/cv port belongs to. - No group is assigned by default. - - You can use a group from PredefinedPortGroups or roll your own.@n - When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. - @see PortGroup, Plugin::initPortGroup - */ - uint32_t groupId; - - /** - Default constructor for a regular audio port. - */ - AudioPort() noexcept - : hints(0x0), - name(), - symbol(), - groupId(kPortGroupNone) {} -}; - -/** - Parameter ranges.@n - This is used to set the default, minimum and maximum values of a parameter. - - By default a parameter has 0.0 as minimum, 1.0 as maximum and 0.0 as default.@n - When changing this struct values you must ensure maximum > minimum and default is within range. - */ -struct ParameterRanges { - /** - Default value. - */ - float def; - - /** - Minimum value. - */ - float min; - - /** - Maximum value. - */ - float max; - - /** - Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum. - */ - ParameterRanges() noexcept - : def(0.0f), - min(0.0f), - max(1.0f) {} - - /** - Constructor using custom values. - */ - ParameterRanges(float df, float mn, float mx) noexcept - : def(df), - min(mn), - max(mx) {} - - /** - Fix the default value within range. - */ - void fixDefault() noexcept - { - fixValue(def); - } - - /** - Fix a value within range. - */ - void fixValue(float& value) const noexcept - { - if (value < min) - value = min; - else if (value > max) - value = max; - } - - /** - Get a fixed value within range. - */ - float getFixedValue(const float& value) const noexcept - { - if (value <= min) - return min; - if (value >= max) - return max; - return value; - } - - /** - Get a value normalized to 0.0<->1.0. - */ - float getNormalizedValue(const float& value) const noexcept - { - const float normValue = (value - min) / (max - min); - - if (normValue <= 0.0f) - return 0.0f; - if (normValue >= 1.0f) - return 1.0f; - return normValue; - } - - /** - Get a value normalized to 0.0<->1.0. - Overloaded function using double precision values. - */ - double getNormalizedValue(const double& value) const noexcept - { - const double normValue = (value - min) / (max - min); - - if (normValue <= 0.0) - return 0.0; - if (normValue >= 1.0) - return 1.0; - return normValue; - } - - /** - Get a value normalized to 0.0<->1.0, fixed within range. - */ - float getFixedAndNormalizedValue(const float& value) const noexcept - { - if (value <= min) - return 0.0f; - if (value >= max) - return 1.0f; - - const float normValue = (value - min) / (max - min); - - if (normValue <= 0.0f) - return 0.0f; - if (normValue >= 1.0f) - return 1.0f; - - return normValue; - } - - /** - Get a value normalized to 0.0<->1.0, fixed within range. - Overloaded function using double precision values. - */ - double getFixedAndNormalizedValue(const double& value) const noexcept - { - if (value <= min) - return 0.0; - if (value >= max) - return 1.0; - - const double normValue = (value - min) / (max - min); - - if (normValue <= 0.0) - return 0.0; - if (normValue >= 1.0) - return 1.0; - - return normValue; - } - - /** - Get a proper value previously normalized to 0.0<->1.0. - */ - float getUnnormalizedValue(const float& value) const noexcept - { - if (value <= 0.0f) - return min; - if (value >= 1.0f) - return max; - - return value * (max - min) + min; - } - - /** - Get a proper value previously normalized to 0.0<->1.0. - Overloaded function using double precision values. - */ - double getUnnormalizedValue(const double& value) const noexcept - { - if (value <= 0.0) - return min; - if (value >= 1.0) - return max; - - return value * (max - min) + min; - } -}; - -/** - Parameter enumeration value.@n - A string representation of a plugin parameter value.@n - Used together can be used to give meaning to parameter values, working as an enumeration. - */ -struct ParameterEnumerationValue { - /** - Parameter value. - */ - float value; - - /** - String representation of this value. - */ - String label; - - /** - Default constructor, using 0.0 as value and empty label. - */ - ParameterEnumerationValue() noexcept - : value(0.0f), - label() {} - - /** - Constructor using custom values. - */ - ParameterEnumerationValue(float v, const char* l) noexcept - : value(v), - label(l) {} -}; - -/** - Collection of parameter enumeration values.@n - Handy class to handle the lifetime and count of all enumeration values. - */ -struct ParameterEnumerationValues { - /** - Number of elements allocated in @values. - */ - uint8_t count; - - /** - Wherever the host is to be restricted to only use enumeration values. - - @note This mode is only a hint! Not all hosts and plugin formats support this mode. - */ - bool restrictedMode; - - /** - Array of @ParameterEnumerationValue items.@n - This pointer must be null or have been allocated on the heap with `new ParameterEnumerationValue[count]`. - */ - ParameterEnumerationValue* values; - - /** - Default constructor, for zero enumeration values. - */ - ParameterEnumerationValues() noexcept - : count(0), - restrictedMode(false), - values() {} - - /** - Constructor using custom values.@n - The pointer to @values must have been allocated on the heap with `new`. - */ - ParameterEnumerationValues(uint32_t c, bool r, ParameterEnumerationValue* v) noexcept - : count(c), - restrictedMode(r), - values(v) {} - - ~ParameterEnumerationValues() noexcept - { - count = 0; - restrictedMode = false; - - if (values != nullptr) - { - delete[] values; - values = nullptr; - } - } - - DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues) -}; - -/** - Parameter. - */ -struct Parameter { - /** - Hints describing this parameter. - @see ParameterHints - */ - uint32_t hints; - - /** - The name of this parameter.@n - A parameter name can contain any character, but hosts might have a hard time with non-ascii ones.@n - The name doesn't have to be unique within a plugin instance, but it's recommended. - */ - String name; - - /** - The short name of this parameter.@n - Used when displaying the parameter name in a very limited space. - @note This value is optional, the full name is used when the short one is missing. - */ - String shortName; - - /** - The symbol of this parameter.@n - A parameter symbol is a short restricted name used as a machine and human readable identifier.@n - The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. - @note Parameter symbols MUST be unique within a plugin instance. - */ - String symbol; - - /** - The unit of this parameter.@n - This means something like "dB", "kHz" and "ms".@n - Can be left blank if a unit does not apply to this parameter. - */ - String unit; - - /** - An extensive description/comment about the parameter. - @note This value is optional and only used for LV2. - */ - String description; - - /** - Ranges of this parameter.@n - The ranges describe the default, minimum and maximum values. - */ - ParameterRanges ranges; - - /** - Enumeration values.@n - Can be used to give meaning to parameter values, working as an enumeration. - */ - ParameterEnumerationValues enumValues; - - /** - Designation for this parameter. - */ - ParameterDesignation designation; - - /** - MIDI CC to use by default on this parameter.@n - A value of 0 or 32 (bank change) is considered invalid.@n - Must also be less or equal to 120. - @note This value is only a hint! Hosts might map it automatically or completely ignore it. - */ - uint8_t midiCC; - - /** - The group id that this parameter belongs to. - No group is assigned by default. - - You can use a group from PredefinedPortGroups or roll your own.@n - When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential. - @see PortGroup, Plugin::initPortGroup - */ - uint32_t groupId; - - /** - Default constructor for a null parameter. - */ - Parameter() noexcept - : hints(0x0), - name(), - shortName(), - symbol(), - unit(), - ranges(), - enumValues(), - designation(kParameterDesignationNull), - midiCC(0), - groupId(kPortGroupNone) {} - - /** - Constructor using custom values. - */ - Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept - : hints(h), - name(n), - shortName(), - symbol(s), - unit(u), - ranges(def, min, max), - enumValues(), - designation(kParameterDesignationNull), - midiCC(0), - groupId(kPortGroupNone) {} - - /** - Initialize a parameter for a specific designation. - */ - void initDesignation(ParameterDesignation d) noexcept - { - designation = d; - - switch (d) - { - case kParameterDesignationNull: - break; - case kParameterDesignationBypass: - hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger; - name = "Bypass"; - shortName = "Bypass"; - symbol = "dpf_bypass"; - unit = ""; - midiCC = 0; - groupId = kPortGroupNone; - ranges.def = 0.0f; - ranges.min = 0.0f; - ranges.max = 1.0f; - break; - } - } -}; - -/** - Port Group.@n - Allows to group together audio/cv ports or parameters. - - Each unique group MUST have an unique symbol and a name. - A group can be applied to both inputs and outputs (at the same time). - The same group cannot be used in audio ports and parameters. - - When both audio and parameter groups are used, audio groups MUST be defined first. - That is, group indexes start with audio ports, then parameters. - - An audio port group logically combines ports which should be considered part of the same stream.@n - For example, two audio ports in a group may form a stereo stream. - - A parameter group provides meta-data to the host to indicate that some parameters belong together. - - The use of port groups is completely optional. - - @see Plugin::initPortGroup, AudioPort::group, Parameter::group - */ -struct PortGroup { - /** - The name of this port group.@n - A port group name can contain any character, but hosts might have a hard time with non-ascii ones.@n - The name doesn't have to be unique within a plugin instance, but it's recommended. - */ - String name; - - /** - The symbol of this port group.@n - A port group symbol is a short restricted name used as a machine and human readable identifier.@n - The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. - @note Port group symbols MUST be unique within a plugin instance. - */ - String symbol; -}; - -/** - State. - - In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n - By default states are completely internal to the plugin and not visible by the host.@n - Flags can be set to allow hosts to see and/or change them. - - TODO API under construction - */ -struct State { - /** - Hints describing this state. - @note Changing these hints can break compatibility with previously saved data. - @see StateHints - */ - uint32_t hints; - - /** - The key or "symbol" of this state.@n - A state key is a short restricted name used as a machine and human readable identifier. - @note State keys MUST be unique within a plugin instance. - TODO define rules for allowed characters, must be usable as URI non-encoded parameters - */ - String key; - - /** - The default value of this state.@n - Can be left empty if considered a valid initial state. - */ - String defaultValue; - - /** - String representation of this state. - */ - String label; - - /** - An extensive description/comment about this state. - @note This value is optional and only used for LV2. - */ - String description; - - #ifdef __MOD_DEVICES__ - /** - The file types that a filename path state supports, written as a comma-separated string without whitespace. - Currently supported file types are: - - audioloop: Audio Loops, meant to be used for looper-style plugins - - audiorecording: Audio Recordings, triggered by plugins and stored in the unit - - audiosample: One-shot Audio Samples, meant to be used for sampler-style plugins - - audiotrack: Audio Tracks, meant to be used as full-performance/song or backtrack - - cabsim: Speaker Cabinets, meant as small IR audio files - - h2drumkit: Hydrogen Drumkits, must use h2drumkit file extension - - ir: Impulse Responses - - midiclip: MIDI Clips, to be used in sync with host tempo, must have mid or midi file extension - - midisong: MIDI Songs, meant to be used as full-performance/song or backtrack - - sf2: SF2 Instruments, must have sf2 or sf3 file extension - - sfz: SFZ Instruments, must have sfz file extension - - @note This is a custom extension only valid in builds MOD Audio. - */ - String fileTypes; - #endif - - /** - Default constructor for a null state. - */ - State() noexcept - : hints(0x0), - key(), - defaultValue(), - label(), - description() {} -}; - -/** - MIDI event. - */ -struct MidiEvent { - /** - Size of internal data. - */ - static const uint32_t kDataSize = 4; - - /** - Time offset in frames. - */ - uint32_t frame; - - /** - Number of bytes used. - */ - uint32_t size; - - /** - MIDI data.@n - If size > kDataSize, dataExt is used (otherwise null). - - When dataExt is used, the event holder is responsible for - keeping the pointer valid during the entirety of the run function. - */ - uint8_t data[kDataSize]; - const uint8_t* dataExt; -}; - -/** - Time position.@n - The @a playing and @a frame values are always valid.@n - BBT values are only valid when @a bbt.valid is true. - - This struct is inspired by the [JACK Transport API](https://jackaudio.org/api/structjack__position__t.html). - */ -struct TimePosition { - /** - Wherever the host transport is playing/rolling. - */ - bool playing; - - /** - Current host transport position in frames. - @note This value is not always monotonic, - with some plugin hosts assigning it based on a source that can accumulate rounding errors. - */ - uint64_t frame; - - /** - Bar-Beat-Tick time position. - */ - struct BarBeatTick { - /** - Wherever the host transport is using BBT.@n - If false you must not read from this struct. - */ - bool valid; - - /** - Current bar.@n - Should always be > 0.@n - The first bar is bar '1'. - */ - int32_t bar; - - /** - Current beat within bar.@n - Should always be > 0 and <= @a beatsPerBar.@n - The first beat is beat '1'. - */ - int32_t beat; - - /** - Current tick within beat.@n - Should always be >= 0 and < @a ticksPerBeat.@n - The first tick is tick '0'. - @note Fraction part of tick is only available on some plugin formats. - */ - double tick; - - /** - Number of ticks that have elapsed between frame 0 and the first beat of the current measure. - */ - double barStartTick; - - /** - Time signature "numerator". - */ - float beatsPerBar; - - /** - Time signature "denominator". - */ - float beatType; - - /** - Number of ticks within a beat.@n - Usually a moderately large integer with many denominators, such as 1920.0. - */ - double ticksPerBeat; - - /** - Number of beats per minute. - */ - double beatsPerMinute; - - /** - Default constructor for a null BBT time position. - */ - BarBeatTick() noexcept - : valid(false), - bar(0), - beat(0), - tick(0), - barStartTick(0.0), - beatsPerBar(0.0f), - beatType(0.0f), - ticksPerBeat(0.0), - beatsPerMinute(0.0) {} - - /** - Reinitialize this position using the default null initialization. - */ - void clear() noexcept - { - valid = false; - bar = 0; - beat = 0; - tick = 0; - barStartTick = 0.0; - beatsPerBar = 0.0f; - beatType = 0.0f; - ticksPerBeat = 0.0; - beatsPerMinute = 0.0; - } - } bbt; - - /** - Default constructor for a time position. - */ - TimePosition() noexcept - : playing(false), - frame(0), - bbt() {} - - /** - Reinitialize this position using the default null initialization. - */ - void clear() noexcept - { - playing = false; - frame = 0; - bbt.clear(); - } -}; - -/** @} */ - /* ------------------------------------------------------------------------------------------------------------ * DPF Plugin */ @@ -984,7 +52,7 @@ struct TimePosition { When enabled you need to implement initProgramName() and loadProgram(). DISTRHO_PLUGIN_WANT_STATE activates internal state features.@n - When enabled you need to implement initStateKey() and setState(). + When enabled you need to implement initState() and setState(). The process function run() changes wherever DISTRHO_PLUGIN_WANT_MIDI_INPUT is enabled or not.@n When enabled it provides midi input events. diff --git a/dpf/distrho/DistrhoUI.hpp b/dpf/distrho/DistrhoUI.hpp index 1b25748..14b3925 100644 --- a/dpf/distrho/DistrhoUI.hpp +++ b/dpf/distrho/DistrhoUI.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -17,6 +17,7 @@ #ifndef DISTRHO_UI_HPP_INCLUDED #define DISTRHO_UI_HPP_INCLUDED +#include "DistrhoDetails.hpp" #include "extra/LeakDetector.hpp" #include "src/DistrhoPluginChecks.h" diff --git a/dpf/distrho/DistrhoUIMain.cpp b/dpf/distrho/DistrhoUIMain.cpp index 7970f01..572b62e 100644 --- a/dpf/distrho/DistrhoUIMain.cpp +++ b/dpf/distrho/DistrhoUIMain.cpp @@ -16,6 +16,10 @@ #include "src/DistrhoUI.cpp" +#if ! DISTRHO_PLUGIN_HAS_UI +# error Trying to build UI without DISTRHO_PLUGIN_HAS_UI set to 1 +#endif + #if defined(DISTRHO_PLUGIN_TARGET_CARLA) # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 #elif defined(DISTRHO_PLUGIN_TARGET_CLAP) diff --git a/dpf/distrho/DistrhoUI_macOS.mm b/dpf/distrho/DistrhoUI_macOS.mm index 80b0618..f2b3359 100644 --- a/dpf/distrho/DistrhoUI_macOS.mm +++ b/dpf/distrho/DistrhoUI_macOS.mm @@ -16,6 +16,7 @@ // A few utils declared in DistrhoUI.cpp but defined here because they use Obj-C +#include "DistrhoDetails.hpp" #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" diff --git a/dpf/distrho/DistrhoUtils.hpp b/dpf/distrho/DistrhoUtils.hpp index a769486..768a1f5 100644 --- a/dpf/distrho/DistrhoUtils.hpp +++ b/dpf/distrho/DistrhoUtils.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -58,7 +58,7 @@ inline float round(float __x) #define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO #define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) -/* ------------------------------------------------------------------------------------------------------------ +/* -------------------------------------------------------------------------------------------------------------------- * misc functions */ /** @@ -94,7 +94,7 @@ void d_pass() noexcept {} /** @} */ -/* ------------------------------------------------------------------------------------------------------------ +/* -------------------------------------------------------------------------------------------------------------------- * string print functions */ /** @@ -240,7 +240,7 @@ void d_safe_exception(const char* const exception, const char* const file, const /** @} */ -/* ------------------------------------------------------------------------------------------------------------ +/* -------------------------------------------------------------------------------------------------------------------- * math functions */ /** @@ -254,7 +254,7 @@ void d_safe_exception(const char* const exception, const char* const file, const Returns true if they match. */ template -static inline +static inline constexpr bool d_isEqual(const T& v1, const T& v2) { return std::abs(v1-v2) < std::numeric_limits::epsilon(); @@ -265,7 +265,7 @@ bool d_isEqual(const T& v1, const T& v2) Returns true if they don't match. */ template -static inline +static inline constexpr bool d_isNotEqual(const T& v1, const T& v2) { return std::abs(v1-v2) >= std::numeric_limits::epsilon(); @@ -275,7 +275,7 @@ bool d_isNotEqual(const T& v1, const T& v2) Safely check if a floating point number is zero. */ template -static inline +static inline constexpr bool d_isZero(const T& value) { return std::abs(value) < std::numeric_limits::epsilon(); @@ -285,7 +285,7 @@ bool d_isZero(const T& value) Safely check if a floating point number is not zero. */ template -static inline +static inline constexpr bool d_isNotZero(const T& value) { return std::abs(value) >= std::numeric_limits::epsilon(); @@ -311,7 +311,8 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept /** @} */ -// ----------------------------------------------------------------------- +/* -------------------------------------------------------------------------------------------------------------------- + * math functions */ #ifndef DONT_SET_USING_DISTRHO_NAMESPACE // If your code uses a lot of DISTRHO classes, then this will obviously save you @@ -320,6 +321,4 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept using namespace DISTRHO_NAMESPACE; #endif -// ----------------------------------------------------------------------- - #endif // DISTRHO_UTILS_HPP_INCLUDED diff --git a/dpf/distrho/extra/FileBrowserDialogImpl.hpp b/dpf/distrho/extra/FileBrowserDialogImpl.hpp index 5b47a52..8b3bbd9 100644 --- a/dpf/distrho/extra/FileBrowserDialogImpl.hpp +++ b/dpf/distrho/extra/FileBrowserDialogImpl.hpp @@ -92,7 +92,7 @@ struct FileBrowserOptions { @p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*) @p scaleFactor: Scale factor to use (only used on X11) @p options: Extra options, optional - By default the file browser dialog will be work as "open file" in the current working directory. + By default the file browser dialog will work as "open file" in the current working directory. */ FileBrowserHandle fileBrowserCreate(bool isEmbed, uintptr_t windowId, @@ -102,13 +102,13 @@ FileBrowserHandle fileBrowserCreate(bool isEmbed, /** Idle the file browser dialog handle.@n Returns true if dialog was closed (with or without a file selection), - in which case the handle must not be used afterwards. + in which case this idle function must not be called anymore for this handle. You can then call fileBrowserGetPath to know the selected file (or null if cancelled). */ bool fileBrowserIdle(const FileBrowserHandle handle); /** - Close the file browser dialog, handle must not be used afterwards. + Close and free the file browser dialog, handle must not be used afterwards. */ void fileBrowserClose(const FileBrowserHandle handle); diff --git a/dpf/distrho/extra/ScopedDenormalDisable.hpp b/dpf/distrho/extra/ScopedDenormalDisable.hpp new file mode 100644 index 0000000..b8f48bb --- /dev/null +++ b/dpf/distrho/extra/ScopedDenormalDisable.hpp @@ -0,0 +1,112 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2023 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_SCOPED_DENORMAL_DISABLE_HPP_INCLUDED +#define DISTRHO_SCOPED_DENORMAL_DISABLE_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +#ifdef __SSE2_MATH__ +# include +#endif + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- +// ScopedDenormalDisable class definition + +/** + ScopedDenormalDisable is a handy class for disabling denormal numbers during a function scope. + Denormal numbers can happen in IIR or other types of filters, they are often very slow. + + Use this class with care! Messing up with the global state is bound to make some hosts unhappy. + */ +class ScopedDenormalDisable { +public: + /* + * Constructor. + * Current cpu flags will saved, then denormals-as-zero and flush-to-zero set on top. + */ + inline ScopedDenormalDisable() noexcept; + + /* + * Destructor. + * CPU flags will be restored to the value obtained in the constructor. + */ + inline ~ScopedDenormalDisable() noexcept + { + setFlags(oldflags); + } + +private: + #if defined(__SSE2_MATH__) + typedef uint cpuflags_t; + #elif defined(__aarch64__) + typedef uint64_t cpuflags_t; + #elif defined(__arm__) && !defined(__SOFTFP__) + typedef uint32_t cpuflags_t; + #else + typedef char cpuflags_t; + #endif + + // retrieved on constructor, reset to it on destructor + cpuflags_t oldflags; + + // helper function to set cpu flags + inline void setFlags(cpuflags_t flags) noexcept; + + DISTRHO_DECLARE_NON_COPYABLE(ScopedDenormalDisable) + DISTRHO_PREVENT_HEAP_ALLOCATION +}; + +// -------------------------------------------------------------------------------------------------------------------- +// ScopedDenormalDisable class implementation + +inline ScopedDenormalDisable::ScopedDenormalDisable() noexcept + : oldflags(0) +{ + #if defined(__SSE2_MATH__) + oldflags = _mm_getcsr(); + setFlags(oldflags | 0x8040); + #elif defined(__aarch64__) + __asm__ __volatile__("mrs %0, fpcr" : "=r" (oldflags)); + setFlags(oldflags | 0x1000000); + __asm__ __volatile__("isb"); + #elif defined(__arm__) && !defined(__SOFTFP__) + __asm__ __volatile__("vmrs %0, fpscr" : "=r" (oldflags)); + setFlags(oldflags | 0x1000000); + #endif +} + +inline void ScopedDenormalDisable::setFlags(const cpuflags_t flags) noexcept +{ + #if defined(__SSE2_MATH__) + _mm_setcsr(flags); + #elif defined(__aarch64__) + __asm__ __volatile__("msr fpcr, %0" :: "r" (flags)); + #elif defined(__arm__) && !defined(__SOFTFP__) + __asm__ __volatile__("vmsr fpscr, %0" :: "r" (flags)); + #else + // unused + (void)flags; + #endif +} + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_SCOPED_DENORMAL_DISABLE_HPP_INCLUDED diff --git a/dpf/distrho/extra/String.hpp b/dpf/distrho/extra/String.hpp index 0238395..d61d5c4 100644 --- a/dpf/distrho/extra/String.hpp +++ b/dpf/distrho/extra/String.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -22,6 +22,10 @@ #include +#if __cplusplus >= 201703L +# include +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------- @@ -87,6 +91,16 @@ public: _dup(strBuf); } + #if __cplusplus >= 201703L + /* + * constexpr compatible variant. + */ + explicit constexpr String(const std::string_view& strView) noexcept + : fBuffer(const_cast(strView.data())), + fBufferLen(strView.size()), + fBufferAlloc(false) {} + #endif + /* * Integer. */ diff --git a/dpf/distrho/extra/ValueSmoother.hpp b/dpf/distrho/extra/ValueSmoother.hpp new file mode 100644 index 0000000..6b78a08 --- /dev/null +++ b/dpf/distrho/extra/ValueSmoother.hpp @@ -0,0 +1,204 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2021 Jean Pierre Cimalando + * Copyright (C) 2021-2023 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED +#define DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * @brief An exponential smoother for control values + * + * This continually smooths a value towards a defined target, + * using a low-pass filter of the 1st order, which creates an exponential curve. + * + * The length of the curve is defined by a T60 constant, + * which is the time it takes for a 1-to-0 smoothing to fall to -60dB. + * + * Note that this smoother has asymptotical behavior, + * and it must not be assumed that the final target is ever reached. + */ +class ExponentialValueSmoother { + float coef; + float target; + float mem; + float tau; + float sampleRate; + +public: + ExponentialValueSmoother() + : coef(0.f), + target(0.f), + mem(0.f), + tau(0.f), + sampleRate(0.f) {} + + void setSampleRate(const float newSampleRate) noexcept + { + if (d_isNotEqual(sampleRate, newSampleRate)) + { + sampleRate = newSampleRate; + updateCoef(); + } + } + + void setTimeConstant(const float newT60) noexcept + { + const float newTau = newT60 * (float)(1.0 / 6.91); + + if (d_isNotEqual(tau, newTau)) + { + tau = newTau; + updateCoef(); + } + } + + float getCurrentValue() const noexcept + { + return mem; + } + + float getTargetValue() const noexcept + { + return target; + } + + void setTargetValue(const float newTarget) noexcept + { + target = newTarget; + } + + void clearToTargetValue() noexcept + { + mem = target; + } + + inline float peek() const noexcept + { + return mem * coef + target * (1.f - coef); + } + + inline float next() noexcept + { + return (mem = mem * coef + target * (1.f - coef)); + } + +private: + void updateCoef() noexcept + { + coef = std::exp(-1.f / (tau * sampleRate)); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +/** + * @brief A linear smoother for control values + * + * This continually smooths a value towards a defined target, using linear segments. + * + * The duration of the smoothing segment is defined by the given time constant. + * Every time the target changes, a new segment restarts for the whole duration of the time constant. + * + * Note that this smoother, unlike an exponential smoother, eventually should converge to its target value. + */ +class LinearValueSmoother { + float step; + float target; + float mem; + float tau; + float sampleRate; + +public: + LinearValueSmoother() + : step(0.f), + target(0.f), + mem(0.f), + tau(0.f), + sampleRate(0.f) {} + + void setSampleRate(const float newSampleRate) noexcept + { + if (d_isNotEqual(sampleRate, newSampleRate)) + { + sampleRate = newSampleRate; + updateStep(); + } + } + + void setTimeConstant(const float newTau) noexcept + { + if (d_isNotEqual(tau, newTau)) + { + tau = newTau; + updateStep(); + } + } + + float getCurrentValue() const noexcept + { + return mem; + } + + float getTargetValue() const noexcept + { + return target; + } + + void setTargetValue(const float newTarget) noexcept + { + if (d_isNotEqual(target, newTarget)) + { + target = newTarget; + updateStep(); + } + } + + void clearToTargetValue() noexcept + { + mem = target; + } + + inline float peek() const noexcept + { + const float dy = target - mem; + return mem + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy); + } + + inline float next() noexcept + { + const float y0 = mem; + const float dy = target - y0; + return (mem = y0 + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy)); + } + +private: + void updateStep() noexcept + { + step = (target - mem) / (tau * sampleRate); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED diff --git a/dpf/distrho/src/DistrhoPlugin.cpp b/dpf/distrho/src/DistrhoPlugin.cpp index 0fa4f5b..3436d47 100644 --- a/dpf/distrho/src/DistrhoPlugin.cpp +++ b/dpf/distrho/src/DistrhoPlugin.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/dpf/distrho/src/DistrhoPluginCLAP.cpp b/dpf/distrho/src/DistrhoPluginCLAP.cpp index 1f642a0..29a0316 100644 --- a/dpf/distrho/src/DistrhoPluginCLAP.cpp +++ b/dpf/distrho/src/DistrhoPluginCLAP.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -211,7 +211,7 @@ public: #endif const bool isFloating) : fPlugin(plugin), - fPluinEventQueue(eventQueue), + fPluginEventQueue(eventQueue), fEventQueue(eventQueue->fEventQueue), fCachedParameters(eventQueue->fCachedParameters), #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -382,7 +382,7 @@ public: *width = minimumWidth; if (minimumHeight > *height) *height = minimumHeight; - + return true; } @@ -534,7 +534,7 @@ public: private: // Plugin and UI PluginExporter& fPlugin; - ClapEventQueue* const fPluinEventQueue; + ClapEventQueue* const fPluginEventQueue; ClapEventQueue::Queue& fEventQueue; ClapEventQueue::CachedParameters& fCachedParameters; #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -578,7 +578,7 @@ private: setStateCallback, sendNoteCallback, setSizeCallback, - fileRequestCallback, + nullptr, // TODO fileRequestCallback, d_nextBundlePath, fPlugin.getInstancePointer(), fScaleFactor); @@ -682,7 +682,7 @@ private: #if DISTRHO_PLUGIN_WANT_STATE void setState(const char* const key, const char* const value) { - fPluinEventQueue->setStateFromUI(key, value); + fPluginEventQueue->setStateFromUI(key, value); } static void setStateCallback(void* const ptr, const char* key, const char* value) @@ -708,6 +708,7 @@ private: } #endif + /* TODO bool fileRequest(const char*) { return true; @@ -717,6 +718,7 @@ private: { return static_cast(ptr)->fileRequest(key); } + */ }; // -------------------------------------------------------------------------------------------------------------------- @@ -977,6 +979,7 @@ public: case CLAP_EVENT_PARAM_GESTURE_BEGIN: case CLAP_EVENT_PARAM_GESTURE_END: case CLAP_EVENT_TRANSPORT: + break; case CLAP_EVENT_MIDI: DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t), event->size, sizeof(clap_event_midi_t)); @@ -1232,7 +1235,7 @@ public: DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), event->size, sizeof(clap_event_param_value)); - + setParameterValueFromEvent(static_cast(static_cast(event))); } } @@ -2068,7 +2071,7 @@ static const char* const kSupportedAPIs[] = { }; // TODO DPF external UI -static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool) +static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool) { for (size_t i=0; i(plugin->plugin_data); instance->destroyUI(); } -static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale) +static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2121,7 +2124,7 @@ static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double s #endif } -static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) +static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2129,7 +2132,7 @@ static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const return gui->getSize(width, height); } -static bool clap_gui_can_resize(const clap_plugin_t* const plugin) +static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2137,7 +2140,7 @@ static bool clap_gui_can_resize(const clap_plugin_t* const plugin) return gui->canResize(); } -static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints) +static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2145,7 +2148,7 @@ static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gu return gui->getResizeHints(hints); } -static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) +static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2153,7 +2156,7 @@ static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* co return gui->adjustSize(width, height); } -static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height) +static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2161,7 +2164,7 @@ static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t return gui->setSizeFromHost(width, height); } -static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window) +static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2169,7 +2172,7 @@ static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_wi return gui->setParent(window); } -static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window) +static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2177,7 +2180,7 @@ static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap return gui->setTransient(window); } -static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title) +static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2185,7 +2188,7 @@ static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char return gui->suggestTitle(title); } -static bool clap_gui_show(const clap_plugin_t* const plugin) +static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2193,7 +2196,7 @@ static bool clap_gui_show(const clap_plugin_t* const plugin) return gui->show(); } -static bool clap_gui_hide(const clap_plugin_t* const plugin) +static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2223,7 +2226,7 @@ static const clap_plugin_gui_t clap_plugin_gui = { // plugin timer #if DPF_CLAP_USING_HOST_TIMER -static void clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id) +static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id) { PluginCLAP* const instance = static_cast(plugin->plugin_data); ClapUI* const gui = instance->getUI(); @@ -2242,14 +2245,14 @@ static const clap_plugin_timer_support_t clap_timer = { // plugin audio ports #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 -static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input) +static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input) { PluginCLAP* const instance = static_cast(plugin->plugin_data); return is_input ? instance->getAudioPortCount() : instance->getAudioPortCount(); } -static bool clap_plugin_audio_ports_get(const clap_plugin_t* const plugin, +static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin, const uint32_t index, const bool is_input, clap_audio_port_info_t* const info) @@ -2269,13 +2272,13 @@ static const clap_plugin_audio_ports_t clap_plugin_audio_ports = { // plugin note ports #if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 -static uint32_t clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input) +static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input) { return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0; } -static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, - const bool is_input, clap_note_port_info_t* const info) +static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t, + const bool is_input, clap_note_port_info_t* const info) { if (is_input) { @@ -2370,7 +2373,6 @@ static const clap_plugin_latency_t clap_plugin_latency = { }; #endif -#if DISTRHO_PLUGIN_WANT_STATE // -------------------------------------------------------------------------------------------------------------------- // plugin state @@ -2390,7 +2392,6 @@ static const clap_plugin_state_t clap_plugin_state = { clap_plugin_state_save, clap_plugin_state_load }; -#endif // -------------------------------------------------------------------------------------------------------------------- // plugin @@ -2452,6 +2453,8 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons { if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) return &clap_plugin_params; + if (std::strcmp(id, CLAP_EXT_STATE) == 0) + return &clap_plugin_state; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) return &clap_plugin_audio_ports; @@ -2464,10 +2467,6 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons if (std::strcmp(id, CLAP_EXT_LATENCY) == 0) return &clap_plugin_latency; #endif - #if DISTRHO_PLUGIN_WANT_STATE - if (std::strcmp(id, CLAP_EXT_STATE) == 0) - return &clap_plugin_state; - #endif #if DISTRHO_PLUGIN_HAS_UI if (std::strcmp(id, CLAP_EXT_GUI) == 0) return &clap_plugin_gui; diff --git a/dpf/distrho/src/DistrhoPluginChecks.h b/dpf/distrho/src/DistrhoPluginChecks.h index 21c98ff..9785b18 100644 --- a/dpf/distrho/src/DistrhoPluginChecks.h +++ b/dpf/distrho/src/DistrhoPluginChecks.h @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -17,6 +17,10 @@ #ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED #define DISTRHO_PLUGIN_CHECKS_H_INCLUDED +#ifndef DISTRHO_DETAILS_HPP_INCLUDED +# error wrong include order +#endif + #include "DistrhoPluginInfo.h" // ----------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoPluginInternal.hpp b/dpf/distrho/src/DistrhoPluginInternal.hpp index 939be37..fdc6f7b 100644 --- a/dpf/distrho/src/DistrhoPluginInternal.hpp +++ b/dpf/distrho/src/DistrhoPluginInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/dpf/distrho/src/DistrhoPluginJACK.cpp b/dpf/distrho/src/DistrhoPluginJACK.cpp index f932f10..5a76f4c 100644 --- a/dpf/distrho/src/DistrhoPluginJACK.cpp +++ b/dpf/distrho/src/DistrhoPluginJACK.cpp @@ -152,23 +152,25 @@ public: fClient(client) { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - char strBuf[0xff+1]; - strBuf[0xff] = '\0'; - # if DISTRHO_PLUGIN_NUM_INPUTS > 0 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) { const AudioPort& port(fPlugin.getAudioPort(true, i)); - fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + ulong hints = JackPortIsInput; + if (port.hints & kAudioPortIsCV) + hints |= JackPortIsControlVoltage; + fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0); setAudioPortMetadata(port, fPortAudioIns[i], i); } # endif # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) { - std::snprintf(strBuf, 0xff, "out%i", i+1); const AudioPort& port(fPlugin.getAudioPort(false, i)); - fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + ulong hints = JackPortIsOutput; + if (port.hints & kAudioPortIsCV) + hints |= JackPortIsControlVoltage; + fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0); setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i); } # endif @@ -623,7 +625,8 @@ private: { char strBuf[0xff]; - snprintf(strBuf, sizeof(0xff)-1, "%u", index); + snprintf(strBuf, 0xff - 2, "%u", index); + strBuf[0xff - 1] = '\0'; jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer"); } @@ -807,7 +810,7 @@ public: protected: void run() override { - plugin.setBufferSize(256); + plugin.setBufferSize(256, true); plugin.activate(); float buffer[256]; @@ -862,8 +865,8 @@ bool runSelfTests() plugin.activate(); plugin.deactivate(); - plugin.setBufferSize(128); - plugin.setSampleRate(48000); + plugin.setBufferSize(128, true); + plugin.setSampleRate(48000, true); plugin.activate(); float buffer[128] = {}; @@ -1030,6 +1033,12 @@ int main(int argc, char* argv[]) jack_status_t status = jack_status_t(0x0); jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); + #ifdef HAVE_JACK + #define STANDALONE_NAME "JACK client" + #else + #define STANDALONE_NAME "Native audio driver" + #endif + if (client == nullptr) { String errorString; @@ -1060,20 +1069,22 @@ int main(int argc, char* argv[]) errorString += "Backend Error;\n"; if (status & JackClientZombie) errorString += "Client is being shutdown against its will;\n"; + if (status & JackBridgeNativeFailed) + errorString += "Native audio driver was unable to start;\n"; if (errorString.isNotEmpty()) { errorString[errorString.length()-2] = '.'; - d_stderr("Failed to create the JACK client, reason was:\n%s", errorString.buffer()); + d_stderr("Failed to create the " STANDALONE_NAME ", reason was:\n%s", errorString.buffer()); } else - d_stderr("Failed to create the JACK client, cannot continue!"); + d_stderr("Failed to create the " STANDALONE_NAME ", cannot continue!"); #if defined(DISTRHO_OS_MAC) CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr, DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8); CFStringRef errorStringRef = CFStringCreateWithCString(nullptr, - String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8); + String("Failed to create " STANDALONE_NAME ", reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8); CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel, nullptr, nullptr, nullptr, @@ -1097,7 +1108,7 @@ int main(int argc, char* argv[]) FreeLibrary(user32); } - const String win32error = "Failed to create JACK client, reason was:\n" + errorString; + const String win32error = "Failed to create " STANDALONE_NAME ", reason was:\n" + errorString; MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR); #endif diff --git a/dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp b/dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp index 07ce113..ac1b599 100644 --- a/dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp +++ b/dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -22,7 +22,7 @@ #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT # error Cannot use MIDI Output with LADSPA or DSSI #endif -#if DISTRHO_PLUGIN_WANT_FULL_STATE +#if DISTRHO_PLUGIN_WANT_FULL_STATE && !defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WITH_LADSPA) # error Cannot use full state with LADSPA or DSSI #endif @@ -618,13 +618,19 @@ static const struct DescriptorInitializer else portDescriptors[port] |= LADSPA_PORT_INPUT; + const uint32_t hints = plugin.getParameterHints(i); + { const ParameterRanges& ranges(plugin.getParameterRanges(i)); const float defValue = ranges.def; - portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; - portRangeHints[port].LowerBound = ranges.min; - portRangeHints[port].UpperBound = ranges.max; + // LADSPA doesn't allow bounded hints on toggles + portRangeHints[port].HintDescriptor = hints & kParameterIsBoolean + ? 0 + : LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE; + + portRangeHints[port].LowerBound = ranges.min; + portRangeHints[port].UpperBound = ranges.max; /**/ if (d_isZero(defValue)) portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; @@ -654,8 +660,6 @@ static const struct DescriptorInitializer } { - const uint32_t hints = plugin.getParameterHints(i); - if (hints & kParameterIsBoolean) { portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; diff --git a/dpf/distrho/src/DistrhoPluginLV2.cpp b/dpf/distrho/src/DistrhoPluginLV2.cpp index 0e730e3..95bf18e 100644 --- a/dpf/distrho/src/DistrhoPluginLV2.cpp +++ b/dpf/distrho/src/DistrhoPluginLV2.cpp @@ -821,7 +821,7 @@ public: if (options[i].type == fURIDs.atomInt) { const int32_t bufferSize(*(const int32_t*)options[i].value); - fPlugin.setBufferSize(bufferSize); + fPlugin.setBufferSize(bufferSize, true); } else { @@ -833,7 +833,7 @@ public: if (options[i].type == fURIDs.atomInt) { const int32_t bufferSize(*(const int32_t*)options[i].value); - fPlugin.setBufferSize(bufferSize); + fPlugin.setBufferSize(bufferSize, true); } else { @@ -846,7 +846,7 @@ public: { const float sampleRate(*(const float*)options[i].value); fSampleRate = sampleRate; - fPlugin.setSampleRate(sampleRate); + fPlugin.setSampleRate(sampleRate, true); } else { @@ -1070,6 +1070,7 @@ public: setState(key, filename); + /* FIXME host should be responsible for updating UI side, not us for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) { if (fPlugin.getStateKey(i) == key) @@ -1079,6 +1080,7 @@ public: break; } } + */ return LV2_WORKER_SUCCESS; } diff --git a/dpf/distrho/src/DistrhoPluginLV2export.cpp b/dpf/distrho/src/DistrhoPluginLV2export.cpp index 8b93e7c..5674e36 100644 --- a/dpf/distrho/src/DistrhoPluginLV2export.cpp +++ b/dpf/distrho/src/DistrhoPluginLV2export.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -42,7 +42,11 @@ # include "mod-license.h" #endif -#ifndef DISTRHO_OS_WINDOWS +#ifdef DISTRHO_OS_WINDOWS +# include +#else +# include +# include # include #endif @@ -65,6 +69,10 @@ # define DISTRHO_PLUGIN_USES_MODGUI 0 #endif +#ifndef DISTRHO_PLUGIN_USES_CUSTOM_MODGUI +# define DISTRHO_PLUGIN_USES_CUSTOM_MODGUI 0 +#endif + #if DISTRHO_PLUGIN_HAS_EMBED_UI # if DISTRHO_OS_HAIKU # define DISTRHO_LV2_UI_TYPE "BeUI" @@ -331,7 +339,7 @@ void lv2_generate_ttl(const char* const basename) } #endif - manifestFile << manifestString << std::endl; + manifestFile << manifestString; manifestFile.close(); std::cout << " done!" << std::endl; } @@ -748,6 +756,8 @@ void lv2_generate_ttl(const char* const basename) if (! designated) { + const uint32_t hints = plugin.getParameterHints(i); + // name and symbol const String& paramName(plugin.getParameterName(i)); @@ -772,13 +782,35 @@ void lv2_generate_ttl(const char* const basename) // ranges const ParameterRanges& ranges(plugin.getParameterRanges(i)); - if (plugin.getParameterHints(i) & kParameterIsInteger) + if (hints & kParameterIsInteger) { if (plugin.isParameterInput(i)) pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n"; pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n"; } + else if (hints & kParameterIsLogarithmic) + { + if (plugin.isParameterInput(i)) + { + if (d_isNotZero(ranges.def)) + pluginString += " lv2:default " + String(ranges.def) + " ;\n"; + else if (d_isEqual(ranges.def, ranges.max)) + pluginString += " lv2:default -0.0001 ;\n"; + else + pluginString += " lv2:default 0.0001 ;\n"; + } + + if (d_isNotZero(ranges.min)) + pluginString += " lv2:minimum " + String(ranges.min) + " ;\n"; + else + pluginString += " lv2:minimum 0.0001 ;\n"; + + if (d_isNotZero(ranges.max)) + pluginString += " lv2:maximum " + String(ranges.max) + " ;\n"; + else + pluginString += " lv2:maximum -0.0001 ;\n"; + } else { if (plugin.isParameterInput(i)) @@ -809,7 +841,7 @@ void lv2_generate_ttl(const char* const basename) else pluginString += " rdfs:label \"" + enumValue.label + "\" ;\n"; - if (plugin.getParameterHints(i) & kParameterIsInteger) + if (hints & kParameterIsInteger) { const int rounded = (int)(enumValue.value + (enumValue.value < 0.0f ? -0.5f : 0.5f)); pluginString += " rdf:value " + String(rounded) + " ;\n"; @@ -877,7 +909,7 @@ void lv2_generate_ttl(const char* const basename) pluginString += " a unit:Unit ;\n"; pluginString += " rdfs:label \"" + unit + "\" ;\n"; pluginString += " unit:symbol \"" + unit + "\" ;\n"; - if (plugin.getParameterHints(i) & kParameterIsInteger) + if (hints & kParameterIsInteger) pluginString += " unit:render \"%d " + unit + "\" ;\n"; else pluginString += " unit:render \"%f " + unit + "\" ;\n"; @@ -897,8 +929,6 @@ void lv2_generate_ttl(const char* const basename) } // hints - const uint32_t hints = plugin.getParameterHints(i); - if (hints & kParameterIsBoolean) { if ((hints & kParameterIsTrigger) == kParameterIsTrigger) @@ -909,6 +939,8 @@ void lv2_generate_ttl(const char* const basename) pluginString += " lv2:portProperty lv2:integer ;\n"; if (hints & kParameterIsLogarithmic) pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; + if (hints & kParameterIsHidden) + pluginString += " lv2:portProperty <" LV2_PORT_PROPS__notOnGUI "> ;\n"; if ((hints & kParameterIsAutomatable) == 0 && plugin.isParameterInput(i)) { pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; @@ -944,11 +976,11 @@ void lv2_generate_ttl(const char* const basename) } } -#ifdef DISTRHO_PLUGIN_BRAND + #ifdef DISTRHO_PLUGIN_BRAND // MOD pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n"; -#endif + #endif // name { @@ -1209,11 +1241,308 @@ void lv2_generate_ttl(const char* const basename) } } - pluginFile << pluginString << std::endl; + pluginFile << pluginString; pluginFile.close(); std::cout << " done!" << std::endl; } + #if DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI + { + std::cout << "Writing modgui.ttl..."; std::cout.flush(); + std::fstream modguiFile("modgui.ttl", std::ios::out); + + String modguiString; + modguiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + modguiString += "@prefix modgui: .\n"; + modguiString += "\n"; + + modguiString += "<" DISTRHO_PLUGIN_URI ">\n"; + modguiString += " modgui:gui [\n"; + #ifdef DISTRHO_PLUGIN_BRAND + modguiString += " modgui:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; + #endif + modguiString += " modgui:label \"" DISTRHO_PLUGIN_NAME "\" ;\n"; + modguiString += " modgui:resourcesDirectory ;\n"; + modguiString += " modgui:iconTemplate ;\n"; + modguiString += " modgui:javascript ;\n"; + modguiString += " modgui:stylesheet ;\n"; + modguiString += " modgui:screenshot ;\n"; + modguiString += " modgui:thumbnail ;\n"; + + uint32_t numParametersOutputs = 0; + for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) + { + if (plugin.isParameterOutput(i)) + ++numParametersOutputs; + } + if (numParametersOutputs != 0) + { + modguiString += " modgui:monitoredOutputs [\n"; + for (uint32_t i=0, j=0, count=plugin.getParameterCount(); i < count; ++i) + { + if (!plugin.isParameterOutput(i)) + continue; + modguiString += " lv2:symbol \"" + plugin.getParameterSymbol(i) + "\" ;\n"; + if (++j != numParametersOutputs) + modguiString += " ] , [\n"; + } + modguiString += " ] ;\n"; + } + + modguiString += " ] .\n"; + + modguiFile << modguiString; + modguiFile.close(); + std::cout << " done!" << std::endl; + } + + #ifdef DISTRHO_OS_WINDOWS + ::_mkdir("modgui"); + #else + ::mkdir("modgui", 0755); + #endif + + { + std::cout << "Writing modgui/javascript.js..."; std::cout.flush(); + std::fstream jsFile("modgui/javascript.js", std::ios::out); + + String jsString; + jsString += "function(e,f){\n"; + jsString += "'use strict';\nvar ps=["; + + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) + jsString += "'lv2_" + plugin.getAudioPort(false, i).symbol + "',"; + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + jsString += "'lv2_" + plugin.getAudioPort(true, i).symbol + "',"; + #if DISTRHO_LV2_USE_EVENTS_IN + jsString += "'lv2_events_in',"; + #endif + #if DISTRHO_LV2_USE_EVENTS_OUT + jsString += "'lv2_events_out',"; + #endif + #if DISTRHO_PLUGIN_WANT_LATENCY + jsString += "'lv2_latency',"; + #endif + + int32_t enabledIndex = INT32_MAX; + for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i) + { + jsString += "'" + plugin.getParameterSymbol(i) + "',"; + if (plugin.getParameterDesignation(i) == kParameterDesignationBypass) + enabledIndex = i; + } + jsString += "];\n"; + jsString += "var ei=" + String(enabledIndex != INT32_MAX ? enabledIndex : -1) + ";\n\n"; + jsString += "if(e.type==='start'){\n"; + jsString += "e.data.p={p:{},c:{},};\n\n"; + jsString += "var err=[];\n"; + jsString += "if(typeof(WebAssembly)==='undefined'){err.push('WebAssembly unsupported');}\n"; + jsString += "else{\n"; + jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,3,1,0,1,10,14,1,12,0,65,0,65,0,65,0,252,10,0,0,11])))"; + jsString += "err.push('Bulk Memory Operations unsupported');\n"; + jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1])))"; + jsString += "err.push('Importable/Exportable mutable globals unsupported');\n"; + jsString += "}\n"; + jsString += "if(err.length!==0){e.icon.find('.canvas_wrapper').html('

'+err.join('
')+'

');return;}\n\n"; + jsString += "var s=document.createElement('script');\n"; + jsString += "s.setAttribute('async',true);\n"; + jsString += "s.setAttribute('src',e.api_version>=3?f.get_custom_resource_filename('module.js'):('/resources/module.js?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION));\n"; + jsString += "s.setAttribute('type','text/javascript');\n"; + jsString += "s.onload=function(){\n"; + jsString += " Module_" DISTRHO_PLUGIN_MODGUI_CLASS_NAME "({\n"; + jsString += " locateFile: function(p,_){return e.api_version>=3?f.get_custom_resource_filename(p):('/resources/'+p+'?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION)},\n"; + jsString += " postRun:function(m){\n"; + jsString += " var cn=e.icon.attr('mod-instance').replaceAll('/','_');\n"; + jsString += " var cnl=m.lengthBytesUTF8(cn) + 1;\n"; + jsString += " var cna=m._malloc(cnl);\n"; + jsString += " m.stringToUTF8(cn, cna, cnl);\n"; + jsString += " e.icon.find('canvas')[0].id=cn;\n"; + jsString += " var a=m.addFunction(function(i,v){f.set_port_value(ps[i],v);},'vif');\n"; + jsString += " var b=m.addFunction(function(u,v){f.patch_set(m.UTF8ToString(u),'s',m.UTF8ToString(v));},'vpp');\n"; + jsString += " var h=m._modgui_init(cna,a,b);\n"; + jsString += " m._free(cna);\n"; + jsString += " e.data.h=h;\n"; + jsString += " e.data.m=m;\n"; + jsString += " for(var u in e.data.p.p){\n"; + jsString += " var ul=m.lengthBytesUTF8(u)+1,ua=m._malloc(ul),v=e.data.p.p[u],vl=m.lengthBytesUTF8(v)+1,va=m._malloc(vl);\n"; + jsString += " m.stringToUTF8(u,ua,ul);\n"; + jsString += " m.stringToUTF8(v,va,vl);\n"; + jsString += " m._modgui_patch_set(h, ua, va);\n"; + jsString += " m._free(ua);\n"; + jsString += " m._free(va);\n"; + jsString += " }\n"; + jsString += " for(var symbol in e.data.p.c){m._modgui_param_set(h,ps.indexOf(symbol),e.data.p.c[symbol]);}\n"; + jsString += " delete e.data.p;\n"; + jsString += " window.dispatchEvent(new Event('resize'));\n"; + jsString += " },\n"; + jsString += " canvas:(function(){var c=e.icon.find('canvas')[0];c.addEventListener('webglcontextlost',function(e2){alert('WebGL context lost. You will need to reload the page.');e2.preventDefault();},false);return c;})(),\n"; + jsString += " });\n"; + jsString += "};\n"; + jsString += "document.head.appendChild(s);\n\n"; + jsString += "}else if(e.type==='change'){\n\n"; + jsString += "if(e.data.h && e.data.m){\n"; + jsString += " var m=e.data.m;\n"; + jsString += " if(e.uri){\n"; + jsString += " var ul=m.lengthBytesUTF8(e.uri)+1,ua=m._malloc(ul),vl=m.lengthBytesUTF8(e.value)+1,va=m._malloc(vl);\n"; + jsString += " m.stringToUTF8(e.uri,ua,ul);\n"; + jsString += " m.stringToUTF8(e.value,va,vl);\n"; + jsString += " m._modgui_patch_set(e.data.h,ua,va);\n"; + jsString += " m._free(ua);\n"; + jsString += " m._free(va);\n"; + jsString += " }else if(e.symbol===':bypass'){return;\n"; + jsString += " }else{m._modgui_param_set(e.data.h,ps.indexOf(e.symbol),e.value);}\n"; + jsString += "}else{\n"; + jsString += " if(e.symbol===':bypass')return;\n"; + jsString += " if(e.uri){e.data.p.p[e.uri]=e.value;}else{e.data.p.c[e.symbol]=e.value;}\n"; + jsString += "}\n\n"; + jsString += "}else if(e.type==='end'){\n"; + jsString += " if(e.data.h && e.data.m){\n"; + jsString += " var h = e.data.h;\n"; + jsString += " var m = e.data.m;\n"; + jsString += " e.data.h = e.data.m = null;\n"; + jsString += " m._modgui_cleanup(h);\n"; + jsString += "}\n\n"; + jsString += "}\n}\n"; + jsFile << jsString; + jsFile.close(); + std::cout << " done!" << std::endl; + } + + { + std::cout << "Writing modgui/icon.html..."; std::cout.flush(); + std::fstream iconFile("modgui/icon.html", std::ios::out); + + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "

{{#brand}}{{brand}} | {{/brand}}{{label}}

" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " " << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{#effect.ports.audio.input}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.audio.input}}" << std::endl; + iconFile << " {{#effect.ports.midi.input}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.midi.input}}" << std::endl; + iconFile << " {{#effect.ports.cv.input}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.cv.input}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{#effect.ports.audio.output}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.audio.output}}" << std::endl; + iconFile << " {{#effect.ports.midi.output}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.midi.output}}" << std::endl; + iconFile << " {{#effect.ports.cv.output}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + iconFile << " {{/effect.ports.cv.output}}" << std::endl; + iconFile << "
" << std::endl; + iconFile << "
" << std::endl; + + iconFile.close(); + std::cout << " done!" << std::endl; + } + + { + std::cout << "Writing modgui/stylesheet.css..."; std::cout.flush(); + std::fstream stylesheetFile("modgui/stylesheet.css", std::ios::out); + + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal{" << std::endl; + stylesheetFile << " padding:0;" << std::endl; + stylesheetFile << " margin:0;" << std::endl; + stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl; + stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT + 50) + "px;" << std::endl; + stylesheetFile << " background:#2a2e32;" << std::endl; + stylesheetFile << " border-radius:20px 20px 0 0;" << std::endl; + stylesheetFile << " color:#fff;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper{" << std::endl; + stylesheetFile << " --device-pixel-ratio:1;" << std::endl; + stylesheetFile << " /*image-rendering:pixelated;*/" << std::endl; + stylesheetFile << " /*image-rendering:crisp-edges;*/" << std::endl; + stylesheetFile << " background:#000;" << std::endl; + stylesheetFile << " position:absolute;" << std::endl; + stylesheetFile << " top:50px;" << std::endl; + stylesheetFile << " transform-origin:0 0 0;" << std::endl; + stylesheetFile << " transform:scale(calc(1/var(--device-pixel-ratio)));" << std::endl; + stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl; + stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT) + "px;" << std::endl; + stylesheetFile << " text-align:center;" << std::endl; + stylesheetFile << " z-index:21;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "/*" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper:focus-within{" << std::endl; + stylesheetFile << " z-index:21;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "*/" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-plugin-title{" << std::endl; + stylesheetFile << " position:absolute;" << std::endl; + stylesheetFile << " text-align:center;" << std::endl; + stylesheetFile << " width:100%;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal h1{" << std::endl; + stylesheetFile << " font-size:20px;" << std::endl; + stylesheetFile << " font-weight:bold;" << std::endl; + stylesheetFile << " line-height:50px;" << std::endl; + stylesheetFile << " margin:0;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-control-group{" << std::endl; + stylesheetFile << " position:absolute;" << std::endl; + stylesheetFile << " left:5px;" << std::endl; + stylesheetFile << " z-index:35;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-input," << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-output{" << std::endl; + stylesheetFile << " top:75px;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-input," << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-output{" << std::endl; + stylesheetFile << " margin-bottom:25px;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .jack-disconnected{" << std::endl; + stylesheetFile << " top:0px!important;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image{" << std::endl; + stylesheetFile << " background-image: url(/img/switch.png);" << std::endl; + stylesheetFile << " background-position: left center;" << std::endl; + stylesheetFile << " background-repeat: no-repeat;" << std::endl; + stylesheetFile << " background-size: auto 50px;" << std::endl; + stylesheetFile << " font-weight: bold;" << std::endl; + stylesheetFile << " width: 100px;" << std::endl; + stylesheetFile << " height: 50px;" << std::endl; + stylesheetFile << " cursor: pointer;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.off{" << std::endl; + stylesheetFile << " background-position: right center !important;" << std::endl; + stylesheetFile << "}" << std::endl; + stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.on{" << std::endl; + stylesheetFile << " background-position: left center !important;" << std::endl; + stylesheetFile << "}" << std::endl; + + stylesheetFile.close(); + std::cout << " done!" << std::endl; + } + #endif // DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI + // --------------------------------------------- #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS @@ -1234,7 +1563,7 @@ void lv2_generate_ttl(const char* const basename) addAttribute(uiString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); - uiFile << uiString << std::endl; + uiFile << uiString; uiFile.close(); std::cout << " done!" << std::endl; } @@ -1310,7 +1639,7 @@ void lv2_generate_ttl(const char* const basename) presetString += " <"; - if (plugin.getStateHints(i) & kStateIsHostReadable) + if (plugin.getStateHints(j) & kStateIsHostReadable) presetString += DISTRHO_PLUGIN_URI "#"; else presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX; @@ -1371,7 +1700,7 @@ void lv2_generate_ttl(const char* const basename) presetsString += presetString; } - presetsFile << presetsString << std::endl; + presetsFile << presetsString; presetsFile.close(); std::cout << " done!" << std::endl; } diff --git a/dpf/distrho/src/DistrhoPluginVST2.cpp b/dpf/distrho/src/DistrhoPluginVST2.cpp index b57366b..5272ece 100644 --- a/dpf/distrho/src/DistrhoPluginVST2.cpp +++ b/dpf/distrho/src/DistrhoPluginVST2.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this diff --git a/dpf/distrho/src/DistrhoPluginVST3.cpp b/dpf/distrho/src/DistrhoPluginVST3.cpp index ed12ebc..756b58c 100644 --- a/dpf/distrho/src/DistrhoPluginVST3.cpp +++ b/dpf/distrho/src/DistrhoPluginVST3.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -4810,7 +4810,7 @@ struct dpf_factory : v3_plugin_factory_cpp { d_strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name)); d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); d_strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); - d_strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version)); + d_strncpy(info->sdk_version, "VST 3.7.4", ARRAY_SIZE(info->sdk_version)); if (idx == 0) { diff --git a/dpf/distrho/src/DistrhoUI.cpp b/dpf/distrho/src/DistrhoUI.cpp index 5ead4e8..6b436e6 100644 --- a/dpf/distrho/src/DistrhoUI.cpp +++ b/dpf/distrho/src/DistrhoUI.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2022 Filipe Coelho + * Copyright (C) 2012-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -14,6 +14,7 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "DistrhoDetails.hpp" #include "src/DistrhoPluginChecks.h" #include "src/DistrhoDefines.h" diff --git a/dpf/distrho/src/DistrhoUIInternal.hpp b/dpf/distrho/src/DistrhoUIInternal.hpp index e7ecebe..65c0b82 100644 --- a/dpf/distrho/src/DistrhoUIInternal.hpp +++ b/dpf/distrho/src/DistrhoUIInternal.hpp @@ -57,9 +57,10 @@ public: void* const dspPtr = nullptr, const double scaleFactor = 0.0, const uint32_t bgColor = 0, - const uint32_t fgColor = 0xffffffff) + const uint32_t fgColor = 0xffffffff, + const char* const appClassName = nullptr) : ui(nullptr), - uiData(new UI::PrivateData()) + uiData(new UI::PrivateData(appClassName)) { uiData->sampleRate = sampleRate; uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; diff --git a/dpf/distrho/src/DistrhoUILV2.cpp b/dpf/distrho/src/DistrhoUILV2.cpp index 617a436..990db39 100644 --- a/dpf/distrho/src/DistrhoUILV2.cpp +++ b/dpf/distrho/src/DistrhoUILV2.cpp @@ -78,7 +78,8 @@ public: const float sampleRate, const float scaleFactor, const uint32_t bgColor, - const uint32_t fgColor) + const uint32_t fgColor, + const char* const appClassName) : fUridMap(uridMap), fUridUnmap(getLv2Feature(features, LV2_URID__unmap)), fUiPortMap(getLv2Feature(features, LV2_UI__portMap)), @@ -97,7 +98,7 @@ public: sendNoteCallback, nullptr, // resize is very messy, hosts can do it without extensions fileRequestCallback, - bundlePath, dspPtr, scaleFactor, bgColor, fgColor) + bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName) { if (widget != nullptr) *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); @@ -113,10 +114,11 @@ public: // if winId == 0 then options must not be null DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); + #ifndef __EMSCRIPTEN__ const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle); const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId); - bool hasTitle = false; + const char* windowTitle = nullptr; for (int i=0; options[i].key != 0; ++i) { @@ -134,19 +136,18 @@ public: { if (options[i].type == fURIDs.atomString) { - if (const char* const windowTitle = (const char*)options[i].value) - { - hasTitle = true; - fUI.setWindowTitle(windowTitle); - } + windowTitle = (const char*)options[i].value; } else d_stderr("Host provides windowTitle but has wrong value type"); } } - if (! hasTitle) - fUI.setWindowTitle(DISTRHO_PLUGIN_NAME); + if (windowTitle == nullptr) + windowTitle = DISTRHO_PLUGIN_NAME; + + fUI.setWindowTitle(windowTitle); + #endif } // ------------------------------------------------------------------- @@ -213,9 +214,14 @@ public: fUI.stateChanged(key, value); } } + else if (atom->type == fURIDs.midiEvent) + { + // ignore + } else { - d_stdout("DPF :: received atom not handled"); + d_stdout("DPF :: received atom not handled :: %s", + fUridUnmap != nullptr ? fUridUnmap->unmap(fUridUnmap->handle, atom->type) : "(null)"); } } #endif @@ -258,7 +264,7 @@ public: if (options[i].type == fURIDs.atomFloat) { const float sampleRate = *(const float*)options[i].value; - fUI.setSampleRate(sampleRate); + fUI.setSampleRate(sampleRate, true); continue; } else @@ -559,17 +565,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, float scaleFactor = 0.0f; uint32_t bgColor = 0; uint32_t fgColor = 0xffffffff; + const char* appClassName = nullptr; if (options != nullptr) { const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); + const LV2_URID uridAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String); const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate); const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor); const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor); #ifndef DISTRHO_OS_MAC const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor); #endif + const LV2_URID uridClassName = uridMap->map(uridMap->handle, "urn:distrho:className"); for (int i=0; options[i].key != 0; ++i) { @@ -603,6 +612,13 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, d_stderr("Host provides UI scale factor but has wrong value type"); } #endif + else if (options[i].key == uridClassName) + { + if (options[i].type == uridAtomString) + appClassName = (const char*)options[i].value; + else + d_stderr("Host provides UI scale factor but has wrong value type"); + } } } @@ -614,7 +630,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, return new UiLv2(bundlePath, winId, options, uridMap, features, controller, writeFunction, widget, instance, - sampleRate, scaleFactor, bgColor, fgColor); + sampleRate, scaleFactor, bgColor, fgColor, appClassName); } #define uiPtr ((UiLv2*)ui) @@ -715,4 +731,199 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) return (index == 0) ? &sLv2UiDescriptor : nullptr; } +#if defined(__MOD_DEVICES__) && defined(__EMSCRIPTEN__) +#include +#include + +typedef void (*_custom_param_set)(uint32_t port_index, float value); +typedef void (*_custom_patch_set)(const char* uri, const char* value); + +struct ModguiHandle { + LV2UI_Handle handle; + long loop_id; + _custom_param_set param_set; + _custom_patch_set patch_set; +}; + +enum URIs { + kUriNull, + kUriAtomEventTransfer, + kUriDpfKeyValue, +}; + +static std::vector kURIs; + +static LV2_URID lv2_urid_map(LV2_URID_Map_Handle, const char* const uri) +{ + for (size_t i=0, size=kURIs.size(); i= 1,); + + // d_stdout("lv2ui_write_function %p %u %u %u %p", controller, port_index, buffer_size, port_protocol, buffer); + ModguiHandle* const mhandle = static_cast(controller); + + switch (port_protocol) + { + case kUriNull: + mhandle->param_set(port_index, *static_cast(buffer)); + break; + case kUriAtomEventTransfer: + if (const LV2_Atom* const atom = static_cast(buffer)) + { + // d_stdout("lv2ui_write_function %u %u:%s", atom->size, atom->type, kURIs[atom->type].c_str()); + + // if (kURIs[atom->type] == "urn:distrho:KeyValueState") + { + const char* const key = (const char*)(atom + 1); + const char* const value = key + (std::strlen(key) + 1U); + // d_stdout("lv2ui_write_function %s %s", key, value); + + String urikey; + urikey = DISTRHO_PLUGIN_URI "#"; + urikey += key; + + mhandle->patch_set(urikey, value); + } + } + break; + } +} + +static void app_idle(void* const handle) +{ + static_cast(handle)->lv2ui_idle(); +} + +DISTRHO_PLUGIN_EXPORT +LV2UI_Handle modgui_init(const char* const className, _custom_param_set param_set, _custom_patch_set patch_set) +{ + d_stdout("init \"%s\"", className); + DISTRHO_SAFE_ASSERT_RETURN(className != nullptr, nullptr); + + static LV2_URID_Map uridMap = { nullptr, lv2_urid_map }; + static LV2_URID_Unmap uridUnmap = { nullptr, lv2_urid_unmap }; + + // known first URIDs, matching URIs + if (kURIs.empty()) + { + kURIs.push_back(""); + kURIs.push_back("http://lv2plug.in/ns/ext/atom#eventTransfer"); + kURIs.push_back(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState"); + } + + static float sampleRateValue = 48000.f; + static LV2_Options_Option options[3] = { + { + LV2_OPTIONS_INSTANCE, + 0, + uridMap.map(uridMap.handle, LV2_PARAMETERS__sampleRate), + sizeof(float), + uridMap.map(uridMap.handle, LV2_ATOM__Float), + &sampleRateValue + }, + { + LV2_OPTIONS_INSTANCE, + 0, + uridMap.map(uridMap.handle, "urn:distrho:className"), + std::strlen(className) + 1, + uridMap.map(uridMap.handle, LV2_ATOM__String), + className + }, + {} + }; + + static const LV2_Feature optionsFt = { LV2_OPTIONS__options, static_cast(options) }; + static const LV2_Feature uridMapFt = { LV2_URID__map, static_cast(&uridMap) }; + static const LV2_Feature uridUnmapFt = { LV2_URID__unmap, static_cast(&uridUnmap) }; + + static const LV2_Feature* features[] = { + &optionsFt, + &uridMapFt, + &uridUnmapFt, + nullptr + }; + + ModguiHandle* const mhandle = new ModguiHandle; + mhandle->handle = nullptr; + mhandle->loop_id = 0; + mhandle->param_set = param_set; + mhandle->patch_set = patch_set; + + LV2UI_Widget widget; + const LV2UI_Handle handle = lv2ui_instantiate(&sLv2UiDescriptor, + DISTRHO_PLUGIN_URI, + "", // bundlePath + lv2ui_write_function, + mhandle, + &widget, + features); + mhandle->handle = handle; + + static_cast(handle)->lv2ui_show(); + mhandle->loop_id = emscripten_set_interval(app_idle, 1000.0/60, handle); + + return mhandle; +} + +DISTRHO_PLUGIN_EXPORT +void modgui_param_set(const LV2UI_Handle handle, const uint32_t index, const float value) +{ + lv2ui_port_event(static_cast(handle)->handle, index, sizeof(float), kUriNull, &value); +} + +DISTRHO_PLUGIN_EXPORT +void modgui_patch_set(const LV2UI_Handle handle, const char* const uri, const char* const value) +{ + static const constexpr uint32_t URI_PREFIX_LEN = sizeof(DISTRHO_PLUGIN_URI); + DISTRHO_SAFE_ASSERT_RETURN(std::strncmp(uri, DISTRHO_PLUGIN_URI "#", URI_PREFIX_LEN) == 0,); + + const uint32_t keySize = std::strlen(uri + URI_PREFIX_LEN) + 1; + const uint32_t valueSize = std::strlen(value) + 1; + const uint32_t atomSize = sizeof(LV2_Atom) + keySize + valueSize; + + LV2_Atom* const atom = static_cast(std::malloc(atomSize)); + atom->size = atomSize; + atom->type = kUriDpfKeyValue; + + std::memcpy(static_cast(static_cast(atom + 1)), uri + URI_PREFIX_LEN, keySize); + std::memcpy(static_cast(static_cast(atom + 1)) + keySize, value, valueSize); + + lv2ui_port_event(static_cast(handle)->handle, + DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS, // events input port + atomSize, kUriAtomEventTransfer, atom); + + std::free(atom); +} + +DISTRHO_PLUGIN_EXPORT +void modgui_cleanup(const LV2UI_Handle handle) +{ + d_stdout("cleanup"); + ModguiHandle* const mhandle = static_cast(handle); + if (mhandle->loop_id != 0) + emscripten_clear_interval(mhandle->loop_id); + lv2ui_cleanup(mhandle->handle); + delete mhandle; +} +#endif + // ----------------------------------------------------------------------- diff --git a/dpf/distrho/src/DistrhoUIPrivateData.hpp b/dpf/distrho/src/DistrhoUIPrivateData.hpp index dfe78ed..2dffd15 100644 --- a/dpf/distrho/src/DistrhoUIPrivateData.hpp +++ b/dpf/distrho/src/DistrhoUIPrivateData.hpp @@ -32,6 +32,11 @@ # include "../../dgl/src/pugl.hpp" #endif +#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# include +# include +#endif + #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) # define DISTRHO_UI_IS_STANDALONE 1 #else @@ -60,7 +65,7 @@ struct PluginApplication DGL_NAMESPACE::IdleCallback* idleCallback; UI* ui; - explicit PluginApplication() + explicit PluginApplication(const char*) : idleCallback(nullptr), ui(nullptr) {} @@ -105,20 +110,26 @@ struct PluginApplication class PluginApplication : public DGL_NAMESPACE::Application { public: - explicit PluginApplication() + explicit PluginApplication(const char* className) : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) { -#ifndef DISTRHO_OS_WASM - const char* const className = ( -#ifdef DISTRHO_PLUGIN_BRAND - DISTRHO_PLUGIN_BRAND -#else - DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE) -#endif - "-" DISTRHO_PLUGIN_NAME - ); + #if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__) + if (className == nullptr) + { + className = ( + #ifdef DISTRHO_PLUGIN_BRAND + DISTRHO_PLUGIN_BRAND + #else + DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE) + #endif + "-" DISTRHO_PLUGIN_NAME + ); + } setClassName(className); -#endif + #else + // unused + (void)className; + #endif } void triggerIdleCallbacks() @@ -320,9 +331,10 @@ struct UI::PrivateData { uint fgColor; double scaleFactor; uintptr_t winId; -#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI char* uiStateFileKeyRequest; -#endif + std::map lastUsedDirnames; + #endif char* bundlePath; // Ignore initial resize events while initializing @@ -337,8 +349,8 @@ struct UI::PrivateData { setSizeFunc setSizeCallbackFunc; fileRequestFunc fileRequestCallbackFunc; - PrivateData() noexcept - : app(), + PrivateData(const char* const appClassName) noexcept + : app(appClassName), window(nullptr), sampleRate(0), parameterOffset(0), @@ -347,9 +359,9 @@ struct UI::PrivateData { fgColor(0xffffffff), scaleFactor(1.0), winId(0), -#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI uiStateFileKeyRequest(nullptr), -#endif + #endif bundlePath(nullptr), initializing(true), callbacksPtr(nullptr), @@ -360,32 +372,32 @@ struct UI::PrivateData { setSizeCallbackFunc(nullptr), fileRequestCallbackFunc(nullptr) { -#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) + #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2) parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; -# if DISTRHO_PLUGIN_WANT_LATENCY + #if DISTRHO_PLUGIN_WANT_LATENCY parameterOffset += 1; -# endif -#endif + #endif + #endif -#ifdef DISTRHO_PLUGIN_TARGET_LV2 -# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) + #ifdef DISTRHO_PLUGIN_TARGET_LV2 + #if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; -# endif -# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) + #endif + #if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE) parameterOffset += 1; -# endif -#endif + #endif + #endif -#ifdef DISTRHO_PLUGIN_TARGET_VST3 + #ifdef DISTRHO_PLUGIN_TARGET_VST3 parameterOffset += kVst3InternalParameterCount; -#endif + #endif } ~PrivateData() noexcept { -#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI std::free(uiStateFileKeyRequest); -#endif + #endif std::free(bundlePath); } @@ -438,7 +450,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) if (fileRequestCallbackFunc != nullptr) return fileRequestCallbackFunc(callbacksPtr, key); -#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI + #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI std::free(uiStateFileKeyRequest); uiStateFileKeyRequest = strdup(key); DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); @@ -449,8 +461,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) DGL_NAMESPACE::FileBrowserOptions opts; opts.title = title; + if (lastUsedDirnames.count(key)) + opts.startDir = lastUsedDirnames[key].c_str(); return window->openFileBrowser(opts); -#endif + #endif return false; } @@ -466,7 +480,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) if (initializing) return; - #if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI if (char* const key = ui->uiData->uiStateFileKeyRequest) { ui->uiData->uiStateFileKeyRequest = nullptr; @@ -474,8 +488,13 @@ inline void PluginWindow::onFileSelected(const char* const filename) { // notify DSP ui->setState(key, filename); + // notify UI ui->stateChanged(key, filename); + + // save dirname for next time + if (const char* const lastsep = std::strrchr(filename, DISTRHO_OS_SEP)) + ui->uiData->lastUsedDirnames[key] = std::string(filename, lastsep-filename); } std::free(key); return; diff --git a/dpf/distrho/src/DistrhoUIVST3.cpp b/dpf/distrho/src/DistrhoUIVST3.cpp index 9688ad0..5d9e6d7 100644 --- a/dpf/distrho/src/DistrhoUIVST3.cpp +++ b/dpf/distrho/src/DistrhoUIVST3.cpp @@ -233,7 +233,7 @@ private: HWND fTimerWindow; String fTimerWindowClassName; - WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) + static void WINAPI platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD) { reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback(); } diff --git a/dpf/distrho/src/jackbridge/JackBridge.cpp b/dpf/distrho/src/jackbridge/JackBridge.cpp index 1538f67..5f85ee4 100644 --- a/dpf/distrho/src/jackbridge/JackBridge.cpp +++ b/dpf/distrho/src/jackbridge/JackBridge.cpp @@ -934,8 +934,27 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options, delete nativeBridge; #endif #endif + if (status != nullptr) - *status = JackServerError; + { + int err = JackServerError; + + #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (nativeBridge != nullptr) + { + err + #ifdef HAVE_JACK + |= + #else + = + #endif + JackBridgeNativeFailed; + } + #endif + + *status = static_cast(err); + } + return nullptr; } diff --git a/dpf/distrho/src/jackbridge/JackBridge.hpp b/dpf/distrho/src/jackbridge/JackBridge.hpp index 0788093..b287b47 100644 --- a/dpf/distrho/src/jackbridge/JackBridge.hpp +++ b/dpf/distrho/src/jackbridge/JackBridge.hpp @@ -126,7 +126,8 @@ enum JackStatus { JackShmFailure = 0x0200, JackVersionError = 0x0400, JackBackendError = 0x0800, - JackClientZombie = 0x1000 + JackClientZombie = 0x1000, + JackBridgeNativeFailed = 0x10000 }; enum JackLatencyCallbackMode { diff --git a/dpf/distrho/src/jackbridge/NativeBridge.hpp b/dpf/distrho/src/jackbridge/NativeBridge.hpp index b30e8bf..1f80c91 100644 --- a/dpf/distrho/src/jackbridge/NativeBridge.hpp +++ b/dpf/distrho/src/jackbridge/NativeBridge.hpp @@ -1,6 +1,6 @@ /* * Native Bridge for DPF - * Copyright (C) 2021-2022 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -21,6 +21,18 @@ #include "../../extra/RingBuffer.hpp" +#if DISTRHO_PLUGIN_NUM_INPUTS > 2 +# define DISTRHO_PLUGIN_NUM_INPUTS_2 2 +#else +# define DISTRHO_PLUGIN_NUM_INPUTS_2 DISTRHO_PLUGIN_NUM_INPUTS +#endif + +#if DISTRHO_PLUGIN_NUM_OUTPUTS > 2 +# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 2 +#else +# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 DISTRHO_PLUGIN_NUM_OUTPUTS +#endif + using DISTRHO_NAMESPACE::HeapRingBuffer; struct NativeBridge { @@ -31,6 +43,8 @@ struct NativeBridge { // Port caching information uint numAudioIns; uint numAudioOuts; + uint numCvIns; + uint numCvOuts; uint numMidiIns; uint numMidiOuts; @@ -43,9 +57,10 @@ struct NativeBridge { // Runtime buffers enum PortMask { kPortMaskAudio = 0x1000, - kPortMaskMIDI = 0x2000, - kPortMaskInput = 0x4000, - kPortMaskOutput = 0x8000, + kPortMaskCV = 0x2000, + kPortMaskMIDI = 0x4000, + kPortMaskInput = 0x10000, + kPortMaskOutput = 0x20000, kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, }; @@ -71,6 +86,8 @@ struct NativeBridge { sampleRate(0), numAudioIns(0), numAudioOuts(0), + numCvIns(0), + numCvOuts(0), numMidiIns(0), numMidiOuts(0), jackProcessCallback(nullptr), @@ -211,27 +228,27 @@ struct NativeBridge { if (audio) { - #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)]; - for (uint i=0; i 0 - std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); - #endif + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 + std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS); + #endif } if (midi) { - #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512); - midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512); - #endif - #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT - midiOutBuffer.createBuffer(2048); - #endif + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT + midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512); + midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512); + #endif + #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT + midiOutBuffer.createBuffer(2048); + #endif } } @@ -252,27 +269,39 @@ struct NativeBridge { jack_port_t* registerPort(const char* const type, const ulong flags) { - bool isAudio, isInput; - - /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) - isAudio = true; - else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) - isAudio = false; - else - return nullptr; + uintptr_t ret = 0; /**/ if (flags & JackPortIsInput) - isInput = true; + ret |= kPortMaskInput; else if (flags & JackPortIsOutput) - isInput = false; + ret |= kPortMaskOutput; else return nullptr; - const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI) - | (isInput ? kPortMaskInput : kPortMaskOutput); + /**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + { + if (flags & JackPortIsControlVoltage) + { + ret |= kPortMaskAudio; + ret += flags & JackPortIsInput ? numAudioIns++ : numAudioOuts++; + } + else + { + ret |= kPortMaskCV; + ret += flags & JackPortIsInput ? numCvIns++ : numCvOuts++; + } + } + else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) + { + ret |= kPortMaskMIDI; + ret += flags & JackPortIsInput ? numMidiIns++ : numMidiOuts++; + } + else + { + return nullptr; + } - return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++) - : (isInput ? numMidiIns++ : numMidiOuts++))); + return (jack_port_t*)ret; } void* getPortBuffer(jack_port_t* const port) @@ -281,7 +310,7 @@ struct NativeBridge { DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 - if (portMask & kPortMaskAudio) + if (portMask & (kPortMaskAudio|kPortMaskCV)) return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT diff --git a/dpf/distrho/src/jackbridge/RtAudioBridge.hpp b/dpf/distrho/src/jackbridge/RtAudioBridge.hpp index 1d5006d..cd2fc76 100644 --- a/dpf/distrho/src/jackbridge/RtAudioBridge.hpp +++ b/dpf/distrho/src/jackbridge/RtAudioBridge.hpp @@ -28,9 +28,9 @@ # define RTAUDIO_API_TYPE MACOSX_CORE # define RTMIDI_API_TYPE MACOSX_CORE #elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) -# define __WINDOWS_DS__ +# define __WINDOWS_WASAPI__ # define __WINDOWS_MM__ -# define RTAUDIO_API_TYPE WINDOWS_DS +# define RTAUDIO_API_TYPE WINDOWS_WASAPI # define RTMIDI_API_TYPE WINDOWS_MM #else # if defined(HAVE_PULSEAUDIO) @@ -50,7 +50,9 @@ # include "rtmidi/RtMidi.h" # include "../../extra/ScopedPointer.hpp" # include "../../extra/String.hpp" +# include "../../extra/ScopedDenormalDisable.hpp" +using DISTRHO_NAMESPACE::ScopedDenormalDisable; using DISTRHO_NAMESPACE::ScopedPointer; using DISTRHO_NAMESPACE::String; @@ -281,13 +283,20 @@ struct RtAudioBridge : NativeBridge { return ok; } - bool _open(const bool withInput) + bool _open(const bool withInput, RtAudio* tryingAgain = nullptr) { ScopedPointer rtAudio; - try { - rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); - } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); + if (tryingAgain == nullptr) + { + try { + rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE); + } DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false); + } + else + { + rtAudio = tryingAgain; + } uint rtAudioBufferFrames = nextBufferSize; @@ -300,15 +309,15 @@ struct RtAudioBridge : NativeBridge { if (withInput) { inParams.deviceId = rtAudio->getDefaultInputDevice(); - inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; + inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2; inParamsPtr = &inParams; } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 RtAudio::StreamParameters outParams; - outParams.deviceId = rtAudio->getDefaultOutputDevice(); - outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS; + outParams.deviceId = tryingAgain != nullptr ? 1 : rtAudio->getDefaultOutputDevice(); + outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS_2; RtAudio::StreamParameters* const outParamsPtr = &outParams; #else RtAudio::StreamParameters* const outParamsPtr = nullptr; @@ -330,6 +339,10 @@ struct RtAudioBridge : NativeBridge { rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, RtAudioCallback, this, &opts, nullptr); } catch (const RtAudioError& err) { + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (outParams.deviceId == 0 && rtAudio->getDeviceCount() > 1) + return _open(withInput, rtAudio.release()); + #endif d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); return false; } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); @@ -357,14 +370,14 @@ struct RtAudioBridge : NativeBridge { if (self->jackProcessCallback == nullptr) { if (outputBuffer != nullptr) - std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS); + std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS_2); return 0; } #if DISTRHO_PLUGIN_NUM_INPUTS > 0 if (float* const insPtr = static_cast(inputBuffer)) { - for (uint i=0; iaudioBuffers[i] = insPtr + (i * numFrames); } #endif @@ -372,11 +385,12 @@ struct RtAudioBridge : NativeBridge { #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 if (float* const outsPtr = static_cast(outputBuffer)) { - for (uint i=0; iaudioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames); } #endif + const ScopedDenormalDisable sdd; self->jackProcessCallback(numFrames, self->jackProcessArg); return 0; diff --git a/dpf/distrho/src/jackbridge/SDL2Bridge.hpp b/dpf/distrho/src/jackbridge/SDL2Bridge.hpp index e40093c..6e8a882 100644 --- a/dpf/distrho/src/jackbridge/SDL2Bridge.hpp +++ b/dpf/distrho/src/jackbridge/SDL2Bridge.hpp @@ -1,6 +1,6 @@ /* * SDL Bridge for DPF - * Copyright (C) 2021-2022 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -18,6 +18,7 @@ #define SDL_BRIDGE_HPP_INCLUDED #include "NativeBridge.hpp" +#include "../../extra/ScopedDenormalDisable.hpp" #include @@ -67,7 +68,7 @@ struct SDL2Bridge : NativeBridge { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); - requested.channels = DISTRHO_PLUGIN_NUM_INPUTS; + requested.channels = DISTRHO_PLUGIN_NUM_INPUTS_2; requested.callback = AudioInputCallback; SDL_AudioSpec receivedCapture; @@ -75,11 +76,12 @@ struct SDL2Bridge : NativeBridge { SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); if (captureDeviceId == 0) { - d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError()); + d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError()); + #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 return false; + #endif } - - if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS) + else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2) { SDL_CloseAudioDevice(captureDeviceId); captureDeviceId = 0; @@ -91,7 +93,7 @@ struct SDL2Bridge : NativeBridge { #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 SDL_AudioSpec receivedPlayback; SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback"); - requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS; + requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS_2; requested.callback = AudioOutputCallback; playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, @@ -102,7 +104,7 @@ struct SDL2Bridge : NativeBridge { return false; } - if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) + if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2) { SDL_CloseAudioDevice(playbackDeviceId); playbackDeviceId = 0; @@ -113,30 +115,39 @@ struct SDL2Bridge : NativeBridge { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 // if using both input and output, make sure they match - if (receivedCapture.samples != receivedPlayback.samples) + if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0) { SDL_CloseAudioDevice(captureDeviceId); SDL_CloseAudioDevice(playbackDeviceId); captureDeviceId = playbackDeviceId = 0; - d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedCapture.samples); + d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples); return false; } - if (receivedCapture.freq != receivedPlayback.freq) + if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0) { SDL_CloseAudioDevice(captureDeviceId); SDL_CloseAudioDevice(playbackDeviceId); captureDeviceId = playbackDeviceId = 0; - d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq); + d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq); return false; } #endif #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - bufferSize = receivedCapture.samples; - sampleRate = receivedCapture.freq; - #else - bufferSize = receivedPlayback.samples; - sampleRate = receivedPlayback.freq; + if (captureDeviceId != 0) + { + bufferSize = receivedCapture.samples; + sampleRate = receivedCapture.freq; + } + #endif + #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + else + #endif + #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + { + bufferSize = receivedPlayback.samples; + sampleRate = receivedPlayback.freq; + } #endif allocBuffers(true, false); @@ -146,9 +157,11 @@ struct SDL2Bridge : NativeBridge { bool close() override { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); - SDL_CloseAudioDevice(captureDeviceId); - captureDeviceId = 0; + if (captureDeviceId != 0) + { + SDL_CloseAudioDevice(captureDeviceId); + captureDeviceId = 0; + } #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); @@ -163,8 +176,8 @@ struct SDL2Bridge : NativeBridge { bool activate() override { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); - SDL_PauseAudioDevice(captureDeviceId, 0); + if (captureDeviceId != 0) + SDL_PauseAudioDevice(captureDeviceId, 0); #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); @@ -176,8 +189,8 @@ struct SDL2Bridge : NativeBridge { bool deactivate() override { #if DISTRHO_PLUGIN_NUM_INPUTS > 0 - DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false); - SDL_PauseAudioDevice(captureDeviceId, 1); + if (captureDeviceId != 0) + SDL_PauseAudioDevice(captureDeviceId, 1); #endif #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); @@ -198,19 +211,20 @@ struct SDL2Bridge : NativeBridge { if (self->jackProcessCallback == nullptr) return; - const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS); + const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS_2); DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); const float* const fstream = (const float*)stream; - for (uint i=0; iaudioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i]; + self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS_2 + i]; } #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0 // if there are no outputs, run process callback now + const ScopedDenormalDisable sdd; self->jackProcessCallback(numFrames, self->jackProcessArg); #endif } @@ -231,17 +245,18 @@ struct SDL2Bridge : NativeBridge { return; } - const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); + const uint numFrames = static_cast(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS_2); DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); + const ScopedDenormalDisable sdd; self->jackProcessCallback(numFrames, self->jackProcessArg); float* const fstream = (float*)stream; - for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) + for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i) { for (uint j=0; j < numFrames; ++j) - fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; + fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS_2 + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j]; } } #endif diff --git a/dpf/distrho/src/jackbridge/WebBridge.hpp b/dpf/distrho/src/jackbridge/WebBridge.hpp index 3afb9e3..c5e1587 100644 --- a/dpf/distrho/src/jackbridge/WebBridge.hpp +++ b/dpf/distrho/src/jackbridge/WebBridge.hpp @@ -163,7 +163,7 @@ struct WebBridge : NativeBridge { if (WAB.audioContext.state === 'suspended') WAB.audioContext.resume(); }); - }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); + }, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this); return true; } @@ -217,11 +217,11 @@ struct WebBridge : NativeBridge { constraints['audio'] = true; constraints['video'] = false; constraints['autoGainControl'] = {}; - constraints['autoGainControl']['exact'] = false; + constraints['autoGainControl']['ideal'] = false; constraints['echoCancellation'] = {}; - constraints['echoCancellation']['exact'] = false; + constraints['echoCancellation']['ideal'] = false; constraints['noiseSuppression'] = {}; - constraints['noiseSuppression']['exact'] = false; + constraints['noiseSuppression']['ideal'] = false; constraints['channelCount'] = {}; constraints['channelCount']['min'] = 0; constraints['channelCount']['ideal'] = numInputs; @@ -236,6 +236,25 @@ struct WebBridge : NativeBridge { constraints['googAutoGainControl'] = false; var success = function(stream) { + var track = stream.getAudioTracks()[0]; + + // try to force as much as we can + track.applyConstraints({'autoGainControl': { 'exact': false } }) + .then(function(){console.log("Mic/Input auto-gain control has been disabled")}) + .catch(function(){console.log("Cannot disable Mic/Input auto-gain")}); + + track.applyConstraints({'echoCancellation': { 'exact': false } }) + .then(function(){console.log("Mic/Input echo-cancellation has been disabled")}) + .catch(function(){console.log("Cannot disable Mic/Input echo-cancellation")}); + + track.applyConstraints({'noiseSuppression': { 'exact': false } }) + .then(function(){console.log("Mic/Input noise-suppression has been disabled")}) + .catch(function(){console.log("Cannot disable Mic/Input noise-suppression")}); + + track.applyConstraints({'googAutoGainControl': { 'exact': false } }) + .then(function(){}) + .catch(function(){}); + WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); WAB.captureStreamNode.connect(WAB.processor); }; @@ -247,7 +266,7 @@ struct WebBridge : NativeBridge { } else if (navigator.webkitGetUserMedia !== undefined) { navigator.webkitGetUserMedia(constraints, success, fail); } - }, DISTRHO_PLUGIN_NUM_INPUTS); + }, DISTRHO_PLUGIN_NUM_INPUTS_2); return true; } @@ -279,7 +298,7 @@ struct WebBridge : NativeBridge { WAB.captureStreamNode.disconnect(WAB.processor); return 1; - }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, newBufferSize) != 0; + }, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, newBufferSize) != 0; if (!success) return false; @@ -292,9 +311,10 @@ struct WebBridge : NativeBridge { bufferSizeCallback(newBufferSize, jackBufferSizeArg); EM_ASM({ - var numInputs = $0; - var numOutputs = $1; - var bufferSize = $2; + var numInputsR = $0; + var numInputs = $1; + var numOutputs = $2; + var bufferSize = $3; var WAB = Module['WebAudioBridge']; // store the new processor @@ -309,13 +329,13 @@ struct WebBridge : NativeBridge { var buffer = e['inputBuffer']['getChannelData'](i); for (var j = 0; j < bufferSize; ++j) { // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); - HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; + HEAPF32[$4 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; } } - dynCall('vi', $4, [$5]); + dynCall('vi', $5, [$6]); for (var i = 0; i < numOutputs; ++i) { var buffer = e['outputBuffer']['getChannelData'](i); - var offset = bufferSize * (numInputs + i); + var offset = bufferSize * (numInputsR + i); for (var j = 0; j < bufferSize; ++j) { buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; } @@ -329,7 +349,7 @@ struct WebBridge : NativeBridge { if (WAB.captureStreamNode) WAB.captureStreamNode.connect(WAB.processor); - }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this); return true; } @@ -452,7 +472,7 @@ struct WebBridge : NativeBridge { } else { - for (uint i=0; iaudioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i], 0, sizeof(float)*numFrames); } } diff --git a/dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp b/dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp index 3e64b85..addfc0a 100644 --- a/dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp +++ b/dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp @@ -4640,13 +4640,10 @@ void RtApiWasapi::stopStream( void ) stream_.state = STREAM_STOPPING; // wait until stream thread is stopped - while( stream_.state != STREAM_STOPPED ) { - Sleep( 1 ); + for (int i=0; i < 2 && stream_.state != STREAM_STOPPED; ++i ) { + Sleep( 1000 * stream_.bufferSize / stream_.sampleRate ); } - // Wait for the last buffer to play before stopping. - Sleep( 1000 * stream_.bufferSize / stream_.sampleRate ); - // close thread handle if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; diff --git a/dpf/utils/generate-ttl.sh b/dpf/utils/generate-ttl.sh index 99ce88d..0ddbb1f 100755 --- a/dpf/utils/generate-ttl.sh +++ b/dpf/utils/generate-ttl.sh @@ -33,7 +33,9 @@ FOLDERS=`find . -type d -name \*.lv2` for i in ${FOLDERS}; do cd ${i} - FILE="$(ls *.${EXT} | sort | head -n 1)" - ${EXE_WRAPPER} "${GEN}" "./${FILE}" + FILE="$(ls *.${EXT} 2>/dev/null | sort | head -n 1)" + if [ -n "${FILE}" ]; then + ${EXE_WRAPPER} "${GEN}" "./${FILE}" + fi cd .. done