| @@ -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") | |||
| @@ -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))) | |||
| @@ -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)/" $< > $@ | |||
| @@ -75,10 +75,10 @@ include(CMakeParseArguments) | |||
| # `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3`, `clap` | |||
| # | |||
| # `UI_TYPE` <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` <file1>...<fileN> | |||
| # list of sources which are part of both DSP and UI | |||
| # | |||
| # `FILES_DSP` <file1>...<fileN> | |||
| # 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` <file1>...<fileN> | |||
| # 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() | |||
| @@ -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. | |||
| @@ -156,7 +156,7 @@ public: | |||
| bool scrollEvent(const Widget::ScrollEvent& ev); | |||
| protected: | |||
| State getState() const noexcept; | |||
| State getState() const noexcept; | |||
| private: | |||
| struct PrivateData; | |||
| @@ -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 | |||
| @@ -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() | |||
| @@ -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; | |||
| @@ -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; | |||
| @@ -1,492 +0,0 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 <stdint.h> | |||
| /* | |||
| 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 <OpenGL/gl.h> | |||
| #else | |||
| # ifdef _WIN32 | |||
| # include <winsock2.h> | |||
| # include <windows.h> /* Broken Windows GL headers require this */ | |||
| # endif | |||
| # include <GL/gl.h> | |||
| #endif | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #else | |||
| # include <stdbool.h> | |||
| #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 */ | |||
| @@ -1,441 +0,0 @@ | |||
| /* | |||
| Copyright 2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 <Application.h> | |||
| #include <interface/Window.h> | |||
| #ifdef PUGL_CAIRO | |||
| #include <cairo/cairo.h> | |||
| typedef BView BViewType; | |||
| #endif | |||
| #ifdef PUGL_OPENGL | |||
| #include <GL/gl.h> | |||
| #include <opengl/GLView.h> | |||
| 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<int>(newWidth), static_cast<int>(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; | |||
| } | |||
| @@ -1,263 +0,0 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 | |||
| } | |||
| @@ -1,974 +0,0 @@ | |||
| /* | |||
| Copyright 2012 David Robillard <http://drobilla.net> | |||
| Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 <stdlib.h> | |||
| #ifdef PUGL_CAIRO | |||
| #import <cairo.h> | |||
| #import <cairo-quartz.h> | |||
| #endif | |||
| #import <Cocoa/Cocoa.h> | |||
| #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<PuglGenericView> *self, NSEvent* event) | |||
| { | |||
| [[NSCursor arrowCursor] set]; | |||
| (void)self; | |||
| (void)event; | |||
| } | |||
| static void | |||
| updateTrackingAreas(NSView<PuglGenericView> *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<PuglGenericView> *self, NSWindow* newWindow) | |||
| { | |||
| if (newWindow != nil) { | |||
| [newWindow setAcceptsMouseMovedEvents:YES]; | |||
| [newWindow makeFirstResponder:self]; | |||
| } | |||
| } | |||
| static void | |||
| reshape(NSView<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> *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<PuglGenericView> | |||
| { | |||
| @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<PuglGenericView> | |||
| { | |||
| 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<PuglGenericView>* 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; | |||
| } | |||
| @@ -1,565 +0,0 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 <ctime> | |||
| #include <cstdio> | |||
| #include <cstdlib> | |||
| #include <winsock2.h> | |||
| #include <windows.h> | |||
| #include <windowsx.h> | |||
| #ifdef PUGL_CAIRO | |||
| #include <cairo/cairo.h> | |||
| #include <cairo/cairo-win32.h> | |||
| #endif | |||
| #ifdef PUGL_OPENGL | |||
| #include <GL/gl.h> | |||
| #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; | |||
| } | |||
| @@ -1,739 +0,0 @@ | |||
| /* | |||
| Copyright 2012-2014 David Robillard <http://drobilla.net> | |||
| Copyright 2011-2012 Ben Loftis, Harrison Consoles | |||
| Copyright 2013,2015 Robin Gareus <robin@gareus.org> | |||
| Copyright 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
| 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 <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #ifdef PUGL_CAIRO | |||
| #include <cairo/cairo.h> | |||
| #include <cairo/cairo-xlib.h> | |||
| #endif | |||
| #ifdef PUGL_OPENGL | |||
| #include <GL/gl.h> | |||
| #include <GL/glx.h> | |||
| #endif | |||
| #include <X11/Xatom.h> | |||
| #include <X11/Xlib.h> | |||
| #include <X11/Xutil.h> | |||
| #include <X11/keysym.h> | |||
| #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; | |||
| } | |||
| @@ -1,29 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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; | |||
| } | |||
| @@ -1,50 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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 | |||
| @@ -1,28 +1,348 @@ | |||
| /* | |||
| Copyright 2012-2019 David Robillard <http://drobilla.net> | |||
| Copyright 2019-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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 <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // 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 | |||
| @@ -1,35 +1,25 @@ | |||
| /* | |||
| Copyright 2012-2019 David Robillard <http://drobilla.net> | |||
| Copyright 2019-2020 Filipe Coelho <falktx@falktx.com> | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // 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 <Application.h> | |||
| #include <Window.h> | |||
| // 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 | |||
| @@ -0,0 +1,87 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "../pugl-upstream/src/stub.h" | |||
| #include "haiku.h" | |||
| #include "pugl/pugl.h" | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <GL/gl.h> | |||
| #include <opengl/GLView.h> | |||
| 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; | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // 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; | |||
| } | |||
| @@ -1,48 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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)]; | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // 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 | |||
| @@ -0,0 +1,228 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // SPDX-License-Identifier: ISC | |||
| #include "../pugl-upstream/src/stub.h" | |||
| #include "wasm.h" | |||
| #include "pugl/pugl.h" | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <EGL/egl.h> | |||
| // 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; | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| // Copyright 2012-2022 David Robillard <d@drobilla.net> | |||
| // Copyright 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| // 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; | |||
| } | |||
| @@ -1,118 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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); | |||
| } | |||
| @@ -1,111 +0,0 @@ | |||
| /* | |||
| Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
| 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 <sys/types.h> | |||
| #include <unistd.h> | |||
| 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<int>(width); | |||
| sizeHints.height = static_cast<int>(height); | |||
| sizeHints.min_width = static_cast<int>(width); | |||
| sizeHints.min_height = static_cast<int>(height); | |||
| sizeHints.max_width = static_cast<int>(width); | |||
| sizeHints.max_height = static_cast<int>(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); | |||
| } | |||
| @@ -37,7 +37,14 @@ | |||
| #include <cstring> | |||
| #include <ctime> | |||
| #if defined(DISTRHO_OS_MAC) | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| # include <Application.h> | |||
| # include <Window.h> | |||
| # ifdef DGL_OPENGL | |||
| # include <GL/gl.h> | |||
| # include <opengl/GLView.h> | |||
| # endif | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| # import <Cocoa/Cocoa.h> | |||
| # include <dlfcn.h> | |||
| # include <mach/mach_time.h> | |||
| @@ -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<PuglSpan>(width); | |||
| view->sizeHints[PUGL_DEFAULT_SIZE].height = view->frame.height = static_cast<PuglSpan>(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 | |||
| @@ -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); | |||
| @@ -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. | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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" | |||
| @@ -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) | |||
| @@ -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" | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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<typename T> | |||
| static inline | |||
| static inline constexpr | |||
| bool d_isEqual(const T& v1, const T& v2) | |||
| { | |||
| return std::abs(v1-v2) < std::numeric_limits<T>::epsilon(); | |||
| @@ -265,7 +265,7 @@ bool d_isEqual(const T& v1, const T& v2) | |||
| Returns true if they don't match. | |||
| */ | |||
| template<typename T> | |||
| static inline | |||
| static inline constexpr | |||
| bool d_isNotEqual(const T& v1, const T& v2) | |||
| { | |||
| return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon(); | |||
| @@ -275,7 +275,7 @@ bool d_isNotEqual(const T& v1, const T& v2) | |||
| Safely check if a floating point number is zero. | |||
| */ | |||
| template<typename T> | |||
| static inline | |||
| static inline constexpr | |||
| bool d_isZero(const T& value) | |||
| { | |||
| return std::abs(value) < std::numeric_limits<T>::epsilon(); | |||
| @@ -285,7 +285,7 @@ bool d_isZero(const T& value) | |||
| Safely check if a floating point number is not zero. | |||
| */ | |||
| template<typename T> | |||
| static inline | |||
| static inline constexpr | |||
| bool d_isNotZero(const T& value) | |||
| { | |||
| return std::abs(value) >= std::numeric_limits<T>::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 | |||
| @@ -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); | |||
| @@ -0,0 +1,112 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 <xmmintrin.h> | |||
| #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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 <algorithm> | |||
| #if __cplusplus >= 201703L | |||
| # include <string_view> | |||
| #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<char*>(strView.data())), | |||
| fBufferLen(strView.size()), | |||
| fBufferAlloc(false) {} | |||
| #endif | |||
| /* | |||
| * Integer. | |||
| */ | |||
| @@ -0,0 +1,204 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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<ClapUI*>(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<const clap_event_param_value*>(static_cast<const void*>(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<ARRAY_SIZE(kSupportedAPIs); ++i) | |||
| { | |||
| @@ -2080,14 +2083,14 @@ static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const ap | |||
| } | |||
| // TODO DPF external UI | |||
| static bool clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating) | |||
| static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating) | |||
| { | |||
| *api = kSupportedAPIs[0]; | |||
| *is_floating = false; | |||
| return true; | |||
| } | |||
| static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating) | |||
| static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating) | |||
| { | |||
| for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i) | |||
| { | |||
| @@ -2101,13 +2104,13 @@ static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const | |||
| return false; | |||
| } | |||
| static void clap_gui_destroy(const clap_plugin_t* const plugin) | |||
| static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin) | |||
| { | |||
| PluginCLAP* const instance = static_cast<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(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<PluginCLAP*>(plugin->plugin_data); | |||
| return is_input ? instance->getAudioPortCount<true>() | |||
| : instance->getAudioPortCount<false>(); | |||
| } | |||
| 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; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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" | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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; | |||
| @@ -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; | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 <direct.h> | |||
| #else | |||
| # include <sys/stat.h> | |||
| # include <sys/types.h> | |||
| # include <unistd.h> | |||
| #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: <http://moddevices.com/ns/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 <modgui> ;\n"; | |||
| modguiString += " modgui:iconTemplate <modgui/icon.html> ;\n"; | |||
| modguiString += " modgui:javascript <modgui/javascript.js> ;\n"; | |||
| modguiString += " modgui:stylesheet <modgui/stylesheet.css> ;\n"; | |||
| modguiString += " modgui:screenshot <modgui/screenshot.png> ;\n"; | |||
| modguiString += " modgui:thumbnail <modgui/thumbnail.png> ;\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('<h2>'+err.join('<br>')+'</h2>');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 << "<div class='" DISTRHO_PLUGIN_MODGUI_CLASS_NAME " mod-pedal'>" << std::endl; | |||
| iconFile << " <div mod-role='drag-handle' class='mod-drag-handle'></div>" << std::endl; | |||
| iconFile << " <div class='mod-plugin-title'><h1>{{#brand}}{{brand}} | {{/brand}}{{label}}</h1></div>" << std::endl; | |||
| iconFile << " <div class='mod-light on' mod-role='bypass-light'></div>" << std::endl; | |||
| iconFile << " <div class='mod-control-group mod-switch'>" << std::endl; | |||
| iconFile << " <div class='mod-control-group mod-switch-image mod-port transport' mod-role='bypass' mod-widget='film'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " <div class='canvas_wrapper'>" << std::endl; | |||
| iconFile << " <canvas oncontextmenu='event.preventDefault()' tabindex=-1></canvas>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-input'>" << std::endl; | |||
| iconFile << " {{#effect.ports.audio.input}}" << std::endl; | |||
| iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.audio.input}}" << std::endl; | |||
| iconFile << " {{#effect.ports.midi.input}}" << std::endl; | |||
| iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.midi.input}}" << std::endl; | |||
| iconFile << " {{#effect.ports.cv.input}}" << std::endl; | |||
| iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.cv.input}}" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-output'>" << std::endl; | |||
| iconFile << " {{#effect.ports.audio.output}}" << std::endl; | |||
| iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.audio.output}}" << std::endl; | |||
| iconFile << " {{#effect.ports.midi.output}}" << std::endl; | |||
| iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.midi.output}}" << std::endl; | |||
| iconFile << " {{#effect.ports.cv.output}}" << std::endl; | |||
| iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl; | |||
| iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << " {{/effect.ports.cv.output}}" << std::endl; | |||
| iconFile << " </div>" << std::endl; | |||
| iconFile << "</div>" << 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; | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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) | |||
| { | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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" | |||
| @@ -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; | |||
| @@ -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<LV2_URID_Unmap>(features, LV2_URID__unmap)), | |||
| fUiPortMap(getLv2Feature<LV2UI_Port_Map>(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 <emscripten/html5.h> | |||
| #include <string> | |||
| 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<std::string> kURIs; | |||
| static LV2_URID lv2_urid_map(LV2_URID_Map_Handle, const char* const uri) | |||
| { | |||
| for (size_t i=0, size=kURIs.size(); i<size; ++i) | |||
| { | |||
| if (kURIs[i] == uri) | |||
| return i; | |||
| } | |||
| kURIs.push_back(uri); | |||
| return kURIs.size() - 1u; | |||
| } | |||
| static const char* lv2_urid_unmap(LV2_URID_Map_Handle, const LV2_URID urid) | |||
| { | |||
| return kURIs[urid].c_str(); | |||
| } | |||
| static void lv2ui_write_function(LV2UI_Controller controller, | |||
| uint32_t port_index, | |||
| uint32_t buffer_size, | |||
| uint32_t port_protocol, | |||
| const void* buffer) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(buffer_size >= 1,); | |||
| // d_stdout("lv2ui_write_function %p %u %u %u %p", controller, port_index, buffer_size, port_protocol, buffer); | |||
| ModguiHandle* const mhandle = static_cast<ModguiHandle*>(controller); | |||
| switch (port_protocol) | |||
| { | |||
| case kUriNull: | |||
| mhandle->param_set(port_index, *static_cast<const float*>(buffer)); | |||
| break; | |||
| case kUriAtomEventTransfer: | |||
| if (const LV2_Atom* const atom = static_cast<const LV2_Atom*>(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<UiLv2*>(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<void*>(options) }; | |||
| static const LV2_Feature uridMapFt = { LV2_URID__map, static_cast<void*>(&uridMap) }; | |||
| static const LV2_Feature uridUnmapFt = { LV2_URID__unmap, static_cast<void*>(&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<UiLv2*>(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<ModguiHandle*>(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<LV2_Atom*>(std::malloc(atomSize)); | |||
| atom->size = atomSize; | |||
| atom->type = kUriDpfKeyValue; | |||
| std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)), uri + URI_PREFIX_LEN, keySize); | |||
| std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)) + keySize, value, valueSize); | |||
| lv2ui_port_event(static_cast<ModguiHandle*>(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<ModguiHandle*>(handle); | |||
| if (mhandle->loop_id != 0) | |||
| emscripten_clear_interval(mhandle->loop_id); | |||
| lv2ui_cleanup(mhandle->handle); | |||
| delete mhandle; | |||
| } | |||
| #endif | |||
| // ----------------------------------------------------------------------- | |||
| @@ -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 <map> | |||
| # include <string> | |||
| #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<std::string,std::string> 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; | |||
| @@ -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<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback(); | |||
| } | |||
| @@ -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<jack_status_t>(err); | |||
| } | |||
| return nullptr; | |||
| } | |||
| @@ -126,7 +126,8 @@ enum JackStatus { | |||
| JackShmFailure = 0x0200, | |||
| JackVersionError = 0x0400, | |||
| JackBackendError = 0x0800, | |||
| JackClientZombie = 0x1000 | |||
| JackClientZombie = 0x1000, | |||
| JackBridgeNativeFailed = 0x10000 | |||
| }; | |||
| enum JackLatencyCallbackMode { | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * Native Bridge for DPF | |||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| audioBuffers[i] = audioBufferStorage + (bufferSize * i); | |||
| #endif | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| audioBuffers[i] = audioBufferStorage + (bufferSize * i); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_NUM_INPUTS > 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 | |||
| @@ -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> 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<float*>(inputBuffer)) | |||
| { | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i) | |||
| self->audioBuffers[i] = insPtr + (i * numFrames); | |||
| } | |||
| #endif | |||
| @@ -372,11 +385,12 @@ struct RtAudioBridge : NativeBridge { | |||
| #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | |||
| if (float* const outsPtr = static_cast<float*>(outputBuffer)) | |||
| { | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i) | |||
| self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames); | |||
| } | |||
| #endif | |||
| const ScopedDenormalDisable sdd; | |||
| self->jackProcessCallback(numFrames, self->jackProcessArg); | |||
| return 0; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * SDL Bridge for DPF | |||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 <SDL.h> | |||
| @@ -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<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS); | |||
| const uint numFrames = static_cast<uint>(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; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i) | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i) | |||
| { | |||
| for (uint j=0; j<numFrames; ++j) | |||
| self->audioBuffers[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<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
| const uint numFrames = static_cast<uint>(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 | |||
| @@ -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; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||
| for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i) | |||
| std::memset(self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i], 0, sizeof(float)*numFrames); | |||
| } | |||
| } | |||
| @@ -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."; | |||
| @@ -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 | |||