@@ -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 |