@@ -32,7 +32,7 @@ if(DPF_LIBRARIES) | |||||
find_package(PkgConfig) | find_package(PkgConfig) | ||||
if(PKG_CONFIG_FOUND) | if(PKG_CONFIG_FOUND) | ||||
pkg_check_modules(CAIRO "cairo") | pkg_check_modules(CAIRO "cairo") | ||||
if(CAIRO_FOUND) | |||||
if(CAIRO_FOUND AND (NOT HAIKU)) | |||||
dpf__add_dgl_cairo(FALSE) | dpf__add_dgl_cairo(FALSE) | ||||
endif() | endif() | ||||
endif() | endif() | ||||
@@ -43,11 +43,14 @@ if(DPF_EXAMPLES) | |||||
find_package(PkgConfig) | find_package(PkgConfig) | ||||
if(PKG_CONFIG_FOUND) | if(PKG_CONFIG_FOUND) | ||||
pkg_check_modules(CAIRO "cairo") | pkg_check_modules(CAIRO "cairo") | ||||
if(CAIRO_FOUND) | |||||
if(CAIRO_FOUND AND (NOT HAIKU)) | |||||
add_subdirectory("examples/CairoUI") | add_subdirectory("examples/CairoUI") | ||||
endif() | endif() | ||||
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/FileHandling") | ||||
add_subdirectory("examples/Info") | add_subdirectory("examples/Info") | ||||
add_subdirectory("examples/Latency") | add_subdirectory("examples/Latency") | ||||
@@ -35,7 +35,9 @@ DPF_MAKEFILE_BASE_INCLUDED = true | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Auto-detect target compiler if not defined | # 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) | TARGET_COMPILER = $(shell echo '\#ifdef __clang__\nclang\n\#else\ngcc\n\#endif' | $(CC) -E -P -x c - 2>/dev/null) | ||||
else | else | ||||
TARGET_COMPILER = $(shell echo '#ifdef __clang__\nclang\n#else\ngcc\n#endif' | $(CC) -E -P -x c - 2>/dev/null) | 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 | endif | ||||
ifeq ($(DEBUG),true) | 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 = | LINK_OPTS = | ||||
ifeq ($(WASM),true) | ifeq ($(WASM),true) | ||||
LINK_OPTS += -sASSERTIONS=1 | LINK_OPTS += -sASSERTIONS=1 | ||||
@@ -347,9 +352,11 @@ endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Check for required libraries | # 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 | HAVE_OPENGL = true | ||||
else | else | ||||
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true) | ||||
@@ -402,18 +409,34 @@ endif | |||||
# Set Generic DGL stuff | # Set Generic DGL stuff | ||||
ifeq ($(HAIKU),true) | ifeq ($(HAIKU),true) | ||||
DGL_SYSTEM_LIBS += -lbe | DGL_SYSTEM_LIBS += -lbe | ||||
else ifeq ($(MACOS),true) | 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) | 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) | else ifeq ($(WINDOWS),true) | ||||
DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32 | |||||
# -lole32 | |||||
DGL_SYSTEM_LIBS += -lcomdlg32 | |||||
DGL_SYSTEM_LIBS += -lgdi32 | |||||
# DGL_SYSTEM_LIBS += -lole32 | |||||
else | else | ||||
ifeq ($(HAVE_DBUS),true) | ifeq ($(HAVE_DBUS),true) | ||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | ||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | ||||
endif | endif | ||||
ifeq ($(HAVE_X11),true) | ifeq ($(HAVE_X11),true) | ||||
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 | DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 | ||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs 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_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR | ||||
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) | DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) | ||||
endif | endif | ||||
endif | |||||
endif # HAVE_X11 | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -444,7 +468,7 @@ CAIRO_LIBS = $(shell $(PKG_CONFIG) --libs cairo) | |||||
HAVE_CAIRO_OR_OPENGL = true | HAVE_CAIRO_OR_OPENGL = true | ||||
endif | |||||
endif # HAVE_CAIRO | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set OpenGL specific stuff | # Set OpenGL specific stuff | ||||
@@ -454,8 +478,8 @@ ifeq ($(HAVE_OPENGL),true) | |||||
DGL_FLAGS += -DHAVE_OPENGL | DGL_FLAGS += -DHAVE_OPENGL | ||||
ifeq ($(HAIKU),true) | 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) | else ifeq ($(MACOS),true) | ||||
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations | OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations | ||||
OPENGL_LIBS = -framework OpenGL | OPENGL_LIBS = -framework OpenGL | ||||
@@ -476,12 +500,12 @@ endif | |||||
HAVE_CAIRO_OR_OPENGL = true | HAVE_CAIRO_OR_OPENGL = true | ||||
endif | |||||
endif # HAVE_OPENGL | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set Stub specific stuff | # Set Stub specific stuff | ||||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | |||||
HAVE_STUB = true | HAVE_STUB = true | ||||
else | else | ||||
HAVE_STUB = $(HAVE_X11) | HAVE_STUB = $(HAVE_X11) | ||||
@@ -540,7 +564,7 @@ endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Backwards-compatible HAVE_DGL | # Backwards-compatible HAVE_DGL | ||||
ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true) | |||||
ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else ifeq ($(HAVE_OPENGL),true) | else ifeq ($(HAVE_OPENGL),true) | ||||
HAVE_DGL = $(HAVE_X11) | HAVE_DGL = $(HAVE_X11) | ||||
@@ -633,6 +657,41 @@ else | |||||
SHARED = -shared | SHARED = -shared | ||||
endif | 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 | # Handle the verbosity switch | ||||
@@ -713,6 +772,7 @@ MOD_ENVIRONMENT = \ | |||||
CXX=${1}/host/usr/bin/${2}-g++ \ | CXX=${1}/host/usr/bin/${2}-g++ \ | ||||
LD=${1}/host/usr/bin/${2}-ld \ | LD=${1}/host/usr/bin/${2}-ld \ | ||||
PKG_CONFIG=${1}/host/usr/bin/pkg-config \ | PKG_CONFIG=${1}/host/usr/bin/pkg-config \ | ||||
PKG_CONFIG_PATH="${1}/staging/usr/lib/pkgconfig" \ | |||||
STRIP=${1}/host/usr/bin/${2}-strip \ | STRIP=${1}/host/usr/bin/${2}-strip \ | ||||
CFLAGS="-I${1}/staging/usr/include $(EXTRA_MOD_FLAGS)" \ | CFLAGS="-I${1}/staging/usr/include $(EXTRA_MOD_FLAGS)" \ | ||||
CPPFLAGS= \ | CPPFLAGS= \ | ||||
@@ -738,12 +798,12 @@ modpush: | |||||
ifneq (,$(findstring modduo-,$(MAKECMDGOALS))) | ifneq (,$(findstring modduo-,$(MAKECMDGOALS))) | ||||
$(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 | endif | ||||
ifneq (,$(findstring modduox-,$(MAKECMDGOALS))) | ifneq (,$(findstring modduox-,$(MAKECMDGOALS))) | ||||
$(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 | endif | ||||
ifneq (,$(findstring moddwarf-,$(MAKECMDGOALS))) | ifneq (,$(findstring moddwarf-,$(MAKECMDGOALS))) | ||||
@@ -6,11 +6,18 @@ | |||||
# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | # NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file! | ||||
ifeq ($(DPF_PATH),) | 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 | DPF_PATH=../../dpf | ||||
else | else | ||||
BASE_PATH=../.. | |||||
DPF_PATH=../.. | DPF_PATH=../.. | ||||
endif | endif | ||||
endif | endif | ||||
@@ -20,17 +27,24 @@ include $(DPF_PATH)/Makefile.base.mk | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Basic setup | # 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 | else | ||||
TARGET_DIR = $(DPF_TARGET_DIR) | |||||
BUILD_DIR = $(BASE_PATH)/build$(BUILD_DIR_SUFFIX)/$(NAME) | |||||
endif | endif | ||||
ifeq ($(DPF_BUILD_DIR),) | |||||
BUILD_DIR = ../../build/$(NAME) | |||||
ifneq ($(DPF_TARGET_DIR),) | |||||
TARGET_DIR = $(DPF_TARGET_DIR) | |||||
else | else | ||||
BUILD_DIR = $(DPF_BUILD_DIR) | |||||
TARGET_DIR = $(BASE_PATH)/bin | |||||
endif | endif | ||||
DGL_BUILD_DIR = $(DPF_PATH)/build$(BUILD_DIR_SUFFIX) | |||||
BUILD_C_FLAGS += -I. | BUILD_C_FLAGS += -I. | ||||
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl | ||||
@@ -58,6 +72,10 @@ ifeq ($(HAVE_SDL2),true) | |||||
BASE_FLAGS += -DHAVE_SDL2 | BASE_FLAGS += -DHAVE_SDL2 | ||||
endif | endif | ||||
ifneq ($(MODGUI_CLASS_NAME),) | |||||
BASE_FLAGS += -DDISTRHO_PLUGIN_MODGUI_CLASS_NAME='"$(MODGUI_CLASS_NAME)"' | |||||
endif | |||||
# always needed | # always needed | ||||
ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true) | ||||
ifneq ($(STATIC_BUILD),true) | ifneq ($(STATIC_BUILD),true) | ||||
@@ -80,14 +98,16 @@ endif | |||||
else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) | else ifneq ($(SKIP_RTAUDIO_FALLBACK),true) | ||||
JACK_FLAGS += -DHAVE_GETTIMEOFDAY | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
JACK_LIBS += -framework CoreAudio -framework CoreFoundation -framework CoreMIDI | JACK_LIBS += -framework CoreAudio -framework CoreFoundation -framework CoreMIDI | ||||
else ifeq ($(WINDOWS),true) | else ifeq ($(WINDOWS),true) | ||||
JACK_LIBS += -lole32 -lwinmm | JACK_LIBS += -lole32 -lwinmm | ||||
# DirectSound | # DirectSound | ||||
JACK_LIBS += -ldsound | |||||
# JACK_LIBS += -ldsound | |||||
# WASAPI | # WASAPI | ||||
# JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid | |||||
JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid | |||||
else | else | ||||
ifeq ($(HAVE_PULSEAUDIO),true) | ifeq ($(HAVE_PULSEAUDIO),true) | ||||
JACK_FLAGS += $(PULSEAUDIO_FLAGS) | JACK_FLAGS += $(PULSEAUDIO_FLAGS) | ||||
@@ -147,7 +167,7 @@ ifeq ($(HAVE_CAIRO),true) | |||||
DGL_FLAGS += -DDGL_CAIRO -DHAVE_DGL | DGL_FLAGS += -DDGL_CAIRO -DHAVE_DGL | ||||
DGL_FLAGS += $(CAIRO_FLAGS) | DGL_FLAGS += $(CAIRO_FLAGS) | ||||
DGL_LIBS += $(CAIRO_LIBS) | DGL_LIBS += $(CAIRO_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a | |||||
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-cairo.a | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else | else | ||||
HAVE_DGL = false | HAVE_DGL = false | ||||
@@ -159,7 +179,7 @@ ifeq ($(HAVE_OPENGL),true) | |||||
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL | DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL | ||||
DGL_FLAGS += $(OPENGL_FLAGS) | DGL_FLAGS += $(OPENGL_FLAGS) | ||||
DGL_LIBS += $(OPENGL_LIBS) | DGL_LIBS += $(OPENGL_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a | |||||
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl.a | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else | else | ||||
HAVE_DGL = false | HAVE_DGL = false | ||||
@@ -171,7 +191,7 @@ ifeq ($(HAVE_OPENGL),true) | |||||
DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL | DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL | ||||
DGL_FLAGS += $(OPENGL_FLAGS) | DGL_FLAGS += $(OPENGL_FLAGS) | ||||
DGL_LIBS += $(OPENGL_LIBS) | DGL_LIBS += $(OPENGL_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl3.a | |||||
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl3.a | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else | else | ||||
HAVE_DGL = false | HAVE_DGL = false | ||||
@@ -183,7 +203,7 @@ ifeq ($(HAVE_VULKAN),true) | |||||
DGL_FLAGS += -DDGL_VULKAN -DHAVE_DGL | DGL_FLAGS += -DDGL_VULKAN -DHAVE_DGL | ||||
DGL_FLAGS += $(VULKAN_FLAGS) | DGL_FLAGS += $(VULKAN_FLAGS) | ||||
DGL_LIBS += $(VULKAN_LIBS) | DGL_LIBS += $(VULKAN_LIBS) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a | |||||
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-vulkan.a | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else | else | ||||
HAVE_DGL = false | HAVE_DGL = false | ||||
@@ -197,7 +217,7 @@ endif | |||||
ifeq ($(UI_TYPE),stub) | ifeq ($(UI_TYPE),stub) | ||||
ifeq ($(HAVE_STUB),true) | ifeq ($(HAVE_STUB),true) | ||||
DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a | |||||
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-stub.a | |||||
HAVE_DGL = true | HAVE_DGL = true | ||||
else | else | ||||
HAVE_DGL = false | HAVE_DGL = false | ||||
@@ -209,12 +229,22 @@ DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm | |||||
# TODO split dsp and ui object build flags | # TODO split dsp and ui object build flags | ||||
BASE_FLAGS += $(DGL_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 | # Set VST2 filename, either single binary or inside a bundle | ||||
ifeq ($(MACOS),true) | 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) | else ifeq ($(USE_VST2_BUNDLE),true) | ||||
VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) | VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT) | ||||
else | else | ||||
@@ -225,39 +255,22 @@ endif | |||||
# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html | # Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html | ||||
ifeq ($(LINUX),true) | 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) | 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 | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# Set plugin binary file targets | # Set plugin binary file targets | ||||
ifeq ($(MACOS),true) | |||||
ifeq ($(HAVE_DGL),true) | |||||
ifeq ($(MACOS)$(HAVE_DGL),truetrue) | |||||
MACOS_APP_BUNDLE = true | MACOS_APP_BUNDLE = true | ||||
endif | endif | ||||
ifeq ($(WINDOWS)$(HAVE_DGL),truetrue) | |||||
JACK_LIBS += -Wl,-subsystem,windows | |||||
endif | endif | ||||
ifeq ($(MACOS_APP_BUNDLE),true) | ifeq ($(MACOS_APP_BUNDLE),true) | ||||
@@ -266,6 +279,7 @@ jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist | |||||
else | else | ||||
jack = $(TARGET_DIR)/$(NAME)$(APP_EXT) | jack = $(TARGET_DIR)/$(NAME)$(APP_EXT) | ||||
endif | endif | ||||
ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) | ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT) | ||||
dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) | dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT) | ||||
dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_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 | static = $(TARGET_DIR)/$(NAME).a | ||||
ifeq ($(MACOS),true) | 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 | endif | ||||
ifneq ($(HAVE_DGL),true) | ifneq ($(HAVE_DGL),true) | ||||
@@ -364,37 +373,41 @@ all: | |||||
# Common | # Common | ||||
$(BUILD_DIR)/%.S.o: %.S | $(BUILD_DIR)/%.S.o: %.S | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | @$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | ||||
$(BUILD_DIR)/%.c.o: %.c | $(BUILD_DIR)/%.c.o: %.c | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | ||||
$(BUILD_DIR)/%.cc.o: %.cc | $(BUILD_DIR)/%.cc.o: %.cc | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | ||||
$(BUILD_DIR)/%.cpp.o: %.cpp | $(BUILD_DIR)/%.cpp.o: %.cpp | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | ||||
$(BUILD_DIR)/%.m.o: %.m | $(BUILD_DIR)/%.m.o: %.m | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ | $(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@ | ||||
$(BUILD_DIR)/%.mm.o: %.mm | $(BUILD_DIR)/%.mm.o: %.mm | ||||
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)" | |||||
-@mkdir -p "$(shell dirname $@)" | |||||
@echo "Compiling $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | $(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | ||||
clean: | clean: | ||||
rm -rf $(BUILD_DIR) | 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)-* | rm -rf $(TARGET_DIR)/$(NAME)-* | ||||
rm -rf $(TARGET_DIR)/$(NAME).lv2 | rm -rf $(TARGET_DIR)/$(NAME).lv2 | ||||
@@ -405,44 +418,52 @@ clean: | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
# DGL | # 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 | $(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 | $(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 | $(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 | $(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 | $(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) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoPluginMain.cpp ($*)" | @echo "Compiling DistrhoPluginMain.cpp ($*)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ | $(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) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUIMain.cpp ($*)" | @echo "Compiling DistrhoUIMain.cpp ($*)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@ | $(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) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUI_macOS.mm ($*)" | @echo "Compiling DistrhoUI_macOS.mm ($*)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ | $(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) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoPluginMain.cpp (JACK)" | @echo "Compiling DistrhoPluginMain.cpp (JACK)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@ | $(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) | -@mkdir -p $(BUILD_DIR) | ||||
@echo "Compiling DistrhoUIMain.cpp (DSSI)" | @echo "Compiling DistrhoUIMain.cpp (DSSI)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI $(LIBLO_FLAGS) -c -o $@ | $(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 | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating JACK standalone for $(NAME)" | @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 | # LADSPA | ||||
@@ -469,7 +490,7 @@ ladspa: $(ladspa_dsp) | |||||
$(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o | $(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LADSPA plugin for $(NAME)" | @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 | # DSSI | ||||
@@ -481,12 +502,12 @@ dssi_ui: $(dssi_ui) | |||||
$(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o | $(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating DSSI plugin library for $(NAME)" | @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) | $(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating DSSI UI for $(NAME)" | @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 | # LV2 | ||||
@@ -502,17 +523,111 @@ $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin for $(NAME)" | @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 | $(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin library for $(NAME)" | @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) | $(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating LV2 plugin UI for $(NAME)" | @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 | # VST2 | ||||
@@ -526,7 +641,7 @@ $(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating VST2 plugin for $(NAME)" | @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 | # VST3 | ||||
@@ -540,7 +655,7 @@ $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating VST3 plugin for $(NAME)" | @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 | # CLAP | ||||
@@ -562,7 +677,7 @@ $(clap): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating CLAP plugin for $(NAME)" | @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 | # Shared | ||||
@@ -576,7 +691,7 @@ $(shared): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o | |||||
endif | endif | ||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
@echo "Creating shared library for $(NAME)" | @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 | # Static | ||||
@@ -600,15 +715,7 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I | |||||
-@mkdir -p $(shell dirname $@) | -@mkdir -p $(shell dirname $@) | ||||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | $(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 $@) | -@mkdir -p $(shell dirname $@) | ||||
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | $(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@ | ||||
@@ -75,10 +75,10 @@ include(CMakeParseArguments) | |||||
# `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3`, `clap` | # `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3`, `clap` | ||||
# | # | ||||
# `UI_TYPE` <type> | # `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> | # `FILES_DSP` <file1>...<fileN> | ||||
# list of sources which are part of the DSP | # list of sources which are part of the DSP | ||||
@@ -87,13 +87,19 @@ include(CMakeParseArguments) | |||||
# list of sources which are part of the UI | # list of sources which are part of the UI | ||||
# empty indicates the plugin does not have 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) | function(dpf_add_plugin NAME) | ||||
set(options MONOLITHIC NO_SHARED_RESOURCES) | 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}) | cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||||
if("${_dpf_plugin_UI_TYPE}" STREQUAL "") | if("${_dpf_plugin_UI_TYPE}" STREQUAL "") | ||||
@@ -101,6 +107,7 @@ function(dpf_add_plugin NAME) | |||||
endif() | endif() | ||||
set(_dgl_library) | set(_dgl_library) | ||||
set(_dgl_external OFF) | |||||
if(_dpf_plugin_FILES_UI) | if(_dpf_plugin_FILES_UI) | ||||
if(_dpf_plugin_UI_TYPE STREQUAL "cairo") | if(_dpf_plugin_UI_TYPE STREQUAL "cairo") | ||||
dpf__add_dgl_cairo("${_dpf_plugin_NO_SHARED_RESOURCES}") | 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") | elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl") | ||||
dpf__add_dgl_opengl("${_dpf_plugin_NO_SHARED_RESOURCES}") | dpf__add_dgl_opengl("${_dpf_plugin_NO_SHARED_RESOURCES}") | ||||
set(_dgl_library dgl-opengl) | set(_dgl_library dgl-opengl) | ||||
elseif(_dpf_plugin_UI_TYPE STREQUAL "external") | |||||
set(_dgl_external ON) | |||||
else() | else() | ||||
message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}") | message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}") | ||||
endif() | endif() | ||||
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_COMMON) | ||||
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP) | dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP) | ||||
@@ -123,21 +137,26 @@ function(dpf_add_plugin NAME) | |||||
target_include_directories("${NAME}" PUBLIC | target_include_directories("${NAME}" PUBLIC | ||||
"${DPF_ROOT_DIR}/distrho") | "${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)) | if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | ||||
target_link_libraries("${NAME}" PRIVATE "dl") | target_link_libraries("${NAME}" PRIVATE "dl") | ||||
endif() | endif() | ||||
if(_dgl_library) | |||||
if(_dgl_library AND NOT _dgl_external) | |||||
# make sure that all code will see DGL_* definitions | # make sure that all code will see DGL_* definitions | ||||
target_link_libraries("${NAME}" PUBLIC | target_link_libraries("${NAME}" PUBLIC | ||||
"${_dgl_library}-definitions" | "${_dgl_library}-definitions" | ||||
dgl-system-libs-definitions) | |||||
dgl-system-libs-definitions | |||||
dgl-system-libs) | |||||
endif() | endif() | ||||
dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP}) | dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP}) | ||||
target_link_libraries("${NAME}-dsp" PUBLIC "${NAME}") | 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}) | dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI}) | ||||
target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library}) | target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library}) | ||||
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU)) | ||||
@@ -145,6 +164,14 @@ function(dpf_add_plugin NAME) | |||||
endif() | endif() | ||||
# add the files containing Objective-C classes | # add the files containing Objective-C classes | ||||
dpf__add_plugin_specific_ui_sources("${NAME}-ui") | 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() | else() | ||||
add_library("${NAME}-ui" INTERFACE) | add_library("${NAME}-ui" INTERFACE) | ||||
endif() | endif() | ||||
@@ -152,19 +179,19 @@ function(dpf_add_plugin NAME) | |||||
### | ### | ||||
foreach(_target ${_dpf_plugin_TARGETS}) | foreach(_target ${_dpf_plugin_TARGETS}) | ||||
if(_target STREQUAL "jack") | if(_target STREQUAL "jack") | ||||
dpf__build_jack("${NAME}" "${_dgl_library}") | |||||
dpf__build_jack("${NAME}" "${_dgl_has_ui}") | |||||
elseif(_target STREQUAL "ladspa") | elseif(_target STREQUAL "ladspa") | ||||
dpf__build_ladspa("${NAME}") | dpf__build_ladspa("${NAME}") | ||||
elseif(_target STREQUAL "dssi") | elseif(_target STREQUAL "dssi") | ||||
dpf__build_dssi("${NAME}" "${_dgl_library}") | |||||
dpf__build_dssi("${NAME}" "${_dgl_has_ui}") | |||||
elseif(_target STREQUAL "lv2") | 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") | elseif(_target STREQUAL "vst2") | ||||
dpf__build_vst2("${NAME}" "${_dgl_library}") | |||||
dpf__build_vst2("${NAME}" "${_dgl_has_ui}") | |||||
elseif(_target STREQUAL "vst3") | elseif(_target STREQUAL "vst3") | ||||
dpf__build_vst3("${NAME}" "${_dgl_library}") | |||||
dpf__build_vst3("${NAME}" "${_dgl_has_ui}") | |||||
elseif(_target STREQUAL "clap") | elseif(_target STREQUAL "clap") | ||||
dpf__build_clap("${NAME}" "${_dgl_library}") | |||||
dpf__build_clap("${NAME}" "${_dgl_has_ui}") | |||||
else() | else() | ||||
message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") | message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}") | ||||
endif() | endif() | ||||
@@ -184,26 +211,27 @@ endfunction() | |||||
# | # | ||||
# Add build rules for a JACK/Standalone program. | # 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__create_dummy_source_list(_no_srcs) | ||||
dpf__add_executable("${NAME}-jack" ${_no_srcs}) | dpf__add_executable("${NAME}-jack" ${_no_srcs}) | ||||
dpf__add_plugin_main("${NAME}-jack" "jack") | 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") | target_link_libraries("${NAME}-jack" PRIVATE "${NAME}-dsp" "${NAME}-ui") | ||||
set_target_properties("${NAME}-jack" PROPERTIES | set_target_properties("${NAME}-jack" PROPERTIES | ||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>" | ||||
OUTPUT_NAME "${NAME}") | OUTPUT_NAME "${NAME}") | ||||
target_compile_definitions("${NAME}" PUBLIC "HAVE_JACK") | target_compile_definitions("${NAME}" PUBLIC "HAVE_JACK") | ||||
target_compile_definitions("${NAME}-jack" PRIVATE "HAVE_GETTIMEOFDAY") | |||||
find_package(PkgConfig) | find_package(PkgConfig) | ||||
pkg_check_modules(SDL2 "sdl2") | pkg_check_modules(SDL2 "sdl2") | ||||
if(SDL2_FOUND) | if(SDL2_FOUND) | ||||
target_compile_definitions("${NAME}" PUBLIC "HAVE_SDL2") | 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() | endif() | ||||
if(APPLE OR WIN32) | if(APPLE OR WIN32) | ||||
@@ -216,13 +244,13 @@ function(dpf__build_jack NAME DGL_LIBRARY) | |||||
target_compile_definitions("${NAME}" PUBLIC "HAVE_ALSA") | target_compile_definitions("${NAME}" PUBLIC "HAVE_ALSA") | ||||
target_include_directories("${NAME}-jack" PRIVATE ${ALSA_INCLUDE_DIRS}) | target_include_directories("${NAME}-jack" PRIVATE ${ALSA_INCLUDE_DIRS}) | ||||
target_link_libraries("${NAME}-jack" PRIVATE ${ALSA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) | 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() | endif() | ||||
if(PULSEAUDIO_FOUND) | if(PULSEAUDIO_FOUND) | ||||
target_compile_definitions("${NAME}" PUBLIC "HAVE_PULSEAUDIO") | target_compile_definitions("${NAME}" PUBLIC "HAVE_PULSEAUDIO") | ||||
target_include_directories("${NAME}-jack" PRIVATE ${PULSEAUDIO_INCLUDE_DIRS}) | target_include_directories("${NAME}-jack" PRIVATE ${PULSEAUDIO_INCLUDE_DIRS}) | ||||
target_link_libraries("${NAME}-jack" PRIVATE ${PULSEAUDIO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) | 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() | endif() | ||||
if(ALSA_FOUND OR PULSEAUDIO_FOUND) | if(ALSA_FOUND OR PULSEAUDIO_FOUND) | ||||
target_compile_definitions("${NAME}" PUBLIC "HAVE_RTAUDIO") | target_compile_definitions("${NAME}" PUBLIC "HAVE_RTAUDIO") | ||||
@@ -239,7 +267,10 @@ function(dpf__build_jack NAME DGL_LIBRARY) | |||||
"${APPLE_COREFOUNDATION_FRAMEWORK}" | "${APPLE_COREFOUNDATION_FRAMEWORK}" | ||||
"${APPLE_COREMIDI_FRAMEWORK}") | "${APPLE_COREMIDI_FRAMEWORK}") | ||||
elseif(WIN32) | 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() | endif() | ||||
endfunction() | endfunction() | ||||
@@ -267,7 +298,7 @@ endfunction() | |||||
# | # | ||||
# Add build rules for a DSSI plugin. | # Add build rules for a DSSI plugin. | ||||
# | # | ||||
function(dpf__build_dssi NAME DGL_LIBRARY) | |||||
function(dpf__build_dssi NAME HAS_UI) | |||||
find_package(PkgConfig) | find_package(PkgConfig) | ||||
pkg_check_modules(LIBLO "liblo") | pkg_check_modules(LIBLO "liblo") | ||||
if(NOT LIBLO_FOUND) | if(NOT LIBLO_FOUND) | ||||
@@ -288,9 +319,9 @@ function(dpf__build_dssi NAME DGL_LIBRARY) | |||||
OUTPUT_NAME "${NAME}-dssi" | OUTPUT_NAME "${NAME}-dssi" | ||||
PREFIX "") | PREFIX "") | ||||
if(DGL_LIBRARY) | |||||
if(HAS_UI) | |||||
dpf__add_executable("${NAME}-dssi-ui" ${_no_srcs}) | 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") | target_link_libraries("${NAME}-dssi-ui" PRIVATE "${NAME}-ui") | ||||
set_target_properties("${NAME}-dssi-ui" PROPERTIES | set_target_properties("${NAME}-dssi-ui" PROPERTIES | ||||
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}-dssi/$<0:>" | 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_compile_definitions("${NAME}" PUBLIC "HAVE_LIBLO") | ||||
target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS}) | target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS}) | ||||
target_link_libraries("${NAME}-dssi-ui" PRIVATE ${LIBLO_LIBRARIES}) | 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() | endif() | ||||
endfunction() | endfunction() | ||||
@@ -308,12 +339,12 @@ endfunction() | |||||
# | # | ||||
# Add build rules for an LV2 plugin. | # 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__create_dummy_source_list(_no_srcs) | ||||
dpf__add_module("${NAME}-lv2" ${_no_srcs}) | dpf__add_module("${NAME}-lv2" ${_no_srcs}) | ||||
dpf__add_plugin_main("${NAME}-lv2" "lv2") | 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") | dpf__set_module_export_list("${NAME}-lv2" "lv2") | ||||
else() | else() | ||||
dpf__set_module_export_list("${NAME}-lv2" "lv2-dsp") | 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" | OUTPUT_NAME "${NAME}_dsp" | ||||
PREFIX "") | PREFIX "") | ||||
if(DGL_LIBRARY) | |||||
if(HAS_UI) | |||||
if(MONOLITHIC) | 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") | target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-ui") | ||||
set_target_properties("${NAME}-lv2" PROPERTIES | set_target_properties("${NAME}-lv2" PROPERTIES | ||||
OUTPUT_NAME "${NAME}") | OUTPUT_NAME "${NAME}") | ||||
else() | else() | ||||
dpf__add_module("${NAME}-lv2-ui" ${_no_srcs}) | 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") | dpf__set_module_export_list("${NAME}-lv2-ui" "lv2-ui") | ||||
target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui") | target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui") | ||||
set_target_properties("${NAME}-lv2-ui" PROPERTIES | set_target_properties("${NAME}-lv2-ui" PROPERTIES | ||||
@@ -361,12 +392,12 @@ endfunction() | |||||
# | # | ||||
# Add build rules for a VST2 plugin. | # 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__create_dummy_source_list(_no_srcs) | ||||
dpf__add_module("${NAME}-vst2" ${_no_srcs}) | dpf__add_module("${NAME}-vst2" ${_no_srcs}) | ||||
dpf__add_plugin_main("${NAME}-vst2" "vst2") | 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") | dpf__set_module_export_list("${NAME}-vst2" "vst2") | ||||
target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui") | target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui") | ||||
set_target_properties("${NAME}-vst2" PROPERTIES | set_target_properties("${NAME}-vst2" PROPERTIES | ||||
@@ -439,14 +470,14 @@ endfunction() | |||||
# | # | ||||
# Add build rules for a VST3 plugin. | # 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__determine_vst3_package_architecture(vst3_arch) | ||||
dpf__create_dummy_source_list(_no_srcs) | dpf__create_dummy_source_list(_no_srcs) | ||||
dpf__add_module("${NAME}-vst3" ${_no_srcs}) | dpf__add_module("${NAME}-vst3" ${_no_srcs}) | ||||
dpf__add_plugin_main("${NAME}-vst3" "vst3") | 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") | dpf__set_module_export_list("${NAME}-vst3" "vst3") | ||||
target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui") | target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui") | ||||
set_target_properties("${NAME}-vst3" PROPERTIES | set_target_properties("${NAME}-vst3" PROPERTIES | ||||
@@ -481,12 +512,12 @@ endfunction() | |||||
# | # | ||||
# Add build rules for a VST2 plugin. | # 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__create_dummy_source_list(_no_srcs) | ||||
dpf__add_module("${NAME}-clap" ${_no_srcs}) | dpf__add_module("${NAME}-clap" ${_no_srcs}) | ||||
dpf__add_plugin_main("${NAME}-clap" "clap") | 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") | dpf__set_module_export_list("${NAME}-clap" "clap") | ||||
target_link_libraries("${NAME}-clap" PRIVATE "${NAME}-dsp" "${NAME}-ui") | target_link_libraries("${NAME}-clap" PRIVATE "${NAME}-dsp" "${NAME}-ui") | ||||
set_target_properties("${NAME}-clap" PROPERTIES | 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/Geometry.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.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/SubWidget.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | "${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.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/Geometry.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | "${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.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/SubWidget.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | "${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp" | ||||
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" | "${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp" | ||||
@@ -661,34 +694,42 @@ function(dpf__add_dgl_system_libs) | |||||
endif() | endif() | ||||
add_library(dgl-system-libs INTERFACE) | add_library(dgl-system-libs INTERFACE) | ||||
add_library(dgl-system-libs-definitions 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_COCOA_FRAMEWORK "Cocoa") | ||||
find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo") | find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${APPLE_COCOA_FRAMEWORK}" "${APPLE_COREVIDEO_FRAMEWORK}") | 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() | 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) | 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_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") | target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}") | ||||
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11") | |||||
if(X11_Xcursor_FOUND) | 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_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}") | |||||
endif() | endif() | ||||
if(X11_Xext_FOUND) | 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_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}") | |||||
endif() | endif() | ||||
if(X11_Xrandr_FOUND) | 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_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}") | |||||
endif() | endif() | ||||
if(X11_XSync_FOUND) | 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_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC") | ||||
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}") | |||||
endif() | endif() | ||||
endif() | endif() | ||||
@@ -94,6 +94,11 @@ struct Color { | |||||
*/ | */ | ||||
Color plus(float value) const noexcept; | 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. | Create a color specified by hue, saturation and lightness. | ||||
Values must in [0..1] range. | Values must in [0..1] range. | ||||
@@ -156,7 +156,7 @@ public: | |||||
bool scrollEvent(const Widget::ScrollEvent& ev); | bool scrollEvent(const Widget::ScrollEvent& ev); | ||||
protected: | protected: | ||||
State getState() const noexcept; | |||||
State getState() const noexcept; | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
@@ -26,58 +26,66 @@ endif | |||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
ifeq ($(MODGUI_BUILD),true) | |||||
BUILD_DIR_SUFFIX = -modgui | |||||
endif | |||||
BUILD_DIR = ../build$(BUILD_DIR_SUFFIX) | |||||
# --------------------------------------------------------------------------------------------------------------------- | |||||
OBJS_common = \ | 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) \ | OBJS_cairo = $(OBJS_common) \ | ||||
../build/dgl/Cairo.cpp.cairo.o | |||||
$(BUILD_DIR)/dgl/Cairo.cpp.cairo.o | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OBJS_cairo += ../build/dgl/pugl.mm.cairo.o | |||||
OBJS_cairo += $(BUILD_DIR)/dgl/pugl.mm.cairo.o | |||||
else | else | ||||
OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o | |||||
OBJS_cairo += $(BUILD_DIR)/dgl/pugl.cpp.cairo.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
OBJS_opengl = $(OBJS_common) \ | 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) | ifeq ($(MACOS),true) | ||||
OBJS_opengl += ../build/dgl/pugl.mm.opengl.o | |||||
OBJS_opengl += $(BUILD_DIR)/dgl/pugl.mm.opengl.o | |||||
else | else | ||||
OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o | |||||
OBJS_opengl += $(BUILD_DIR)/dgl/pugl.cpp.opengl.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
OBJS_opengl3 = $(OBJS_common) \ | 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) | ifeq ($(MACOS),true) | ||||
OBJS_opengl3 += ../build/dgl/pugl.mm.opengl3.o | |||||
OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.mm.opengl3.o | |||||
else | else | ||||
OBJS_opengl3 += ../build/dgl/pugl.cpp.opengl3.o | |||||
OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.cpp.opengl3.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
@@ -85,170 +93,170 @@ endif | |||||
OBJS_stub = $(OBJS_common) | OBJS_stub = $(OBJS_common) | ||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OBJS_stub += ../build/dgl/pugl.mm.o | |||||
OBJS_stub += $(BUILD_DIR)/dgl/pugl.mm.o | |||||
else | else | ||||
OBJS_stub += ../build/dgl/pugl.cpp.o | |||||
OBJS_stub += $(BUILD_DIR)/dgl/pugl.cpp.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
OBJS_vulkan = $(OBJS_common) \ | OBJS_vulkan = $(OBJS_common) \ | ||||
../build/dgl/Vulkan.cpp.vulkan.o | |||||
$(BUILD_DIR)/dgl/Vulkan.cpp.vulkan.o | |||||
ifeq ($(MACOS),true) | ifeq ($(MACOS),true) | ||||
OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o | |||||
OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.mm.vulkan.o | |||||
else | else | ||||
OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o | |||||
OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.cpp.vulkan.o | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
ifeq ($(HAVE_CAIRO),true) | ifeq ($(HAVE_CAIRO),true) | ||||
TARGETS += ../build/libdgl-cairo.a | |||||
TARGETS += $(BUILD_DIR)/libdgl-cairo.a | |||||
endif | endif | ||||
ifeq ($(HAVE_OPENGL),true) | ifeq ($(HAVE_OPENGL),true) | ||||
TARGETS += ../build/libdgl-opengl.a | |||||
TARGETS += $(BUILD_DIR)/libdgl-opengl.a | |||||
# Compat name, to be removed soon | # Compat name, to be removed soon | ||||
TARGETS += ../build/libdgl.a | |||||
TARGETS += $(BUILD_DIR)/libdgl.a | |||||
endif | endif | ||||
ifeq ($(HAVE_STUB),true) | ifeq ($(HAVE_STUB),true) | ||||
TARGETS += ../build/libdgl-stub.a | |||||
TARGETS += $(BUILD_DIR)/libdgl-stub.a | |||||
endif | endif | ||||
ifeq ($(HAVE_VULKAN),true) | ifeq ($(HAVE_VULKAN),true) | ||||
TARGETS += ../build/libdgl-vulkan.a | |||||
TARGETS += $(BUILD_DIR)/libdgl-vulkan.a | |||||
endif | endif | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
all: $(TARGETS) | 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" | @echo "Creating libdgl-cairo.a" | ||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(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" | @echo "Creating libdgl-opengl.a" | ||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(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" | @echo "Creating libdgl-opengl3.a" | ||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(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" | @echo "Creating libdgl-stub.a" | ||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(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" | @echo "Creating libdgl-vulkan.a" | ||||
$(SILENT)rm -f $@ | $(SILENT)rm -f $@ | ||||
$(SILENT)$(AR) crs $@ $^ | $(SILENT)$(AR) crs $@ $^ | ||||
# Compat name, to be removed soon | # 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" | @echo "Symlinking libdgl.a" | ||||
$(SILENT)ln -sf $< $@ | $(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 $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | $(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 $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | $(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 $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@ | $(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 $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -o $@ | $(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 $<" | @echo "Compiling $<" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@ | $(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)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@ | $(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)" | @echo "Compiling $< (Cairo variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@ | $(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)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@ | $(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)" | @echo "Compiling $< (OpenGL variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@ | $(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)" | @echo "Compiling $< (OpenGL3 variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@ | $(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)" | @echo "Compiling $< (OpenGL3 variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@ | $(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)" | @echo "Compiling $< (Vulkan variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@ | $(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)" | @echo "Compiling $< (Vulkan variant)" | ||||
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ | $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@ | ||||
# --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
clean: | clean: | ||||
rm -rf ../build/dgl ../build/libdgl*.* | |||||
rm -rf $(BUILD_DIR)/dgl $(BUILD_DIR)/libdgl*.* | |||||
debug: | debug: | ||||
$(MAKE) DEBUG=true | $(MAKE) DEBUG=true | ||||
@@ -67,9 +67,11 @@ Application::PrivateData::PrivateData(const bool standalone) | |||||
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,); | ||||
puglSetWorldHandle(world, this); | puglSetWorldHandle(world, this); | ||||
#ifndef __EMSCRIPTEN__ | |||||
#ifdef __EMSCRIPTEN__ | |||||
puglSetClassName(world, "canvas"); | |||||
#else | |||||
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE)); | ||||
#endif | |||||
#endif | |||||
} | } | ||||
Application::PrivateData::~PrivateData() | Application::PrivateData::~PrivateData() | ||||
@@ -163,6 +163,15 @@ Color Color::plus(const float value) const noexcept | |||||
return color; | 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) | Color Color::fromHSL(float hue, float saturation, float lightness, float alpha) | ||||
{ | { | ||||
float m1, m2; | float m1, m2; | ||||
@@ -441,7 +441,7 @@ struct KnobEventHandler::PrivateData { | |||||
} | } | ||||
if (d_isZero(movDiff)) | if (d_isZero(movDiff)) | ||||
return false; | |||||
return true; | |||||
const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; | const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel; | ||||
valueTmp += (maximum - minimum) / divisor * movDiff; | 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 "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 | PuglStatus | ||||
puglGrabFocus(PuglView* view) | puglGrabFocus(PuglView* view) | ||||
{ | { | ||||
@@ -79,3 +399,4 @@ void setVisible(const bool yesNo) | |||||
bView->Hide(); | 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 "pugl/pugl.h" | ||||
#include <Application.h> | #include <Application.h> | ||||
#include <Window.h> | #include <Window.h> | ||||
// using? interface/ | |||||
struct PuglWorldInternalsImpl { | struct PuglWorldInternalsImpl { | ||||
BApplication* app; | |||||
BApplication* app; | |||||
}; | }; | ||||
struct PuglInternalsImpl { | 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 <cstring> | ||||
#include <ctime> | #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> | # import <Cocoa/Cocoa.h> | ||||
# include <dlfcn.h> | # include <dlfcn.h> | ||||
# include <mach/mach_time.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 | # ifndef DISTRHO_MACOS_NAMESPACE_MACRO | ||||
# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE | # 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) | # define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE) | ||||
@@ -146,10 +159,10 @@ START_NAMESPACE_DGL | |||||
# endif | # endif | ||||
# pragma clang diagnostic pop | # pragma clang diagnostic pop | ||||
#elif defined(DISTRHO_OS_WASM) | #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 | # ifdef DGL_OPENGL | ||||
# include "pugl-upstream/src/wasm_gl.c" | |||||
# include "pugl-extra/wasm_gl.c" | |||||
# endif | # endif | ||||
#elif defined(DISTRHO_OS_WINDOWS) | #elif defined(DISTRHO_OS_WINDOWS) | ||||
# include "pugl-upstream/src/win.c" | # include "pugl-upstream/src/win.c" | ||||
@@ -237,7 +250,8 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view) | |||||
void puglRaiseWindow(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 | if (NSWindow* const window = view->impl->window ? view->impl->window | ||||
: [view->impl->wrapperView window]) | : [view->impl->wrapperView window]) | ||||
[window orderFrontRegardless]; | [window orderFrontRegardless]; | ||||
@@ -257,7 +271,10 @@ void puglRaiseWindow(PuglView* const view) | |||||
double puglGetScaleFactorFromParent(const PuglView* const view) | double puglGetScaleFactorFromParent(const PuglView* const view) | ||||
{ | { | ||||
const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0; | 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 | // some of these can return 0 as backingScaleFactor, pick the most relevant valid one | ||||
const NSWindow* possibleWindows[] = { | const NSWindow* possibleWindows[] = { | ||||
parent != 0 ? [(NSView*)parent window] : nullptr, | 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; | 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) | if (view->impl->window) | ||||
{ | { | ||||
PuglStatus status; | PuglStatus status; | ||||
@@ -328,7 +346,8 @@ void puglSetResizable(PuglView* const view, const bool resizable) | |||||
{ | { | ||||
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); | 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) | if (PuglWindow* const window = view->impl->window) | ||||
{ | { | ||||
const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask) | 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].width = view->frame.width = static_cast<PuglSpan>(width); | ||||
view->sizeHints[PUGL_DEFAULT_SIZE].height = view->frame.height = static_cast<PuglSpan>(height); | 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 | // mostly matches upstream pugl, simplified | ||||
PuglInternals* const impl = view->impl; | 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 | // macOS specific, add another view's window as child | ||||
@@ -73,7 +73,11 @@ void puglOnDisplayPrepare(PuglView* view); | |||||
// DGL specific, build-specific fallback resize | // DGL specific, build-specific fallback resize | ||||
void puglFallbackOnResize(PuglView* view); | 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 | // macOS specific, add another view's window as child | ||||
PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child); | PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child); | ||||
@@ -17,944 +17,12 @@ | |||||
#ifndef DISTRHO_PLUGIN_HPP_INCLUDED | #ifndef DISTRHO_PLUGIN_HPP_INCLUDED | ||||
#define DISTRHO_PLUGIN_HPP_INCLUDED | #define DISTRHO_PLUGIN_HPP_INCLUDED | ||||
#include "extra/String.hpp" | |||||
#include "DistrhoDetails.hpp" | |||||
#include "extra/LeakDetector.hpp" | #include "extra/LeakDetector.hpp" | ||||
#include "src/DistrhoPluginChecks.h" | #include "src/DistrhoPluginChecks.h" | ||||
START_NAMESPACE_DISTRHO | 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 */ | * DPF Plugin */ | ||||
@@ -984,7 +52,7 @@ struct TimePosition { | |||||
When enabled you need to implement initProgramName() and loadProgram(). | When enabled you need to implement initProgramName() and loadProgram(). | ||||
DISTRHO_PLUGIN_WANT_STATE activates internal state features.@n | 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 | The process function run() changes wherever DISTRHO_PLUGIN_WANT_MIDI_INPUT is enabled or not.@n | ||||
When enabled it provides midi input events. | When enabled it provides midi input events. | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,6 +17,7 @@ | |||||
#ifndef DISTRHO_UI_HPP_INCLUDED | #ifndef DISTRHO_UI_HPP_INCLUDED | ||||
#define DISTRHO_UI_HPP_INCLUDED | #define DISTRHO_UI_HPP_INCLUDED | ||||
#include "DistrhoDetails.hpp" | |||||
#include "extra/LeakDetector.hpp" | #include "extra/LeakDetector.hpp" | ||||
#include "src/DistrhoPluginChecks.h" | #include "src/DistrhoPluginChecks.h" | ||||
@@ -16,6 +16,10 @@ | |||||
#include "src/DistrhoUI.cpp" | #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) | #if defined(DISTRHO_PLUGIN_TARGET_CARLA) | ||||
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | # define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1 | ||||
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP) | #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 | // A few utils declared in DistrhoUI.cpp but defined here because they use Obj-C | ||||
#include "DistrhoDetails.hpp" | |||||
#include "src/DistrhoPluginChecks.h" | #include "src/DistrhoPluginChecks.h" | ||||
#include "src/DistrhoDefines.h" | #include "src/DistrhoDefines.h" | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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_VALUE(MACRO) #MACRO | ||||
#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) | #define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO) | ||||
/* ------------------------------------------------------------------------------------------------------------ | |||||
/* -------------------------------------------------------------------------------------------------------------------- | |||||
* misc functions */ | * misc functions */ | ||||
/** | /** | ||||
@@ -94,7 +94,7 @@ void d_pass() noexcept {} | |||||
/** @} */ | /** @} */ | ||||
/* ------------------------------------------------------------------------------------------------------------ | |||||
/* -------------------------------------------------------------------------------------------------------------------- | |||||
* string print functions */ | * string print functions */ | ||||
/** | /** | ||||
@@ -240,7 +240,7 @@ void d_safe_exception(const char* const exception, const char* const file, const | |||||
/** @} */ | /** @} */ | ||||
/* ------------------------------------------------------------------------------------------------------------ | |||||
/* -------------------------------------------------------------------------------------------------------------------- | |||||
* math functions */ | * math functions */ | ||||
/** | /** | ||||
@@ -254,7 +254,7 @@ void d_safe_exception(const char* const exception, const char* const file, const | |||||
Returns true if they match. | Returns true if they match. | ||||
*/ | */ | ||||
template<typename T> | template<typename T> | ||||
static inline | |||||
static inline constexpr | |||||
bool d_isEqual(const T& v1, const T& v2) | bool d_isEqual(const T& v1, const T& v2) | ||||
{ | { | ||||
return std::abs(v1-v2) < std::numeric_limits<T>::epsilon(); | 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. | Returns true if they don't match. | ||||
*/ | */ | ||||
template<typename T> | template<typename T> | ||||
static inline | |||||
static inline constexpr | |||||
bool d_isNotEqual(const T& v1, const T& v2) | bool d_isNotEqual(const T& v1, const T& v2) | ||||
{ | { | ||||
return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon(); | 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. | Safely check if a floating point number is zero. | ||||
*/ | */ | ||||
template<typename T> | template<typename T> | ||||
static inline | |||||
static inline constexpr | |||||
bool d_isZero(const T& value) | bool d_isZero(const T& value) | ||||
{ | { | ||||
return std::abs(value) < std::numeric_limits<T>::epsilon(); | 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. | Safely check if a floating point number is not zero. | ||||
*/ | */ | ||||
template<typename T> | template<typename T> | ||||
static inline | |||||
static inline constexpr | |||||
bool d_isNotZero(const T& value) | bool d_isNotZero(const T& value) | ||||
{ | { | ||||
return std::abs(value) >= std::numeric_limits<T>::epsilon(); | 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 | #ifndef DONT_SET_USING_DISTRHO_NAMESPACE | ||||
// If your code uses a lot of DISTRHO classes, then this will obviously save you | // 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; | using namespace DISTRHO_NAMESPACE; | ||||
#endif | #endif | ||||
// ----------------------------------------------------------------------- | |||||
#endif // DISTRHO_UTILS_HPP_INCLUDED | #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 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 scaleFactor: Scale factor to use (only used on X11) | ||||
@p options: Extra options, optional | @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, | FileBrowserHandle fileBrowserCreate(bool isEmbed, | ||||
uintptr_t windowId, | uintptr_t windowId, | ||||
@@ -102,13 +102,13 @@ FileBrowserHandle fileBrowserCreate(bool isEmbed, | |||||
/** | /** | ||||
Idle the file browser dialog handle.@n | Idle the file browser dialog handle.@n | ||||
Returns true if dialog was closed (with or without a file selection), | 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). | You can then call fileBrowserGetPath to know the selected file (or null if cancelled). | ||||
*/ | */ | ||||
bool fileBrowserIdle(const FileBrowserHandle handle); | 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); | 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) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -22,6 +22,10 @@ | |||||
#include <algorithm> | #include <algorithm> | ||||
#if __cplusplus >= 201703L | |||||
# include <string_view> | |||||
#endif | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -87,6 +91,16 @@ public: | |||||
_dup(strBuf); | _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. | * 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) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -211,7 +211,7 @@ public: | |||||
#endif | #endif | ||||
const bool isFloating) | const bool isFloating) | ||||
: fPlugin(plugin), | : fPlugin(plugin), | ||||
fPluinEventQueue(eventQueue), | |||||
fPluginEventQueue(eventQueue), | |||||
fEventQueue(eventQueue->fEventQueue), | fEventQueue(eventQueue->fEventQueue), | ||||
fCachedParameters(eventQueue->fCachedParameters), | fCachedParameters(eventQueue->fCachedParameters), | ||||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | #if DISTRHO_PLUGIN_WANT_PROGRAMS | ||||
@@ -382,7 +382,7 @@ public: | |||||
*width = minimumWidth; | *width = minimumWidth; | ||||
if (minimumHeight > *height) | if (minimumHeight > *height) | ||||
*height = minimumHeight; | *height = minimumHeight; | ||||
return true; | return true; | ||||
} | } | ||||
@@ -534,7 +534,7 @@ public: | |||||
private: | private: | ||||
// Plugin and UI | // Plugin and UI | ||||
PluginExporter& fPlugin; | PluginExporter& fPlugin; | ||||
ClapEventQueue* const fPluinEventQueue; | |||||
ClapEventQueue* const fPluginEventQueue; | |||||
ClapEventQueue::Queue& fEventQueue; | ClapEventQueue::Queue& fEventQueue; | ||||
ClapEventQueue::CachedParameters& fCachedParameters; | ClapEventQueue::CachedParameters& fCachedParameters; | ||||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | #if DISTRHO_PLUGIN_WANT_PROGRAMS | ||||
@@ -578,7 +578,7 @@ private: | |||||
setStateCallback, | setStateCallback, | ||||
sendNoteCallback, | sendNoteCallback, | ||||
setSizeCallback, | setSizeCallback, | ||||
fileRequestCallback, | |||||
nullptr, // TODO fileRequestCallback, | |||||
d_nextBundlePath, | d_nextBundlePath, | ||||
fPlugin.getInstancePointer(), | fPlugin.getInstancePointer(), | ||||
fScaleFactor); | fScaleFactor); | ||||
@@ -682,7 +682,7 @@ private: | |||||
#if DISTRHO_PLUGIN_WANT_STATE | #if DISTRHO_PLUGIN_WANT_STATE | ||||
void setState(const char* const key, const char* const value) | 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) | static void setStateCallback(void* const ptr, const char* key, const char* value) | ||||
@@ -708,6 +708,7 @@ private: | |||||
} | } | ||||
#endif | #endif | ||||
/* TODO | |||||
bool fileRequest(const char*) | bool fileRequest(const char*) | ||||
{ | { | ||||
return true; | return true; | ||||
@@ -717,6 +718,7 @@ private: | |||||
{ | { | ||||
return static_cast<ClapUI*>(ptr)->fileRequest(key); | return static_cast<ClapUI*>(ptr)->fileRequest(key); | ||||
} | } | ||||
*/ | |||||
}; | }; | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
@@ -977,6 +979,7 @@ public: | |||||
case CLAP_EVENT_PARAM_GESTURE_BEGIN: | case CLAP_EVENT_PARAM_GESTURE_BEGIN: | ||||
case CLAP_EVENT_PARAM_GESTURE_END: | case CLAP_EVENT_PARAM_GESTURE_END: | ||||
case CLAP_EVENT_TRANSPORT: | case CLAP_EVENT_TRANSPORT: | ||||
break; | |||||
case CLAP_EVENT_MIDI: | case CLAP_EVENT_MIDI: | ||||
DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t), | DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t), | ||||
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), | DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value), | ||||
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))); | 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 | // 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) | 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 | // 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]; | *api = kSupportedAPIs[0]; | ||||
*is_floating = false; | *is_floating = false; | ||||
return true; | 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) | 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; | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
instance->destroyUI(); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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 | #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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | ClapUI* const gui = instance->getUI(); | ||||
@@ -2137,7 +2140,7 @@ static bool clap_gui_can_resize(const clap_plugin_t* const plugin) | |||||
return gui->canResize(); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | 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); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | ClapUI* const gui = instance->getUI(); | ||||
@@ -2193,7 +2196,7 @@ static bool clap_gui_show(const clap_plugin_t* const plugin) | |||||
return gui->show(); | 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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | ClapUI* const gui = instance->getUI(); | ||||
@@ -2223,7 +2226,7 @@ static const clap_plugin_gui_t clap_plugin_gui = { | |||||
// plugin timer | // plugin timer | ||||
#if DPF_CLAP_USING_HOST_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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
ClapUI* const gui = instance->getUI(); | ClapUI* const gui = instance->getUI(); | ||||
@@ -2242,14 +2245,14 @@ static const clap_plugin_timer_support_t clap_timer = { | |||||
// plugin audio ports | // plugin audio ports | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | #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); | PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data); | ||||
return is_input ? instance->getAudioPortCount<true>() | return is_input ? instance->getAudioPortCount<true>() | ||||
: instance->getAudioPortCount<false>(); | : 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 uint32_t index, | ||||
const bool is_input, | const bool is_input, | ||||
clap_audio_port_info_t* const info) | 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 | // plugin note ports | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0 | #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; | 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) | if (is_input) | ||||
{ | { | ||||
@@ -2370,7 +2373,6 @@ static const clap_plugin_latency_t clap_plugin_latency = { | |||||
}; | }; | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_WANT_STATE | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
// plugin state | // plugin state | ||||
@@ -2390,7 +2392,6 @@ static const clap_plugin_state_t clap_plugin_state = { | |||||
clap_plugin_state_save, | clap_plugin_state_save, | ||||
clap_plugin_state_load | clap_plugin_state_load | ||||
}; | }; | ||||
#endif | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
// plugin | // 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) | if (std::strcmp(id, CLAP_EXT_PARAMS) == 0) | ||||
return &clap_plugin_params; | 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 DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0 | ||||
if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) | if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) | ||||
return &clap_plugin_audio_ports; | 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) | if (std::strcmp(id, CLAP_EXT_LATENCY) == 0) | ||||
return &clap_plugin_latency; | return &clap_plugin_latency; | ||||
#endif | #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 DISTRHO_PLUGIN_HAS_UI | ||||
if (std::strcmp(id, CLAP_EXT_GUI) == 0) | if (std::strcmp(id, CLAP_EXT_GUI) == 0) | ||||
return &clap_plugin_gui; | return &clap_plugin_gui; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -17,6 +17,10 @@ | |||||
#ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED | #ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED | ||||
#define DISTRHO_PLUGIN_CHECKS_H_INCLUDED | #define DISTRHO_PLUGIN_CHECKS_H_INCLUDED | ||||
#ifndef DISTRHO_DETAILS_HPP_INCLUDED | |||||
# error wrong include order | |||||
#endif | |||||
#include "DistrhoPluginInfo.h" | #include "DistrhoPluginInfo.h" | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -152,23 +152,25 @@ public: | |||||
fClient(client) | fClient(client) | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
char strBuf[0xff+1]; | |||||
strBuf[0xff] = '\0'; | |||||
# if DISTRHO_PLUGIN_NUM_INPUTS > 0 | # if DISTRHO_PLUGIN_NUM_INPUTS > 0 | ||||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i) | ||||
{ | { | ||||
const AudioPort& port(fPlugin.getAudioPort(true, 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); | setAudioPortMetadata(port, fPortAudioIns[i], i); | ||||
} | } | ||||
# endif | # endif | ||||
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | # if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | 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)); | 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); | setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i); | ||||
} | } | ||||
# endif | # endif | ||||
@@ -623,7 +625,8 @@ private: | |||||
{ | { | ||||
char strBuf[0xff]; | 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"); | jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer"); | ||||
} | } | ||||
@@ -807,7 +810,7 @@ public: | |||||
protected: | protected: | ||||
void run() override | void run() override | ||||
{ | { | ||||
plugin.setBufferSize(256); | |||||
plugin.setBufferSize(256, true); | |||||
plugin.activate(); | plugin.activate(); | ||||
float buffer[256]; | float buffer[256]; | ||||
@@ -862,8 +865,8 @@ bool runSelfTests() | |||||
plugin.activate(); | plugin.activate(); | ||||
plugin.deactivate(); | plugin.deactivate(); | ||||
plugin.setBufferSize(128); | |||||
plugin.setSampleRate(48000); | |||||
plugin.setBufferSize(128, true); | |||||
plugin.setSampleRate(48000, true); | |||||
plugin.activate(); | plugin.activate(); | ||||
float buffer[128] = {}; | float buffer[128] = {}; | ||||
@@ -1030,6 +1033,12 @@ int main(int argc, char* argv[]) | |||||
jack_status_t status = jack_status_t(0x0); | jack_status_t status = jack_status_t(0x0); | ||||
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); | 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) | if (client == nullptr) | ||||
{ | { | ||||
String errorString; | String errorString; | ||||
@@ -1060,20 +1069,22 @@ int main(int argc, char* argv[]) | |||||
errorString += "Backend Error;\n"; | errorString += "Backend Error;\n"; | ||||
if (status & JackClientZombie) | if (status & JackClientZombie) | ||||
errorString += "Client is being shutdown against its will;\n"; | errorString += "Client is being shutdown against its will;\n"; | ||||
if (status & JackBridgeNativeFailed) | |||||
errorString += "Native audio driver was unable to start;\n"; | |||||
if (errorString.isNotEmpty()) | if (errorString.isNotEmpty()) | ||||
{ | { | ||||
errorString[errorString.length()-2] = '.'; | 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 | 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) | #if defined(DISTRHO_OS_MAC) | ||||
CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr, | CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr, | ||||
DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8); | DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8); | ||||
CFStringRef errorStringRef = CFStringCreateWithCString(nullptr, | 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, | CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel, | ||||
nullptr, nullptr, nullptr, | nullptr, nullptr, nullptr, | ||||
@@ -1097,7 +1108,7 @@ int main(int argc, char* argv[]) | |||||
FreeLibrary(user32); | 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); | MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR); | ||||
#endif | #endif | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -22,7 +22,7 @@ | |||||
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | ||||
# error Cannot use MIDI Output with LADSPA or DSSI | # error Cannot use MIDI Output with LADSPA or DSSI | ||||
#endif | #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 | # error Cannot use full state with LADSPA or DSSI | ||||
#endif | #endif | ||||
@@ -618,13 +618,19 @@ static const struct DescriptorInitializer | |||||
else | else | ||||
portDescriptors[port] |= LADSPA_PORT_INPUT; | portDescriptors[port] |= LADSPA_PORT_INPUT; | ||||
const uint32_t hints = plugin.getParameterHints(i); | |||||
{ | { | ||||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | const ParameterRanges& ranges(plugin.getParameterRanges(i)); | ||||
const float defValue = ranges.def; | 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)) | /**/ if (d_isZero(defValue)) | ||||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0; | 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) | if (hints & kParameterIsBoolean) | ||||
{ | { | ||||
portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; | portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED; | ||||
@@ -821,7 +821,7 @@ public: | |||||
if (options[i].type == fURIDs.atomInt) | if (options[i].type == fURIDs.atomInt) | ||||
{ | { | ||||
const int32_t bufferSize(*(const int32_t*)options[i].value); | const int32_t bufferSize(*(const int32_t*)options[i].value); | ||||
fPlugin.setBufferSize(bufferSize); | |||||
fPlugin.setBufferSize(bufferSize, true); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -833,7 +833,7 @@ public: | |||||
if (options[i].type == fURIDs.atomInt) | if (options[i].type == fURIDs.atomInt) | ||||
{ | { | ||||
const int32_t bufferSize(*(const int32_t*)options[i].value); | const int32_t bufferSize(*(const int32_t*)options[i].value); | ||||
fPlugin.setBufferSize(bufferSize); | |||||
fPlugin.setBufferSize(bufferSize, true); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -846,7 +846,7 @@ public: | |||||
{ | { | ||||
const float sampleRate(*(const float*)options[i].value); | const float sampleRate(*(const float*)options[i].value); | ||||
fSampleRate = sampleRate; | fSampleRate = sampleRate; | ||||
fPlugin.setSampleRate(sampleRate); | |||||
fPlugin.setSampleRate(sampleRate, true); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -1070,6 +1070,7 @@ public: | |||||
setState(key, filename); | 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) | for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | ||||
{ | { | ||||
if (fPlugin.getStateKey(i) == key) | if (fPlugin.getStateKey(i) == key) | ||||
@@ -1079,6 +1080,7 @@ public: | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
*/ | |||||
return LV2_WORKER_SUCCESS; | return LV2_WORKER_SUCCESS; | ||||
} | } | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -42,7 +42,11 @@ | |||||
# include "mod-license.h" | # include "mod-license.h" | ||||
#endif | #endif | ||||
#ifndef DISTRHO_OS_WINDOWS | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
# include <direct.h> | |||||
#else | |||||
# include <sys/stat.h> | |||||
# include <sys/types.h> | |||||
# include <unistd.h> | # include <unistd.h> | ||||
#endif | #endif | ||||
@@ -65,6 +69,10 @@ | |||||
# define DISTRHO_PLUGIN_USES_MODGUI 0 | # define DISTRHO_PLUGIN_USES_MODGUI 0 | ||||
#endif | #endif | ||||
#ifndef DISTRHO_PLUGIN_USES_CUSTOM_MODGUI | |||||
# define DISTRHO_PLUGIN_USES_CUSTOM_MODGUI 0 | |||||
#endif | |||||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | #if DISTRHO_PLUGIN_HAS_EMBED_UI | ||||
# if DISTRHO_OS_HAIKU | # if DISTRHO_OS_HAIKU | ||||
# define DISTRHO_LV2_UI_TYPE "BeUI" | # define DISTRHO_LV2_UI_TYPE "BeUI" | ||||
@@ -331,7 +339,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
} | } | ||||
#endif | #endif | ||||
manifestFile << manifestString << std::endl; | |||||
manifestFile << manifestString; | |||||
manifestFile.close(); | manifestFile.close(); | ||||
std::cout << " done!" << std::endl; | std::cout << " done!" << std::endl; | ||||
} | } | ||||
@@ -748,6 +756,8 @@ void lv2_generate_ttl(const char* const basename) | |||||
if (! designated) | if (! designated) | ||||
{ | { | ||||
const uint32_t hints = plugin.getParameterHints(i); | |||||
// name and symbol | // name and symbol | ||||
const String& paramName(plugin.getParameterName(i)); | const String& paramName(plugin.getParameterName(i)); | ||||
@@ -772,13 +782,35 @@ void lv2_generate_ttl(const char* const basename) | |||||
// ranges | // ranges | ||||
const ParameterRanges& ranges(plugin.getParameterRanges(i)); | const ParameterRanges& ranges(plugin.getParameterRanges(i)); | ||||
if (plugin.getParameterHints(i) & kParameterIsInteger) | |||||
if (hints & kParameterIsInteger) | |||||
{ | { | ||||
if (plugin.isParameterInput(i)) | if (plugin.isParameterInput(i)) | ||||
pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n"; | pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n"; | ||||
pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n"; | ||||
pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\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 | else | ||||
{ | { | ||||
if (plugin.isParameterInput(i)) | if (plugin.isParameterInput(i)) | ||||
@@ -809,7 +841,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
else | else | ||||
pluginString += " rdfs:label \"" + enumValue.label + "\" ;\n"; | 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)); | const int rounded = (int)(enumValue.value + (enumValue.value < 0.0f ? -0.5f : 0.5f)); | ||||
pluginString += " rdf:value " + String(rounded) + " ;\n"; | pluginString += " rdf:value " + String(rounded) + " ;\n"; | ||||
@@ -877,7 +909,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
pluginString += " a unit:Unit ;\n"; | pluginString += " a unit:Unit ;\n"; | ||||
pluginString += " rdfs:label \"" + unit + "\" ;\n"; | pluginString += " rdfs:label \"" + unit + "\" ;\n"; | ||||
pluginString += " unit:symbol \"" + unit + "\" ;\n"; | pluginString += " unit:symbol \"" + unit + "\" ;\n"; | ||||
if (plugin.getParameterHints(i) & kParameterIsInteger) | |||||
if (hints & kParameterIsInteger) | |||||
pluginString += " unit:render \"%d " + unit + "\" ;\n"; | pluginString += " unit:render \"%d " + unit + "\" ;\n"; | ||||
else | else | ||||
pluginString += " unit:render \"%f " + unit + "\" ;\n"; | pluginString += " unit:render \"%f " + unit + "\" ;\n"; | ||||
@@ -897,8 +929,6 @@ void lv2_generate_ttl(const char* const basename) | |||||
} | } | ||||
// hints | // hints | ||||
const uint32_t hints = plugin.getParameterHints(i); | |||||
if (hints & kParameterIsBoolean) | if (hints & kParameterIsBoolean) | ||||
{ | { | ||||
if ((hints & kParameterIsTrigger) == kParameterIsTrigger) | if ((hints & kParameterIsTrigger) == kParameterIsTrigger) | ||||
@@ -909,6 +939,8 @@ void lv2_generate_ttl(const char* const basename) | |||||
pluginString += " lv2:portProperty lv2:integer ;\n"; | pluginString += " lv2:portProperty lv2:integer ;\n"; | ||||
if (hints & kParameterIsLogarithmic) | if (hints & kParameterIsLogarithmic) | ||||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n"; | 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)) | if ((hints & kParameterIsAutomatable) == 0 && plugin.isParameterInput(i)) | ||||
{ | { | ||||
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n"; | 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 | // MOD | ||||
pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n"; | ||||
pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n"; | pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n"; | ||||
#endif | |||||
#endif | |||||
// name | // name | ||||
{ | { | ||||
@@ -1209,11 +1241,308 @@ void lv2_generate_ttl(const char* const basename) | |||||
} | } | ||||
} | } | ||||
pluginFile << pluginString << std::endl; | |||||
pluginFile << pluginString; | |||||
pluginFile.close(); | pluginFile.close(); | ||||
std::cout << " done!" << std::endl; | 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 | #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, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4); | ||||
addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true); | ||||
uiFile << uiString << std::endl; | |||||
uiFile << uiString; | |||||
uiFile.close(); | uiFile.close(); | ||||
std::cout << " done!" << std::endl; | std::cout << " done!" << std::endl; | ||||
} | } | ||||
@@ -1310,7 +1639,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
presetString += " <"; | presetString += " <"; | ||||
if (plugin.getStateHints(i) & kStateIsHostReadable) | |||||
if (plugin.getStateHints(j) & kStateIsHostReadable) | |||||
presetString += DISTRHO_PLUGIN_URI "#"; | presetString += DISTRHO_PLUGIN_URI "#"; | ||||
else | else | ||||
presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX; | presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX; | ||||
@@ -1371,7 +1700,7 @@ void lv2_generate_ttl(const char* const basename) | |||||
presetsString += presetString; | presetsString += presetString; | ||||
} | } | ||||
presetsFile << presetsString << std::endl; | |||||
presetsFile << presetsString; | |||||
presetsFile.close(); | presetsFile.close(); | ||||
std::cout << " done!" << std::endl; | std::cout << " done!" << std::endl; | ||||
} | } | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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->name, sPlugin->getName(), ARRAY_SIZE(info->name)); | ||||
d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); | d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor)); | ||||
d_strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version)); | 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) | if (idx == 0) | ||||
{ | { | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * 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 | * 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 | * 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. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
#include "DistrhoDetails.hpp" | |||||
#include "src/DistrhoPluginChecks.h" | #include "src/DistrhoPluginChecks.h" | ||||
#include "src/DistrhoDefines.h" | #include "src/DistrhoDefines.h" | ||||
@@ -57,9 +57,10 @@ public: | |||||
void* const dspPtr = nullptr, | void* const dspPtr = nullptr, | ||||
const double scaleFactor = 0.0, | const double scaleFactor = 0.0, | ||||
const uint32_t bgColor = 0, | const uint32_t bgColor = 0, | ||||
const uint32_t fgColor = 0xffffffff) | |||||
const uint32_t fgColor = 0xffffffff, | |||||
const char* const appClassName = nullptr) | |||||
: ui(nullptr), | : ui(nullptr), | ||||
uiData(new UI::PrivateData()) | |||||
uiData(new UI::PrivateData(appClassName)) | |||||
{ | { | ||||
uiData->sampleRate = sampleRate; | uiData->sampleRate = sampleRate; | ||||
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr; | ||||
@@ -78,7 +78,8 @@ public: | |||||
const float sampleRate, | const float sampleRate, | ||||
const float scaleFactor, | const float scaleFactor, | ||||
const uint32_t bgColor, | const uint32_t bgColor, | ||||
const uint32_t fgColor) | |||||
const uint32_t fgColor, | |||||
const char* const appClassName) | |||||
: fUridMap(uridMap), | : fUridMap(uridMap), | ||||
fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)), | fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)), | ||||
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), | fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)), | ||||
@@ -97,7 +98,7 @@ public: | |||||
sendNoteCallback, | sendNoteCallback, | ||||
nullptr, // resize is very messy, hosts can do it without extensions | nullptr, // resize is very messy, hosts can do it without extensions | ||||
fileRequestCallback, | fileRequestCallback, | ||||
bundlePath, dspPtr, scaleFactor, bgColor, fgColor) | |||||
bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName) | |||||
{ | { | ||||
if (widget != nullptr) | if (widget != nullptr) | ||||
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); | *widget = (LV2UI_Widget)fUI.getNativeWindowHandle(); | ||||
@@ -113,10 +114,11 @@ public: | |||||
// if winId == 0 then options must not be null | // if winId == 0 then options must not be null | ||||
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | ||||
#ifndef __EMSCRIPTEN__ | |||||
const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle); | const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle); | ||||
const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId); | 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) | for (int i=0; options[i].key != 0; ++i) | ||||
{ | { | ||||
@@ -134,19 +136,18 @@ public: | |||||
{ | { | ||||
if (options[i].type == fURIDs.atomString) | 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 | else | ||||
d_stderr("Host provides windowTitle but has wrong value type"); | 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); | fUI.stateChanged(key, value); | ||||
} | } | ||||
} | } | ||||
else if (atom->type == fURIDs.midiEvent) | |||||
{ | |||||
// ignore | |||||
} | |||||
else | 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 | #endif | ||||
@@ -258,7 +264,7 @@ public: | |||||
if (options[i].type == fURIDs.atomFloat) | if (options[i].type == fURIDs.atomFloat) | ||||
{ | { | ||||
const float sampleRate = *(const float*)options[i].value; | const float sampleRate = *(const float*)options[i].value; | ||||
fUI.setSampleRate(sampleRate); | |||||
fUI.setSampleRate(sampleRate, true); | |||||
continue; | continue; | ||||
} | } | ||||
else | else | ||||
@@ -559,17 +565,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||||
float scaleFactor = 0.0f; | float scaleFactor = 0.0f; | ||||
uint32_t bgColor = 0; | uint32_t bgColor = 0; | ||||
uint32_t fgColor = 0xffffffff; | uint32_t fgColor = 0xffffffff; | ||||
const char* appClassName = nullptr; | |||||
if (options != nullptr) | if (options != nullptr) | ||||
{ | { | ||||
const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); | 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 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 uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate); | ||||
const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor); | const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor); | ||||
const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor); | const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor); | ||||
#ifndef DISTRHO_OS_MAC | #ifndef DISTRHO_OS_MAC | ||||
const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor); | const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor); | ||||
#endif | #endif | ||||
const LV2_URID uridClassName = uridMap->map(uridMap->handle, "urn:distrho:className"); | |||||
for (int i=0; options[i].key != 0; ++i) | 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"); | d_stderr("Host provides UI scale factor but has wrong value type"); | ||||
} | } | ||||
#endif | #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, | return new UiLv2(bundlePath, winId, options, uridMap, features, | ||||
controller, writeFunction, widget, instance, | controller, writeFunction, widget, instance, | ||||
sampleRate, scaleFactor, bgColor, fgColor); | |||||
sampleRate, scaleFactor, bgColor, fgColor, appClassName); | |||||
} | } | ||||
#define uiPtr ((UiLv2*)ui) | #define uiPtr ((UiLv2*)ui) | ||||
@@ -715,4 +731,199 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) | |||||
return (index == 0) ? &sLv2UiDescriptor : nullptr; | 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" | # include "../../dgl/src/pugl.hpp" | ||||
#endif | #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) | #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI) | ||||
# define DISTRHO_UI_IS_STANDALONE 1 | # define DISTRHO_UI_IS_STANDALONE 1 | ||||
#else | #else | ||||
@@ -60,7 +65,7 @@ struct PluginApplication | |||||
DGL_NAMESPACE::IdleCallback* idleCallback; | DGL_NAMESPACE::IdleCallback* idleCallback; | ||||
UI* ui; | UI* ui; | ||||
explicit PluginApplication() | |||||
explicit PluginApplication(const char*) | |||||
: idleCallback(nullptr), | : idleCallback(nullptr), | ||||
ui(nullptr) {} | ui(nullptr) {} | ||||
@@ -105,20 +110,26 @@ struct PluginApplication | |||||
class PluginApplication : public DGL_NAMESPACE::Application | class PluginApplication : public DGL_NAMESPACE::Application | ||||
{ | { | ||||
public: | public: | ||||
explicit PluginApplication() | |||||
explicit PluginApplication(const char* className) | |||||
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE) | : 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); | setClassName(className); | ||||
#endif | |||||
#else | |||||
// unused | |||||
(void)className; | |||||
#endif | |||||
} | } | ||||
void triggerIdleCallbacks() | void triggerIdleCallbacks() | ||||
@@ -320,9 +331,10 @@ struct UI::PrivateData { | |||||
uint fgColor; | uint fgColor; | ||||
double scaleFactor; | double scaleFactor; | ||||
uintptr_t winId; | 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; | char* uiStateFileKeyRequest; | ||||
#endif | |||||
std::map<std::string,std::string> lastUsedDirnames; | |||||
#endif | |||||
char* bundlePath; | char* bundlePath; | ||||
// Ignore initial resize events while initializing | // Ignore initial resize events while initializing | ||||
@@ -337,8 +349,8 @@ struct UI::PrivateData { | |||||
setSizeFunc setSizeCallbackFunc; | setSizeFunc setSizeCallbackFunc; | ||||
fileRequestFunc fileRequestCallbackFunc; | fileRequestFunc fileRequestCallbackFunc; | ||||
PrivateData() noexcept | |||||
: app(), | |||||
PrivateData(const char* const appClassName) noexcept | |||||
: app(appClassName), | |||||
window(nullptr), | window(nullptr), | ||||
sampleRate(0), | sampleRate(0), | ||||
parameterOffset(0), | parameterOffset(0), | ||||
@@ -347,9 +359,9 @@ struct UI::PrivateData { | |||||
fgColor(0xffffffff), | fgColor(0xffffffff), | ||||
scaleFactor(1.0), | scaleFactor(1.0), | ||||
winId(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), | uiStateFileKeyRequest(nullptr), | ||||
#endif | |||||
#endif | |||||
bundlePath(nullptr), | bundlePath(nullptr), | ||||
initializing(true), | initializing(true), | ||||
callbacksPtr(nullptr), | callbacksPtr(nullptr), | ||||
@@ -360,32 +372,32 @@ struct UI::PrivateData { | |||||
setSizeCallbackFunc(nullptr), | setSizeCallbackFunc(nullptr), | ||||
fileRequestCallbackFunc(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; | parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | ||||
# if DISTRHO_PLUGIN_WANT_LATENCY | |||||
#if DISTRHO_PLUGIN_WANT_LATENCY | |||||
parameterOffset += 1; | 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; | 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; | parameterOffset += 1; | ||||
# endif | |||||
#endif | |||||
#endif | |||||
#endif | |||||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||||
#ifdef DISTRHO_PLUGIN_TARGET_VST3 | |||||
parameterOffset += kVst3InternalParameterCount; | parameterOffset += kVst3InternalParameterCount; | ||||
#endif | |||||
#endif | |||||
} | } | ||||
~PrivateData() noexcept | ~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); | std::free(uiStateFileKeyRequest); | ||||
#endif | |||||
#endif | |||||
std::free(bundlePath); | std::free(bundlePath); | ||||
} | } | ||||
@@ -438,7 +450,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||||
if (fileRequestCallbackFunc != nullptr) | if (fileRequestCallbackFunc != nullptr) | ||||
return fileRequestCallbackFunc(callbacksPtr, key); | 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); | std::free(uiStateFileKeyRequest); | ||||
uiStateFileKeyRequest = strdup(key); | uiStateFileKeyRequest = strdup(key); | ||||
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); | DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false); | ||||
@@ -449,8 +461,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key) | |||||
DGL_NAMESPACE::FileBrowserOptions opts; | DGL_NAMESPACE::FileBrowserOptions opts; | ||||
opts.title = title; | opts.title = title; | ||||
if (lastUsedDirnames.count(key)) | |||||
opts.startDir = lastUsedDirnames[key].c_str(); | |||||
return window->openFileBrowser(opts); | return window->openFileBrowser(opts); | ||||
#endif | |||||
#endif | |||||
return false; | return false; | ||||
} | } | ||||
@@ -466,7 +480,7 @@ inline void PluginWindow::onFileSelected(const char* const filename) | |||||
if (initializing) | if (initializing) | ||||
return; | 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) | if (char* const key = ui->uiData->uiStateFileKeyRequest) | ||||
{ | { | ||||
ui->uiData->uiStateFileKeyRequest = nullptr; | ui->uiData->uiStateFileKeyRequest = nullptr; | ||||
@@ -474,8 +488,13 @@ inline void PluginWindow::onFileSelected(const char* const filename) | |||||
{ | { | ||||
// notify DSP | // notify DSP | ||||
ui->setState(key, filename); | ui->setState(key, filename); | ||||
// notify UI | // notify UI | ||||
ui->stateChanged(key, filename); | 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); | std::free(key); | ||||
return; | return; | ||||
@@ -233,7 +233,7 @@ private: | |||||
HWND fTimerWindow; | HWND fTimerWindow; | ||||
String fTimerWindowClassName; | 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(); | 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; | delete nativeBridge; | ||||
#endif | #endif | ||||
#endif | #endif | ||||
if (status != nullptr) | 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; | return nullptr; | ||||
} | } | ||||
@@ -126,7 +126,8 @@ enum JackStatus { | |||||
JackShmFailure = 0x0200, | JackShmFailure = 0x0200, | ||||
JackVersionError = 0x0400, | JackVersionError = 0x0400, | ||||
JackBackendError = 0x0800, | JackBackendError = 0x0800, | ||||
JackClientZombie = 0x1000 | |||||
JackClientZombie = 0x1000, | |||||
JackBridgeNativeFailed = 0x10000 | |||||
}; | }; | ||||
enum JackLatencyCallbackMode { | enum JackLatencyCallbackMode { | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* Native Bridge for DPF | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -21,6 +21,18 @@ | |||||
#include "../../extra/RingBuffer.hpp" | #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; | using DISTRHO_NAMESPACE::HeapRingBuffer; | ||||
struct NativeBridge { | struct NativeBridge { | ||||
@@ -31,6 +43,8 @@ struct NativeBridge { | |||||
// Port caching information | // Port caching information | ||||
uint numAudioIns; | uint numAudioIns; | ||||
uint numAudioOuts; | uint numAudioOuts; | ||||
uint numCvIns; | |||||
uint numCvOuts; | |||||
uint numMidiIns; | uint numMidiIns; | ||||
uint numMidiOuts; | uint numMidiOuts; | ||||
@@ -43,9 +57,10 @@ struct NativeBridge { | |||||
// Runtime buffers | // Runtime buffers | ||||
enum PortMask { | enum PortMask { | ||||
kPortMaskAudio = 0x1000, | kPortMaskAudio = 0x1000, | ||||
kPortMaskMIDI = 0x2000, | |||||
kPortMaskInput = 0x4000, | |||||
kPortMaskOutput = 0x8000, | |||||
kPortMaskCV = 0x2000, | |||||
kPortMaskMIDI = 0x4000, | |||||
kPortMaskInput = 0x10000, | |||||
kPortMaskOutput = 0x20000, | |||||
kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, | kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI, | ||||
kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, | kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI, | ||||
}; | }; | ||||
@@ -71,6 +86,8 @@ struct NativeBridge { | |||||
sampleRate(0), | sampleRate(0), | ||||
numAudioIns(0), | numAudioIns(0), | ||||
numAudioOuts(0), | numAudioOuts(0), | ||||
numCvIns(0), | |||||
numCvOuts(0), | |||||
numMidiIns(0), | numMidiIns(0), | ||||
numMidiOuts(0), | numMidiOuts(0), | ||||
jackProcessCallback(nullptr), | jackProcessCallback(nullptr), | ||||
@@ -211,27 +228,27 @@ struct NativeBridge { | |||||
if (audio) | 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 (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) | 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) | /**/ if (flags & JackPortIsInput) | ||||
isInput = true; | |||||
ret |= kPortMaskInput; | |||||
else if (flags & JackPortIsOutput) | else if (flags & JackPortIsOutput) | ||||
isInput = false; | |||||
ret |= kPortMaskOutput; | |||||
else | else | ||||
return nullptr; | 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) | void* getPortBuffer(jack_port_t* const port) | ||||
@@ -281,7 +310,7 @@ struct NativeBridge { | |||||
DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); | DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr); | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #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)]; | return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)]; | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
@@ -28,9 +28,9 @@ | |||||
# define RTAUDIO_API_TYPE MACOSX_CORE | # define RTAUDIO_API_TYPE MACOSX_CORE | ||||
# define RTMIDI_API_TYPE MACOSX_CORE | # define RTMIDI_API_TYPE MACOSX_CORE | ||||
#elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) | #elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER) | ||||
# define __WINDOWS_DS__ | |||||
# define __WINDOWS_WASAPI__ | |||||
# define __WINDOWS_MM__ | # define __WINDOWS_MM__ | ||||
# define RTAUDIO_API_TYPE WINDOWS_DS | |||||
# define RTAUDIO_API_TYPE WINDOWS_WASAPI | |||||
# define RTMIDI_API_TYPE WINDOWS_MM | # define RTMIDI_API_TYPE WINDOWS_MM | ||||
#else | #else | ||||
# if defined(HAVE_PULSEAUDIO) | # if defined(HAVE_PULSEAUDIO) | ||||
@@ -50,7 +50,9 @@ | |||||
# include "rtmidi/RtMidi.h" | # include "rtmidi/RtMidi.h" | ||||
# include "../../extra/ScopedPointer.hpp" | # include "../../extra/ScopedPointer.hpp" | ||||
# include "../../extra/String.hpp" | # include "../../extra/String.hpp" | ||||
# include "../../extra/ScopedDenormalDisable.hpp" | |||||
using DISTRHO_NAMESPACE::ScopedDenormalDisable; | |||||
using DISTRHO_NAMESPACE::ScopedPointer; | using DISTRHO_NAMESPACE::ScopedPointer; | ||||
using DISTRHO_NAMESPACE::String; | using DISTRHO_NAMESPACE::String; | ||||
@@ -281,13 +283,20 @@ struct RtAudioBridge : NativeBridge { | |||||
return ok; | return ok; | ||||
} | } | ||||
bool _open(const bool withInput) | |||||
bool _open(const bool withInput, RtAudio* tryingAgain = nullptr) | |||||
{ | { | ||||
ScopedPointer<RtAudio> rtAudio; | 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; | uint rtAudioBufferFrames = nextBufferSize; | ||||
@@ -300,15 +309,15 @@ struct RtAudioBridge : NativeBridge { | |||||
if (withInput) | if (withInput) | ||||
{ | { | ||||
inParams.deviceId = rtAudio->getDefaultInputDevice(); | inParams.deviceId = rtAudio->getDefaultInputDevice(); | ||||
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS; | |||||
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2; | |||||
inParamsPtr = &inParams; | inParamsPtr = &inParams; | ||||
} | } | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
RtAudio::StreamParameters outParams; | 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; | RtAudio::StreamParameters* const outParamsPtr = &outParams; | ||||
#else | #else | ||||
RtAudio::StreamParameters* const outParamsPtr = nullptr; | RtAudio::StreamParameters* const outParamsPtr = nullptr; | ||||
@@ -330,6 +339,10 @@ struct RtAudioBridge : NativeBridge { | |||||
rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, | rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames, | ||||
RtAudioCallback, this, &opts, nullptr); | RtAudioCallback, this, &opts, nullptr); | ||||
} catch (const RtAudioError& err) { | } 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__); | d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__); | ||||
return false; | return false; | ||||
} DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); | } DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false); | ||||
@@ -357,14 +370,14 @@ struct RtAudioBridge : NativeBridge { | |||||
if (self->jackProcessCallback == nullptr) | if (self->jackProcessCallback == nullptr) | ||||
{ | { | ||||
if (outputBuffer != 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; | return 0; | ||||
} | } | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | ||||
if (float* const insPtr = static_cast<float*>(inputBuffer)) | 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); | self->audioBuffers[i] = insPtr + (i * numFrames); | ||||
} | } | ||||
#endif | #endif | ||||
@@ -372,11 +385,12 @@ struct RtAudioBridge : NativeBridge { | |||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
if (float* const outsPtr = static_cast<float*>(outputBuffer)) | 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); | self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames); | ||||
} | } | ||||
#endif | #endif | ||||
const ScopedDenormalDisable sdd; | |||||
self->jackProcessCallback(numFrames, self->jackProcessArg); | self->jackProcessCallback(numFrames, self->jackProcessArg); | ||||
return 0; | return 0; | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* SDL Bridge for DPF | * 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 | * 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 | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -18,6 +18,7 @@ | |||||
#define SDL_BRIDGE_HPP_INCLUDED | #define SDL_BRIDGE_HPP_INCLUDED | ||||
#include "NativeBridge.hpp" | #include "NativeBridge.hpp" | ||||
#include "../../extra/ScopedDenormalDisable.hpp" | |||||
#include <SDL.h> | #include <SDL.h> | ||||
@@ -67,7 +68,7 @@ struct SDL2Bridge : NativeBridge { | |||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #if DISTRHO_PLUGIN_NUM_INPUTS > 0 | ||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure"); | 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; | requested.callback = AudioInputCallback; | ||||
SDL_AudioSpec receivedCapture; | SDL_AudioSpec receivedCapture; | ||||
@@ -75,11 +76,12 @@ struct SDL2Bridge : NativeBridge { | |||||
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); | SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE); | ||||
if (captureDeviceId == 0) | 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; | return false; | ||||
#endif | |||||
} | } | ||||
if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS) | |||||
else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2) | |||||
{ | { | ||||
SDL_CloseAudioDevice(captureDeviceId); | SDL_CloseAudioDevice(captureDeviceId); | ||||
captureDeviceId = 0; | captureDeviceId = 0; | ||||
@@ -91,7 +93,7 @@ struct SDL2Bridge : NativeBridge { | |||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
SDL_AudioSpec receivedPlayback; | SDL_AudioSpec receivedPlayback; | ||||
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback"); | 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; | requested.callback = AudioOutputCallback; | ||||
playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, | playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback, | ||||
@@ -102,7 +104,7 @@ struct SDL2Bridge : NativeBridge { | |||||
return false; | return false; | ||||
} | } | ||||
if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS) | |||||
if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2) | |||||
{ | { | ||||
SDL_CloseAudioDevice(playbackDeviceId); | SDL_CloseAudioDevice(playbackDeviceId); | ||||
playbackDeviceId = 0; | playbackDeviceId = 0; | ||||
@@ -113,30 +115,39 @@ struct SDL2Bridge : NativeBridge { | |||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
// if using both input and output, make sure they match | // 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(captureDeviceId); | ||||
SDL_CloseAudioDevice(playbackDeviceId); | SDL_CloseAudioDevice(playbackDeviceId); | ||||
captureDeviceId = playbackDeviceId = 0; | 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; | return false; | ||||
} | } | ||||
if (receivedCapture.freq != receivedPlayback.freq) | |||||
if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0) | |||||
{ | { | ||||
SDL_CloseAudioDevice(captureDeviceId); | SDL_CloseAudioDevice(captureDeviceId); | ||||
SDL_CloseAudioDevice(playbackDeviceId); | SDL_CloseAudioDevice(playbackDeviceId); | ||||
captureDeviceId = playbackDeviceId = 0; | 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; | return false; | ||||
} | } | ||||
#endif | #endif | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #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 | #endif | ||||
allocBuffers(true, false); | allocBuffers(true, false); | ||||
@@ -146,9 +157,11 @@ struct SDL2Bridge : NativeBridge { | |||||
bool close() override | bool close() override | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #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 | #endif | ||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | ||||
@@ -163,8 +176,8 @@ struct SDL2Bridge : NativeBridge { | |||||
bool activate() override | bool activate() override | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #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 | #endif | ||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | ||||
@@ -176,8 +189,8 @@ struct SDL2Bridge : NativeBridge { | |||||
bool deactivate() override | bool deactivate() override | ||||
{ | { | ||||
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 | #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 | #endif | ||||
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 | ||||
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false); | ||||
@@ -198,19 +211,20 @@ struct SDL2Bridge : NativeBridge { | |||||
if (self->jackProcessCallback == nullptr) | if (self->jackProcessCallback == nullptr) | ||||
return; | 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,); | DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); | ||||
const float* const fstream = (const float*)stream; | 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) | 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 DISTRHO_PLUGIN_NUM_OUTPUTS == 0 | ||||
// if there are no outputs, run process callback now | // if there are no outputs, run process callback now | ||||
const ScopedDenormalDisable sdd; | |||||
self->jackProcessCallback(numFrames, self->jackProcessArg); | self->jackProcessCallback(numFrames, self->jackProcessArg); | ||||
#endif | #endif | ||||
} | } | ||||
@@ -231,17 +245,18 @@ struct SDL2Bridge : NativeBridge { | |||||
return; | 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,); | DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,); | ||||
const ScopedDenormalDisable sdd; | |||||
self->jackProcessCallback(numFrames, self->jackProcessArg); | self->jackProcessCallback(numFrames, self->jackProcessArg); | ||||
float* const fstream = (float*)stream; | 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) | 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 | #endif | ||||
@@ -163,7 +163,7 @@ struct WebBridge : NativeBridge { | |||||
if (WAB.audioContext.state === 'suspended') | if (WAB.audioContext.state === 'suspended') | ||||
WAB.audioContext.resume(); | 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; | return true; | ||||
} | } | ||||
@@ -217,11 +217,11 @@ struct WebBridge : NativeBridge { | |||||
constraints['audio'] = true; | constraints['audio'] = true; | ||||
constraints['video'] = false; | constraints['video'] = false; | ||||
constraints['autoGainControl'] = {}; | constraints['autoGainControl'] = {}; | ||||
constraints['autoGainControl']['exact'] = false; | |||||
constraints['autoGainControl']['ideal'] = false; | |||||
constraints['echoCancellation'] = {}; | constraints['echoCancellation'] = {}; | ||||
constraints['echoCancellation']['exact'] = false; | |||||
constraints['echoCancellation']['ideal'] = false; | |||||
constraints['noiseSuppression'] = {}; | constraints['noiseSuppression'] = {}; | ||||
constraints['noiseSuppression']['exact'] = false; | |||||
constraints['noiseSuppression']['ideal'] = false; | |||||
constraints['channelCount'] = {}; | constraints['channelCount'] = {}; | ||||
constraints['channelCount']['min'] = 0; | constraints['channelCount']['min'] = 0; | ||||
constraints['channelCount']['ideal'] = numInputs; | constraints['channelCount']['ideal'] = numInputs; | ||||
@@ -236,6 +236,25 @@ struct WebBridge : NativeBridge { | |||||
constraints['googAutoGainControl'] = false; | constraints['googAutoGainControl'] = false; | ||||
var success = function(stream) { | 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 = WAB.audioContext['createMediaStreamSource'](stream); | ||||
WAB.captureStreamNode.connect(WAB.processor); | WAB.captureStreamNode.connect(WAB.processor); | ||||
}; | }; | ||||
@@ -247,7 +266,7 @@ struct WebBridge : NativeBridge { | |||||
} else if (navigator.webkitGetUserMedia !== undefined) { | } else if (navigator.webkitGetUserMedia !== undefined) { | ||||
navigator.webkitGetUserMedia(constraints, success, fail); | navigator.webkitGetUserMedia(constraints, success, fail); | ||||
} | } | ||||
}, DISTRHO_PLUGIN_NUM_INPUTS); | |||||
}, DISTRHO_PLUGIN_NUM_INPUTS_2); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -279,7 +298,7 @@ struct WebBridge : NativeBridge { | |||||
WAB.captureStreamNode.disconnect(WAB.processor); | WAB.captureStreamNode.disconnect(WAB.processor); | ||||
return 1; | 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) | if (!success) | ||||
return false; | return false; | ||||
@@ -292,9 +311,10 @@ struct WebBridge : NativeBridge { | |||||
bufferSizeCallback(newBufferSize, jackBufferSizeArg); | bufferSizeCallback(newBufferSize, jackBufferSizeArg); | ||||
EM_ASM({ | 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']; | var WAB = Module['WebAudioBridge']; | ||||
// store the new processor | // store the new processor | ||||
@@ -309,13 +329,13 @@ struct WebBridge : NativeBridge { | |||||
var buffer = e['inputBuffer']['getChannelData'](i); | var buffer = e['inputBuffer']['getChannelData'](i); | ||||
for (var j = 0; j < bufferSize; ++j) { | for (var j = 0; j < bufferSize; ++j) { | ||||
// setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); | // 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) { | for (var i = 0; i < numOutputs; ++i) { | ||||
var buffer = e['outputBuffer']['getChannelData'](i); | var buffer = e['outputBuffer']['getChannelData'](i); | ||||
var offset = bufferSize * (numInputs + i); | |||||
var offset = bufferSize * (numInputsR + i); | |||||
for (var j = 0; j < bufferSize; ++j) { | for (var j = 0; j < bufferSize; ++j) { | ||||
buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; | buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; | ||||
} | } | ||||
@@ -329,7 +349,7 @@ struct WebBridge : NativeBridge { | |||||
if (WAB.captureStreamNode) | if (WAB.captureStreamNode) | ||||
WAB.captureStreamNode.connect(WAB.processor); | 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; | return true; | ||||
} | } | ||||
@@ -452,7 +472,7 @@ struct WebBridge : NativeBridge { | |||||
} | } | ||||
else | 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); | 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; | stream_.state = STREAM_STOPPING; | ||||
// wait until stream thread is stopped | // 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 | // close thread handle | ||||
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { | if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) { | ||||
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; | errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread."; | ||||
@@ -33,7 +33,9 @@ FOLDERS=`find . -type d -name \*.lv2` | |||||
for i in ${FOLDERS}; do | for i in ${FOLDERS}; do | ||||
cd ${i} | 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 .. | cd .. | ||||
done | done |