Browse Source

Update dpf

Signed-off-by: falkTX <falktx@falktx.com>
tags/v1.7
falkTX 2 years ago
parent
commit
8722584c1e
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
64 changed files with 4708 additions and 5303 deletions
  1. +6
    -3
      dpf/CMakeLists.txt
  2. +76
    -16
      dpf/Makefile.base.mk
  3. +194
    -87
      dpf/Makefile.plugins.mk
  4. +90
    -49
      dpf/cmake/DPF-plugin.cmake
  5. +5
    -0
      dpf/dgl/Color.hpp
  6. +1
    -1
      dpf/dgl/EventHandlers.hpp
  7. +89
    -81
      dpf/dgl/Makefile
  8. +4
    -2
      dpf/dgl/src/ApplicationPrivateData.cpp
  9. +9
    -0
      dpf/dgl/src/Color.cpp
  10. +1
    -1
      dpf/dgl/src/EventHandlers.cpp
  11. +0
    -492
      dpf/dgl/src/pugl-custom/pugl.h
  12. +0
    -441
      dpf/dgl/src/pugl-custom/pugl_haiku.cpp
  13. +0
    -263
      dpf/dgl/src/pugl-custom/pugl_internal.h
  14. +0
    -974
      dpf/dgl/src/pugl-custom/pugl_osx.m
  15. +0
    -565
      dpf/dgl/src/pugl-custom/pugl_win.cpp
  16. +0
    -739
      dpf/dgl/src/pugl-custom/pugl_x11.c
  17. +0
    -29
      dpf/dgl/src/pugl-extra/extras.c
  18. +0
    -50
      dpf/dgl/src/pugl-extra/extras.h
  19. +342
    -21
      dpf/dgl/src/pugl-extra/haiku.cpp
  20. +12
    -22
      dpf/dgl/src/pugl-extra/haiku.h
  21. +87
    -0
      dpf/dgl/src/pugl-extra/haiku_gl.cpp
  22. +24
    -0
      dpf/dgl/src/pugl-extra/haiku_stub.cpp
  23. +0
    -48
      dpf/dgl/src/pugl-extra/mac.m
  24. +1141
    -0
      dpf/dgl/src/pugl-extra/wasm.c
  25. +45
    -0
      dpf/dgl/src/pugl-extra/wasm.h
  26. +228
    -0
      dpf/dgl/src/pugl-extra/wasm_gl.c
  27. +24
    -0
      dpf/dgl/src/pugl-extra/wasm_stub.c
  28. +0
    -118
      dpf/dgl/src/pugl-extra/win.c
  29. +0
    -111
      dpf/dgl/src/pugl-extra/x11.c
  30. +35
    -11
      dpf/dgl/src/pugl.cpp
  31. +5
    -1
      dpf/dgl/src/pugl.hpp
  32. +1031
    -0
      dpf/distrho/DistrhoDetails.hpp
  33. +2
    -934
      dpf/distrho/DistrhoPlugin.hpp
  34. +2
    -1
      dpf/distrho/DistrhoUI.hpp
  35. +4
    -0
      dpf/distrho/DistrhoUIMain.cpp
  36. +1
    -0
      dpf/distrho/DistrhoUI_macOS.mm
  37. +10
    -11
      dpf/distrho/DistrhoUtils.hpp
  38. +3
    -3
      dpf/distrho/extra/FileBrowserDialogImpl.hpp
  39. +112
    -0
      dpf/distrho/extra/ScopedDenormalDisable.hpp
  40. +15
    -1
      dpf/distrho/extra/String.hpp
  41. +204
    -0
      dpf/distrho/extra/ValueSmoother.hpp
  42. +1
    -1
      dpf/distrho/src/DistrhoPlugin.cpp
  43. +33
    -34
      dpf/distrho/src/DistrhoPluginCLAP.cpp
  44. +5
    -1
      dpf/distrho/src/DistrhoPluginChecks.h
  45. +1
    -1
      dpf/distrho/src/DistrhoPluginInternal.hpp
  46. +25
    -14
      dpf/distrho/src/DistrhoPluginJACK.cpp
  47. +11
    -7
      dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp
  48. +5
    -3
      dpf/distrho/src/DistrhoPluginLV2.cpp
  49. +343
    -14
      dpf/distrho/src/DistrhoPluginLV2export.cpp
  50. +1
    -1
      dpf/distrho/src/DistrhoPluginVST2.cpp
  51. +2
    -2
      dpf/distrho/src/DistrhoPluginVST3.cpp
  52. +2
    -1
      dpf/distrho/src/DistrhoUI.cpp
  53. +3
    -2
      dpf/distrho/src/DistrhoUIInternal.hpp
  54. +224
    -13
      dpf/distrho/src/DistrhoUILV2.cpp
  55. +54
    -35
      dpf/distrho/src/DistrhoUIPrivateData.hpp
  56. +1
    -1
      dpf/distrho/src/DistrhoUIVST3.cpp
  57. +20
    -1
      dpf/distrho/src/jackbridge/JackBridge.cpp
  58. +2
    -1
      dpf/distrho/src/jackbridge/JackBridge.hpp
  59. +63
    -34
      dpf/distrho/src/jackbridge/NativeBridge.hpp
  60. +26
    -12
      dpf/distrho/src/jackbridge/RtAudioBridge.hpp
  61. +44
    -29
      dpf/distrho/src/jackbridge/SDL2Bridge.hpp
  62. +34
    -14
      dpf/distrho/src/jackbridge/WebBridge.hpp
  63. +2
    -5
      dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp
  64. +4
    -2
      dpf/utils/generate-ttl.sh

+ 6
- 3
dpf/CMakeLists.txt View File

@@ -32,7 +32,7 @@ if(DPF_LIBRARIES)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND)
if(CAIRO_FOUND AND (NOT HAIKU))
dpf__add_dgl_cairo(FALSE)
endif()
endif()
@@ -43,11 +43,14 @@ if(DPF_EXAMPLES)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND)
if(CAIRO_FOUND AND (NOT HAIKU))
add_subdirectory("examples/CairoUI")
endif()
endif()
#add_subdirectory("examples/ExternalUI")
if((NOT WIN32) AND (NOT APPLE))
add_subdirectory("examples/ExternalUI")
endif()
add_subdirectory("examples/EmbedExternalUI")
add_subdirectory("examples/FileHandling")
add_subdirectory("examples/Info")
add_subdirectory("examples/Latency")


+ 76
- 16
dpf/Makefile.base.mk View File

@@ -35,7 +35,9 @@ DPF_MAKEFILE_BASE_INCLUDED = true
# ---------------------------------------------------------------------------------------------------------------------
# Auto-detect target compiler if not defined

ifeq ($(shell echo '\#test' | grep -- '\#test'),\#test)
ifneq ($(shell echo -e escaped-by-default | grep -- '-e escaped-by-default'),-e escaped-by-default)
TARGET_COMPILER = $(shell echo -e '#ifdef __clang__\nclang\n#else\ngcc\n#endif' | $(CC) -E -P -x c - 2>/dev/null)
else ifeq ($(shell echo '\#escaped-by-default' | grep -- '\#escaped-by-default'),\#escaped-by-default)
TARGET_COMPILER = $(shell echo '\#ifdef __clang__\nclang\n\#else\ngcc\n\#endif' | $(CC) -E -P -x c - 2>/dev/null)
else
TARGET_COMPILER = $(shell echo '#ifdef __clang__\nclang\n#else\ngcc\n#endif' | $(CC) -E -P -x c - 2>/dev/null)
@@ -272,7 +274,10 @@ BASE_OPTS = -O2 -ffast-math -fdata-sections -ffunction-sections
endif

ifeq ($(DEBUG),true)
BASE_FLAGS += -DDEBUG -O0 -g -fsanitize=address
BASE_FLAGS += -DDEBUG -O0 -g
ifneq ($(HAIKU),true)
BASE_FLAGS += -fsanitize=address
endif
LINK_OPTS =
ifeq ($(WASM),true)
LINK_OPTS += -sASSERTIONS=1
@@ -347,9 +352,11 @@ endif
# ---------------------------------------------------------------------------------------------------------------------
# Check for required libraries

HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true)
ifneq ($(HAIKU),true)
HAVE_CAIRO = $(shell $(PKG_CONFIG) --exists cairo && echo true)
endif

ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true)
ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true)
HAVE_OPENGL = true
else
HAVE_OPENGL = $(shell $(PKG_CONFIG) --exists gl && echo true)
@@ -402,18 +409,34 @@ endif
# Set Generic DGL stuff

ifeq ($(HAIKU),true)

DGL_SYSTEM_LIBS += -lbe

else ifeq ($(MACOS),true)
DGL_SYSTEM_LIBS += -framework Cocoa -framework CoreVideo

DGL_SYSTEM_LIBS += -framework Cocoa
DGL_SYSTEM_LIBS += -framework CoreVideo

else ifeq ($(WASM),true)

# wasm builds cannot work using regular desktop OpenGL
ifeq (,$(USE_GLES2)$(USE_GLES3))
USE_GLES2 = true
endif

else ifeq ($(WINDOWS),true)
DGL_SYSTEM_LIBS += -lgdi32 -lcomdlg32
# -lole32

DGL_SYSTEM_LIBS += -lcomdlg32
DGL_SYSTEM_LIBS += -lgdi32
# DGL_SYSTEM_LIBS += -lole32

else

ifeq ($(HAVE_DBUS),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1)
endif

ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs x11)
@@ -429,7 +452,8 @@ ifeq ($(HAVE_XRANDR),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR
DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr)
endif
endif
endif # HAVE_X11

endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -444,7 +468,7 @@ CAIRO_LIBS = $(shell $(PKG_CONFIG) --libs cairo)

HAVE_CAIRO_OR_OPENGL = true

endif
endif # HAVE_CAIRO

# ---------------------------------------------------------------------------------------------------------------------
# Set OpenGL specific stuff
@@ -454,8 +478,8 @@ ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DHAVE_OPENGL

ifeq ($(HAIKU),true)
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl)
OPENGL_LIBS = $(shell $(PKG_CONFIG) --libs gl)
OPENGL_FLAGS =
OPENGL_LIBS = -lGL
else ifeq ($(MACOS),true)
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations
OPENGL_LIBS = -framework OpenGL
@@ -476,12 +500,12 @@ endif

HAVE_CAIRO_OR_OPENGL = true

endif
endif # HAVE_OPENGL

# ---------------------------------------------------------------------------------------------------------------------
# Set Stub specific stuff

ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true)
ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true)
HAVE_STUB = true
else
HAVE_STUB = $(HAVE_X11)
@@ -540,7 +564,7 @@ endif
# ---------------------------------------------------------------------------------------------------------------------
# Backwards-compatible HAVE_DGL

ifeq ($(MACOS_OR_WASM_OR_WINDOWS),true)
ifeq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true)
HAVE_DGL = true
else ifeq ($(HAVE_OPENGL),true)
HAVE_DGL = $(HAVE_X11)
@@ -633,6 +657,41 @@ else
SHARED = -shared
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set CLAP binary directory

ifeq ($(MACOS),true)
CLAP_BINARY_DIR = Contents/MacOS
else
CLAP_BINARY_DIR =
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set VST2 binary directory

ifeq ($(MACOS),true)
VST2_BINARY_DIR = Contents/MacOS
else
VST2_BINARY_DIR =
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set VST3 binary directory, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html

ifeq ($(LINUX),true)
VST3_BINARY_DIR = Contents/$(TARGET_PROCESSOR)-linux
else ifeq ($(MACOS),true)
VST3_BINARY_DIR = Contents/MacOS
else ifeq ($(WASM),true)
VST3_BINARY_DIR = Contents/wasm
else ifeq ($(WINDOWS)$(CPU_I386),truetrue)
VST3_BINARY_DIR = Contents/x86-win
else ifeq ($(WINDOWS)$(CPU_X86_64),truetrue)
VST3_BINARY_DIR = Contents/x86_64-win
else
VST3_BINARY_DIR =
endif

# ---------------------------------------------------------------------------------------------------------------------
# Handle the verbosity switch

@@ -713,6 +772,7 @@ MOD_ENVIRONMENT = \
CXX=${1}/host/usr/bin/${2}-g++ \
LD=${1}/host/usr/bin/${2}-ld \
PKG_CONFIG=${1}/host/usr/bin/pkg-config \
PKG_CONFIG_PATH="${1}/staging/usr/lib/pkgconfig" \
STRIP=${1}/host/usr/bin/${2}-strip \
CFLAGS="-I${1}/staging/usr/include $(EXTRA_MOD_FLAGS)" \
CPPFLAGS= \
@@ -738,12 +798,12 @@ modpush:

ifneq (,$(findstring modduo-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo,arm-mod-linux-gnueabihf,arm) $(subst modduo-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm) $(subst modduo-,,$(MAKECMDGOALS))
endif

ifneq (,$(findstring modduox-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox,aarch64-mod-linux-gnueabi,aarch64) $(subst modduox-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64) $(subst modduox-,,$(MAKECMDGOALS))
endif

ifneq (,$(findstring moddwarf-,$(MAKECMDGOALS)))


+ 194
- 87
dpf/Makefile.plugins.mk View File

@@ -6,11 +6,18 @@

# NOTE: NAME, FILES_DSP and FILES_UI must have been defined before including this file!


ifeq ($(DPF_PATH),)
ifeq (,$(wildcard ../../Makefile.base.mk))
ifneq (,$(wildcard dpf/Makefile.base.mk))
BASE_PATH=.
DPF_PATH=dpf
else ifneq (,$(wildcard ../dpf/Makefile.base.mk))
BASE_PATH=..
DPF_PATH=../dpf
else ifneq (,$(wildcard ../../dpf/Makefile.base.mk))
BASE_PATH=../..
DPF_PATH=../../dpf
else
BASE_PATH=../..
DPF_PATH=../..
endif
endif
@@ -20,17 +27,24 @@ include $(DPF_PATH)/Makefile.base.mk
# ---------------------------------------------------------------------------------------------------------------------
# Basic setup

ifeq ($(DPF_TARGET_DIR),)
TARGET_DIR = ../../bin
ifeq ($(MODGUI_BUILD),true)
BUILD_DIR_SUFFIX = -modgui
endif

ifneq ($(DPF_BUILD_DIR),)
BUILD_DIR = $(DPF_BUILD_DIR)$(BUILD_DIR_SUFFIX)
else
TARGET_DIR = $(DPF_TARGET_DIR)
BUILD_DIR = $(BASE_PATH)/build$(BUILD_DIR_SUFFIX)/$(NAME)
endif
ifeq ($(DPF_BUILD_DIR),)
BUILD_DIR = ../../build/$(NAME)

ifneq ($(DPF_TARGET_DIR),)
TARGET_DIR = $(DPF_TARGET_DIR)
else
BUILD_DIR = $(DPF_BUILD_DIR)
TARGET_DIR = $(BASE_PATH)/bin
endif

DGL_BUILD_DIR = $(DPF_PATH)/build$(BUILD_DIR_SUFFIX)

BUILD_C_FLAGS += -I.
BUILD_CXX_FLAGS += -I. -I$(DPF_PATH)/distrho -I$(DPF_PATH)/dgl

@@ -58,6 +72,10 @@ ifeq ($(HAVE_SDL2),true)
BASE_FLAGS += -DHAVE_SDL2
endif

ifneq ($(MODGUI_CLASS_NAME),)
BASE_FLAGS += -DDISTRHO_PLUGIN_MODGUI_CLASS_NAME='"$(MODGUI_CLASS_NAME)"'
endif

# always needed
ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true)
ifneq ($(STATIC_BUILD),true)
@@ -80,14 +98,16 @@ endif

else ifneq ($(SKIP_RTAUDIO_FALLBACK),true)

JACK_FLAGS += -DHAVE_GETTIMEOFDAY

ifeq ($(MACOS),true)
JACK_LIBS += -framework CoreAudio -framework CoreFoundation -framework CoreMIDI
else ifeq ($(WINDOWS),true)
JACK_LIBS += -lole32 -lwinmm
# DirectSound
JACK_LIBS += -ldsound
# JACK_LIBS += -ldsound
# WASAPI
# JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid
JACK_LIBS += -lksuser -lmfplat -lmfuuid -lwmcodecdspuuid
else
ifeq ($(HAVE_PULSEAUDIO),true)
JACK_FLAGS += $(PULSEAUDIO_FLAGS)
@@ -147,7 +167,7 @@ ifeq ($(HAVE_CAIRO),true)
DGL_FLAGS += -DDGL_CAIRO -DHAVE_DGL
DGL_FLAGS += $(CAIRO_FLAGS)
DGL_LIBS += $(CAIRO_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-cairo.a
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-cairo.a
HAVE_DGL = true
else
HAVE_DGL = false
@@ -159,7 +179,7 @@ ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl.a
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl.a
HAVE_DGL = true
else
HAVE_DGL = false
@@ -171,7 +191,7 @@ ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-opengl3.a
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl3.a
HAVE_DGL = true
else
HAVE_DGL = false
@@ -183,7 +203,7 @@ ifeq ($(HAVE_VULKAN),true)
DGL_FLAGS += -DDGL_VULKAN -DHAVE_DGL
DGL_FLAGS += $(VULKAN_FLAGS)
DGL_LIBS += $(VULKAN_LIBS)
DGL_LIB = $(DPF_PATH)/build/libdgl-vulkan.a
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-vulkan.a
HAVE_DGL = true
else
HAVE_DGL = false
@@ -197,7 +217,7 @@ endif

ifeq ($(UI_TYPE),stub)
ifeq ($(HAVE_STUB),true)
DGL_LIB = $(DPF_PATH)/build/libdgl-stub.a
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-stub.a
HAVE_DGL = true
else
HAVE_DGL = false
@@ -209,12 +229,22 @@ DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm
# TODO split dsp and ui object build flags
BASE_FLAGS += $(DGL_FLAGS)

# ---------------------------------------------------------------------------------------------------------------------
# Set CLAP filename, either single binary or inside a bundle

ifeq ($(MACOS),true)
CLAP_FILENAME = $(NAME).clap/$(CLAP_BINARY_DIR)/$(NAME)
else ifeq ($(USE_CLAP_BUNDLE),true)
CLAP_FILENAME = $(NAME).clap/$(NAME).clap
else
CLAP_FILENAME = $(NAME).clap
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set VST2 filename, either single binary or inside a bundle

ifeq ($(MACOS),true)
VST2_CONTENTS = $(NAME).vst/Contents
VST2_FILENAME = $(VST2_CONTENTS)/MacOS/$(NAME)
VST2_FILENAME = $(NAME).vst/$(VST2_BINARY_DIR)/$(NAME)
else ifeq ($(USE_VST2_BUNDLE),true)
VST2_FILENAME = $(NAME).vst/$(NAME)$(LIB_EXT)
else
@@ -225,39 +255,22 @@ endif
# Set VST3 filename, see https://vst3sdk-doc.diatonic.jp/doc/vstinterfaces/vst3loc.html

ifeq ($(LINUX),true)
VST3_FILENAME = $(NAME).vst3/Contents/$(TARGET_PROCESSOR)-linux/$(NAME).so
VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME)$(LIB_EXT)
else ifeq ($(MACOS),true)
VST3_CONTENTS = $(NAME).vst3/Contents
VST3_FILENAME = $(VST3_CONTENTS)/MacOS/$(NAME)
else ifeq ($(WASM),true)
VST3_FILENAME = $(NAME).vst3/Contents/wasm/$(NAME).vst3
else ifeq ($(WINDOWS),true)
ifeq ($(CPU_I386),true)
VST3_FILENAME = $(NAME).vst3/Contents/x86-win/$(NAME).vst3
else ifeq ($(CPU_X86_64),true)
VST3_FILENAME = $(NAME).vst3/Contents/x86_64-win/$(NAME).vst3
endif
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set CLAP filename, either single binary or inside a bundle

ifeq ($(MACOS),true)
CLAP_CONTENTS = $(NAME).clap/Contents
CLAP_FILENAME = $(CLAP_CONTENTS)/MacOS/$(NAME)
else ifeq ($(USE_CLAP_BUNDLE),true)
CLAP_FILENAME = $(NAME).clap/$(NAME).clap
else
CLAP_FILENAME = $(NAME).clap
VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME)
else ifneq ($(VST3_BINARY_DIR),)
VST3_FILENAME = $(NAME).vst3/$(VST3_BINARY_DIR)/$(NAME).vst3
endif

# ---------------------------------------------------------------------------------------------------------------------
# Set plugin binary file targets

ifeq ($(MACOS),true)
ifeq ($(HAVE_DGL),true)
ifeq ($(MACOS)$(HAVE_DGL),truetrue)
MACOS_APP_BUNDLE = true
endif

ifeq ($(WINDOWS)$(HAVE_DGL),truetrue)
JACK_LIBS += -Wl,-subsystem,windows
endif

ifeq ($(MACOS_APP_BUNDLE),true)
@@ -266,6 +279,7 @@ jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist
else
jack = $(TARGET_DIR)/$(NAME)$(APP_EXT)
endif

ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT)
dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT)
dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT)
@@ -281,15 +295,10 @@ shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT)
static = $(TARGET_DIR)/$(NAME).a

ifeq ($(MACOS),true)
vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Info.plist
vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/PkgInfo
vst2files += $(TARGET_DIR)/$(VST2_CONTENTS)/Resources/empty.lproj
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Info.plist
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/PkgInfo
vst3files += $(TARGET_DIR)/$(VST3_CONTENTS)/Resources/empty.lproj
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Info.plist
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/PkgInfo
clapfiles += $(TARGET_DIR)/$(CLAP_CONTENTS)/Resources/empty.lproj
BUNDLE_RESOURCES = Info.plist PkgInfo Resources/empty.lproj
vst2files += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).vst/Contents/%)
vst3files += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).vst3/Contents/%)
clapfiles += $(BUNDLE_RESOURCES=%:$(TARGET_DIR)/$(NAME).clap/Contents/%)
endif

ifneq ($(HAVE_DGL),true)
@@ -364,37 +373,41 @@ all:
# Common

$(BUILD_DIR)/%.S.o: %.S
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@

$(BUILD_DIR)/%.c.o: %.c
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@

$(BUILD_DIR)/%.cc.o: %.cc
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(BUILD_DIR)/%.cpp.o: %.cpp
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(BUILD_DIR)/%.m.o: %.m
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -ObjC -c -o $@

$(BUILD_DIR)/%.mm.o: %.mm
-@mkdir -p "$(shell dirname $(BUILD_DIR)/$<)"
-@mkdir -p "$(shell dirname $@)"
@echo "Compiling $<"
$(SILENT)$(CC) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

clean:
rm -rf $(BUILD_DIR)
ifeq ($(DPF_BUILD_DIR),)
rm -rf $(BASE_PATH)/build-modgui/$(NAME)
rm -rf $(DPF_PATH)/build-modgui
endif
rm -rf $(TARGET_DIR)/$(NAME)
rm -rf $(TARGET_DIR)/$(NAME)-*
rm -rf $(TARGET_DIR)/$(NAME).lv2
@@ -405,44 +418,52 @@ clean:
# ---------------------------------------------------------------------------------------------------------------------
# DGL

$(DPF_PATH)/build/libdgl-cairo.a:
DGL_POSSIBLE_DEPS = \
$(DPF_PATH)/dgl/*.* \
$(DPF_PATH)/dgl/src/*.* \
$(DPF_PATH)/dgl/src/nanovg/*.* \
$(DPF_PATH)/dgl/src/pugl-extra/*.* \
$(DPF_PATH)/dgl/src/pugl-upstream/include/pugl/*.* \
$(DPF_PATH)/dgl/src/pugl-upstream/src/*.*

$(DGL_BUILD_DIR)/libdgl-cairo.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl cairo

$(DPF_PATH)/build/libdgl-opengl.a:
$(DGL_BUILD_DIR)/libdgl-opengl.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl opengl

$(DPF_PATH)/build/libdgl-opengl3.a:
$(DGL_BUILD_DIR)/libdgl-opengl3.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl opengl3

$(DPF_PATH)/build/libdgl-stub.a:
$(DGL_BUILD_DIR)/libdgl-stub.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl stub

$(DPF_PATH)/build/libdgl-vulkan.a:
$(DGL_BUILD_DIR)/libdgl-vulkan.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl vulkan

# ---------------------------------------------------------------------------------------------------------------------

$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES)
$(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoPluginMain.cpp ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@

$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES)
$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUIMain.cpp ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_$* -c -o $@

$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES)
$(BUILD_DIR)/DistrhoUI_macOS_%.mm.o: $(DPF_PATH)/distrho/DistrhoUI_macOS.mm $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUI_macOS.mm ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES)
$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoPluginMain.cpp (JACK)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_JACK $(JACK_FLAGS) -c -o $@

$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES)
$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUIMain.cpp (DSSI)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -DDISTRHO_PLUGIN_TARGET_DSSI $(LIBLO_FLAGS) -c -o $@
@@ -459,7 +480,7 @@ $(jack): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating JACK standalone for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(JACK_LIBS) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(JACK_LIBS) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# LADSPA
@@ -469,7 +490,7 @@ ladspa: $(ladspa_dsp)
$(ladspa_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating LADSPA plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_LADSPA) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_LADSPA) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# DSSI
@@ -481,12 +502,12 @@ dssi_ui: $(dssi_ui)
$(dssi_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating DSSI plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(SHARED) $(SYMBOLS_DSSI) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_DSSI) -o $@

$(dssi_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@echo "Creating DSSI UI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(LIBLO_LIBS) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# LV2
@@ -502,17 +523,111 @@ $(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2) -o $@

$(lv2_dsp): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(SHARED) $(SYMBOLS_LV2DSP) -o $@

$(lv2_ui): $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin UI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_LV2UI) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# LV2 modgui

ifeq ($(MODGUI_BUILD),true)
ifeq ($(MODGUI_CLASS_NAME),)
$(error MODGUI_CLASS_NAME undefined)
endif
endif

# clear all possible flags coming from DPF, while keeping any extra flags specified for this build
MODGUI_IGNORED_FLAGS = -fdata-sections
MODGUI_IGNORED_FLAGS += -ffast-math
MODGUI_IGNORED_FLAGS += -ffunction-sections
MODGUI_IGNORED_FLAGS += -fno-gnu-unique
MODGUI_IGNORED_FLAGS += -fprefetch-loop-arrays
MODGUI_IGNORED_FLAGS += -fvisibility=hidden
MODGUI_IGNORED_FLAGS += -fvisibility-inlines-hidden
MODGUI_IGNORED_FLAGS += -fPIC
MODGUI_IGNORED_FLAGS += -ldl
MODGUI_IGNORED_FLAGS += -mfpmath=sse
MODGUI_IGNORED_FLAGS += -msse
MODGUI_IGNORED_FLAGS += -msse2
MODGUI_IGNORED_FLAGS += -mtune=generic
MODGUI_IGNORED_FLAGS += -pipe
MODGUI_IGNORED_FLAGS += -std=gnu99
MODGUI_IGNORED_FLAGS += -std=gnu++11
MODGUI_IGNORED_FLAGS += -DDISTRHO_PLUGIN_MODGUI_CLASS_NAME='"$(MODGUI_CLASS_NAME)"'
MODGUI_IGNORED_FLAGS += -DDGL_OPENGL
MODGUI_IGNORED_FLAGS += -DGL_SILENCE_DEPRECATION=1
MODGUI_IGNORED_FLAGS += -DHAVE_ALSA
MODGUI_IGNORED_FLAGS += -DHAVE_DGL
MODGUI_IGNORED_FLAGS += -DHAVE_JACK
MODGUI_IGNORED_FLAGS += -DHAVE_LIBLO
MODGUI_IGNORED_FLAGS += -DHAVE_OPENGL
MODGUI_IGNORED_FLAGS += -DHAVE_PULSEAUDIO
MODGUI_IGNORED_FLAGS += -DHAVE_RTAUDIO
MODGUI_IGNORED_FLAGS += -DHAVE_SDL2
MODGUI_IGNORED_FLAGS += -DNDEBUG
MODGUI_IGNORED_FLAGS += -DPIC
MODGUI_IGNORED_FLAGS += -I.
MODGUI_IGNORED_FLAGS += -I$(DPF_PATH)/distrho
MODGUI_IGNORED_FLAGS += -I$(DPF_PATH)/dgl
MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/modduo-static/staging/usr/include
MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/modduox-static/staging/usr/include
MODGUI_IGNORED_FLAGS += -I$(MOD_WORKDIR)/moddwarf/staging/usr/include
MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/modduo-static/staging/usr/lib
MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/modduox-static/staging/usr/lib
MODGUI_IGNORED_FLAGS += -L$(MOD_WORKDIR)/moddwarf/staging/usr/lib
MODGUI_IGNORED_FLAGS += -MD
MODGUI_IGNORED_FLAGS += -MP
MODGUI_IGNORED_FLAGS += -O2
MODGUI_IGNORED_FLAGS += -O3
MODGUI_IGNORED_FLAGS += -Wall
MODGUI_IGNORED_FLAGS += -Wextra
MODGUI_IGNORED_FLAGS += -Wl,-O1,--as-needed,--gc-sections
MODGUI_IGNORED_FLAGS += -Wl,-dead_strip,-dead_strip_dylibs
MODGUI_IGNORED_FLAGS += -Wl,-x
MODGUI_IGNORED_FLAGS += -Wl,--gc-sections
MODGUI_IGNORED_FLAGS += -Wl,--no-undefined
MODGUI_IGNORED_FLAGS += -Wl,--strip-all
MODGUI_IGNORED_FLAGS += -Wno-deprecated-declarations
MODGUI_IGNORED_FLAGS += $(DGL_FLAGS)
MODGUI_CFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(BUILD_C_FLAGS)) -D__MOD_DEVICES__
MODGUI_CXXFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(BUILD_CXX_FLAGS)) -D__MOD_DEVICES__
MODGUI_LDFLAGS = $(filter-out $(MODGUI_IGNORED_FLAGS),$(LINK_FLAGS))

$(TARGET_DIR)/$(NAME).lv2/modgui/module.js: $(OBJS_UI) $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB)
-@mkdir -p $(shell dirname $@)
@echo "Creating LV2 plugin modgui for $(NAME)"
$(SILENT)$(CXX) $^ $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) \
-sALLOW_MEMORY_GROWTH -sALLOW_TABLE_GROWTH -sDISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 -sLZ4=1 \
-sMODULARIZE=1 -sMAIN_MODULE=2 \
-sEXPORTED_FUNCTIONS="['_malloc','_free','_modgui_init','_modgui_param_set','_modgui_patch_set','_modgui_cleanup']" \
-sEXPORTED_RUNTIME_METHODS=['addFunction','lengthBytesUTF8','stringToUTF8','UTF8ToString'] \
-sEXPORT_NAME="Module_$(MODGUI_CLASS_NAME)" \
-o $@

modgui:
$(MAKE) $(TARGET_DIR)/$(NAME).lv2/modgui/module.js \
EXE_WRAPPER= \
HAVE_OPENGL=true \
MODGUI_BUILD=true \
NOOPT=true \
PKG_CONFIG=false \
USE_GLES2=true \
AR=emar \
CC=emcc \
CXX=em++ \
CFLAGS="-O3 $(MODGUI_CFLAGS)" \
CXXFLAGS="-O3 $(MODGUI_CXXFLAGS)" \
LDFLAGS="-O3 $(MODGUI_LDFLAGS)"

.PHONY: modgui

# ---------------------------------------------------------------------------------------------------------------------
# VST2
@@ -526,7 +641,7 @@ $(vst2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating VST2 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST2) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# VST3
@@ -540,7 +655,7 @@ $(vst3): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating VST3 plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_VST3) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# CLAP
@@ -562,7 +677,7 @@ $(clap): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating CLAP plugin for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(CLAP_LIBS) $(SHARED) $(SYMBOLS_CLAP) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Shared
@@ -576,7 +691,7 @@ $(shared): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o
endif
-@mkdir -p $(shell dirname $@)
@echo "Creating shared library for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Static
@@ -600,15 +715,7 @@ $(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/I
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@

$(TARGET_DIR)/%.vst/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@

$(TARGET_DIR)/%.vst3/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@

$(TARGET_DIR)/%.clap/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist
$(TARGET_DIR)/%/Contents/Info.plist: $(DPF_PATH)/utils/plugin.bundle/Contents/Info.plist
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@



+ 90
- 49
dpf/cmake/DPF-plugin.cmake View File

@@ -75,10 +75,10 @@ include(CMakeParseArguments)
# `jack`, `ladspa`, `dssi`, `lv2`, `vst2`, `vst3`, `clap`
#
# `UI_TYPE` <type>
# the user interface type: `opengl` (default), `cairo`
# the user interface type: `opengl` (default), `cairo`, `external`
#
# `MONOLITHIC`
# build LV2 as a single binary for UI and DSP
# `FILES_COMMON` <file1>...<fileN>
# list of sources which are part of both DSP and UI
#
# `FILES_DSP` <file1>...<fileN>
# list of sources which are part of the DSP
@@ -87,13 +87,19 @@ include(CMakeParseArguments)
# list of sources which are part of the UI
# empty indicates the plugin does not have UI
#
# `FILES_COMMON` <file1>...<fileN>
# list of sources which are part of both DSP and UI
# `MODGUI_CLASS_NAME`
# class name to use for modgui builds
#
# `MONOLITHIC`
# build LV2 as a single binary for UI and DSP
#
# `NO_SHARED_RESOURCES`
# do not build DPF shared resources (fonts, etc)
#
function(dpf_add_plugin NAME)
set(options MONOLITHIC NO_SHARED_RESOURCES)
set(oneValueArgs UI_TYPE)
set(multiValueArgs TARGETS FILES_DSP FILES_UI FILES_COMMON)
set(oneValueArgs MODGUI_CLASS_NAME UI_TYPE)
set(multiValueArgs FILES_COMMON FILES_DSP FILES_UI TARGETS)
cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if("${_dpf_plugin_UI_TYPE}" STREQUAL "")
@@ -101,6 +107,7 @@ function(dpf_add_plugin NAME)
endif()

set(_dgl_library)
set(_dgl_external OFF)
if(_dpf_plugin_FILES_UI)
if(_dpf_plugin_UI_TYPE STREQUAL "cairo")
dpf__add_dgl_cairo("${_dpf_plugin_NO_SHARED_RESOURCES}")
@@ -108,11 +115,18 @@ function(dpf_add_plugin NAME)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl")
dpf__add_dgl_opengl("${_dpf_plugin_NO_SHARED_RESOURCES}")
set(_dgl_library dgl-opengl)
elseif(_dpf_plugin_UI_TYPE STREQUAL "external")
set(_dgl_external ON)
else()
message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}")
endif()
endif()

set(_dgl_has_ui OFF)
if(_dgl_library OR _dgl_external)
set(_dgl_has_ui ON)
endif()

###
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_COMMON)
dpf__ensure_sources_non_empty(_dpf_plugin_FILES_DSP)
@@ -123,21 +137,26 @@ function(dpf_add_plugin NAME)
target_include_directories("${NAME}" PUBLIC
"${DPF_ROOT_DIR}/distrho")

if(_dpf_plugin_MODGUI_CLASS_NAME)
target_compile_definitions("${NAME}" PUBLIC "DISTRHO_PLUGIN_MODGUI_CLASS_NAME=\"${_dpf_plugin_MODGUI_CLASS_NAME}\"")
endif()

if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
target_link_libraries("${NAME}" PRIVATE "dl")
endif()

if(_dgl_library)
if(_dgl_library AND NOT _dgl_external)
# make sure that all code will see DGL_* definitions
target_link_libraries("${NAME}" PUBLIC
"${_dgl_library}-definitions"
dgl-system-libs-definitions)
dgl-system-libs-definitions
dgl-system-libs)
endif()

dpf__add_static_library("${NAME}-dsp" ${_dpf_plugin_FILES_DSP})
target_link_libraries("${NAME}-dsp" PUBLIC "${NAME}")

if(_dgl_library)
if(_dgl_library AND NOT _dgl_external)
dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI})
target_link_libraries("${NAME}-ui" PUBLIC "${NAME}" ${_dgl_library})
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
@@ -145,6 +164,14 @@ function(dpf_add_plugin NAME)
endif()
# add the files containing Objective-C classes
dpf__add_plugin_specific_ui_sources("${NAME}-ui")
elseif(_dgl_external)
dpf__add_static_library("${NAME}-ui" ${_dpf_plugin_FILES_UI})
target_link_libraries("${NAME}-ui" PUBLIC "${NAME}")
if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
target_link_libraries("${NAME}-ui" PRIVATE "dl")
endif()
# add the files containing Objective-C classes
dpf__add_plugin_specific_ui_sources("${NAME}-ui")
else()
add_library("${NAME}-ui" INTERFACE)
endif()
@@ -152,19 +179,19 @@ function(dpf_add_plugin NAME)
###
foreach(_target ${_dpf_plugin_TARGETS})
if(_target STREQUAL "jack")
dpf__build_jack("${NAME}" "${_dgl_library}")
dpf__build_jack("${NAME}" "${_dgl_has_ui}")
elseif(_target STREQUAL "ladspa")
dpf__build_ladspa("${NAME}")
elseif(_target STREQUAL "dssi")
dpf__build_dssi("${NAME}" "${_dgl_library}")
dpf__build_dssi("${NAME}" "${_dgl_has_ui}")
elseif(_target STREQUAL "lv2")
dpf__build_lv2("${NAME}" "${_dgl_library}" "${_dpf_plugin_MONOLITHIC}")
dpf__build_lv2("${NAME}" "${_dgl_has_ui}" "${_dpf_plugin_MONOLITHIC}")
elseif(_target STREQUAL "vst2")
dpf__build_vst2("${NAME}" "${_dgl_library}")
dpf__build_vst2("${NAME}" "${_dgl_has_ui}")
elseif(_target STREQUAL "vst3")
dpf__build_vst3("${NAME}" "${_dgl_library}")
dpf__build_vst3("${NAME}" "${_dgl_has_ui}")
elseif(_target STREQUAL "clap")
dpf__build_clap("${NAME}" "${_dgl_library}")
dpf__build_clap("${NAME}" "${_dgl_has_ui}")
else()
message(FATAL_ERROR "Unrecognized target type for plugin: ${_target}")
endif()
@@ -184,26 +211,27 @@ endfunction()
#
# Add build rules for a JACK/Standalone program.
#
function(dpf__build_jack NAME DGL_LIBRARY)
function(dpf__build_jack NAME HAS_UI)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_executable("${NAME}-jack" ${_no_srcs})
dpf__add_plugin_main("${NAME}-jack" "jack")
dpf__add_ui_main("${NAME}-jack" "jack" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-jack" "jack" "${HAS_UI}")
target_link_libraries("${NAME}-jack" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-jack" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}")

target_compile_definitions("${NAME}" PUBLIC "HAVE_JACK")
target_compile_definitions("${NAME}-jack" PRIVATE "HAVE_GETTIMEOFDAY")

find_package(PkgConfig)
pkg_check_modules(SDL2 "sdl2")
if(SDL2_FOUND)
target_compile_definitions("${NAME}" PUBLIC "HAVE_SDL2")
target_include_directories("${NAME}-jack" PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries("${NAME}-jack" PRIVATE ${SDL2_LIBRARIES})
dpf__target_link_directories("${NAME}-jack" ${SDL2_LIBRARY_DIRS})
target_include_directories("${NAME}-jack" PRIVATE ${SDL2_STATIC_INCLUDE_DIRS})
target_link_libraries("${NAME}-jack" PRIVATE ${SDL2_STATIC_LIBRARIES})
dpf__target_link_directories("${NAME}-jack" "${SDL2_STATIC_LIBRARY_DIRS}")
endif()

if(APPLE OR WIN32)
@@ -216,13 +244,13 @@ function(dpf__build_jack NAME DGL_LIBRARY)
target_compile_definitions("${NAME}" PUBLIC "HAVE_ALSA")
target_include_directories("${NAME}-jack" PRIVATE ${ALSA_INCLUDE_DIRS})
target_link_libraries("${NAME}-jack" PRIVATE ${ALSA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
dpf__target_link_directories("${NAME}-jack" ${ALSA_LIBRARY_DIRS})
dpf__target_link_directories("${NAME}-jack" "${ALSA_LIBRARY_DIRS}")
endif()
if(PULSEAUDIO_FOUND)
target_compile_definitions("${NAME}" PUBLIC "HAVE_PULSEAUDIO")
target_include_directories("${NAME}-jack" PRIVATE ${PULSEAUDIO_INCLUDE_DIRS})
target_link_libraries("${NAME}-jack" PRIVATE ${PULSEAUDIO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
dpf__target_link_directories("${NAME}-jack" ${PULSEAUDIO_LIBRARY_DIRS})
dpf__target_link_directories("${NAME}-jack" "${PULSEAUDIO_LIBRARY_DIRS}")
endif()
if(ALSA_FOUND OR PULSEAUDIO_FOUND)
target_compile_definitions("${NAME}" PUBLIC "HAVE_RTAUDIO")
@@ -239,7 +267,10 @@ function(dpf__build_jack NAME DGL_LIBRARY)
"${APPLE_COREFOUNDATION_FRAMEWORK}"
"${APPLE_COREMIDI_FRAMEWORK}")
elseif(WIN32)
target_link_libraries("${NAME}-jack" PRIVATE "dsound" "ole32" "winmm")
target_link_libraries("${NAME}-jack" PRIVATE "ksuser" "mfplat" "mfuuid" "ole32" "winmm" "wmcodecdspuuid")
if(HAS_UI AND MINGW)
set_target_properties("${NAME}-jack" PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
endif()
endfunction()

@@ -267,7 +298,7 @@ endfunction()
#
# Add build rules for a DSSI plugin.
#
function(dpf__build_dssi NAME DGL_LIBRARY)
function(dpf__build_dssi NAME HAS_UI)
find_package(PkgConfig)
pkg_check_modules(LIBLO "liblo")
if(NOT LIBLO_FOUND)
@@ -288,9 +319,9 @@ function(dpf__build_dssi NAME DGL_LIBRARY)
OUTPUT_NAME "${NAME}-dssi"
PREFIX "")

if(DGL_LIBRARY)
if(HAS_UI)
dpf__add_executable("${NAME}-dssi-ui" ${_no_srcs})
dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-dssi-ui" "dssi" "${HAS_UI}")
target_link_libraries("${NAME}-dssi-ui" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-dssi-ui" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}-dssi/$<0:>"
@@ -299,7 +330,7 @@ function(dpf__build_dssi NAME DGL_LIBRARY)
target_compile_definitions("${NAME}" PUBLIC "HAVE_LIBLO")
target_include_directories("${NAME}-dssi-ui" PRIVATE ${LIBLO_INCLUDE_DIRS})
target_link_libraries("${NAME}-dssi-ui" PRIVATE ${LIBLO_LIBRARIES})
dpf__target_link_directories("${NAME}-dssi-ui" ${LIBLO_LIBRARY_DIRS})
dpf__target_link_directories("${NAME}-dssi-ui" "${LIBLO_LIBRARY_DIRS}")
endif()
endfunction()

@@ -308,12 +339,12 @@ endfunction()
#
# Add build rules for an LV2 plugin.
#
function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC)
function(dpf__build_lv2 NAME HAS_UI MONOLITHIC)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-lv2" ${_no_srcs})
dpf__add_plugin_main("${NAME}-lv2" "lv2")
if(DGL_LIBRARY AND MONOLITHIC)
if(HAS_UI AND MONOLITHIC)
dpf__set_module_export_list("${NAME}-lv2" "lv2")
else()
dpf__set_module_export_list("${NAME}-lv2" "lv2-dsp")
@@ -325,15 +356,15 @@ function(dpf__build_lv2 NAME DGL_LIBRARY MONOLITHIC)
OUTPUT_NAME "${NAME}_dsp"
PREFIX "")

if(DGL_LIBRARY)
if(HAS_UI)
if(MONOLITHIC)
dpf__add_ui_main("${NAME}-lv2" "lv2" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-lv2" "lv2" "${HAS_UI}")
target_link_libraries("${NAME}-lv2" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-lv2" PROPERTIES
OUTPUT_NAME "${NAME}")
else()
dpf__add_module("${NAME}-lv2-ui" ${_no_srcs})
dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-lv2-ui" "lv2" "${HAS_UI}")
dpf__set_module_export_list("${NAME}-lv2-ui" "lv2-ui")
target_link_libraries("${NAME}-lv2-ui" PRIVATE "${NAME}-ui")
set_target_properties("${NAME}-lv2-ui" PROPERTIES
@@ -361,12 +392,12 @@ endfunction()
#
# Add build rules for a VST2 plugin.
#
function(dpf__build_vst2 NAME DGL_LIBRARY)
function(dpf__build_vst2 NAME HAS_UI)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-vst2" ${_no_srcs})
dpf__add_plugin_main("${NAME}-vst2" "vst2")
dpf__add_ui_main("${NAME}-vst2" "vst2" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-vst2" "vst2" "${HAS_UI}")
dpf__set_module_export_list("${NAME}-vst2" "vst2")
target_link_libraries("${NAME}-vst2" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-vst2" PROPERTIES
@@ -439,14 +470,14 @@ endfunction()
#
# Add build rules for a VST3 plugin.
#
function(dpf__build_vst3 NAME DGL_LIBRARY)
function(dpf__build_vst3 NAME HAS_UI)
dpf__determine_vst3_package_architecture(vst3_arch)

dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-vst3" ${_no_srcs})
dpf__add_plugin_main("${NAME}-vst3" "vst3")
dpf__add_ui_main("${NAME}-vst3" "vst3" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-vst3" "vst3" "${HAS_UI}")
dpf__set_module_export_list("${NAME}-vst3" "vst3")
target_link_libraries("${NAME}-vst3" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-vst3" PROPERTIES
@@ -481,12 +512,12 @@ endfunction()
#
# Add build rules for a VST2 plugin.
#
function(dpf__build_clap NAME DGL_LIBRARY)
function(dpf__build_clap NAME HAS_UI)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_module("${NAME}-clap" ${_no_srcs})
dpf__add_plugin_main("${NAME}-clap" "clap")
dpf__add_ui_main("${NAME}-clap" "clap" "${DGL_LIBRARY}")
dpf__add_ui_main("${NAME}-clap" "clap" "${HAS_UI}")
dpf__set_module_export_list("${NAME}-clap" "clap")
target_link_libraries("${NAME}-clap" PRIVATE "${NAME}-dsp" "${NAME}-ui")
set_target_properties("${NAME}-clap" PROPERTIES
@@ -532,6 +563,7 @@ function(dpf__add_dgl_cairo NO_SHARED_RESOURCES)
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"
"${DPF_ROOT_DIR}/dgl/src/Layout.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp"
@@ -597,6 +629,7 @@ function(dpf__add_dgl_opengl NO_SHARED_RESOURCES)
"${DPF_ROOT_DIR}/dgl/src/Geometry.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBase.cpp"
"${DPF_ROOT_DIR}/dgl/src/ImageBaseWidgets.cpp"
"${DPF_ROOT_DIR}/dgl/src/Layout.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidget.cpp"
"${DPF_ROOT_DIR}/dgl/src/SubWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidget.cpp"
@@ -661,34 +694,42 @@ function(dpf__add_dgl_system_libs)
endif()
add_library(dgl-system-libs INTERFACE)
add_library(dgl-system-libs-definitions INTERFACE)
if(HAIKU)
target_link_libraries(dgl-system-libs INTERFACE "be")
elseif(WIN32)
target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32")
elseif(APPLE)
if(APPLE)
find_library(APPLE_COCOA_FRAMEWORK "Cocoa")
find_library(APPLE_COREVIDEO_FRAMEWORK "CoreVideo")
target_link_libraries(dgl-system-libs INTERFACE "${APPLE_COCOA_FRAMEWORK}" "${APPLE_COREVIDEO_FRAMEWORK}")
elseif(EMSCRIPTEN)
elseif(HAIKU)
target_link_libraries(dgl-system-libs INTERFACE "be")
elseif(WIN32)
target_link_libraries(dgl-system-libs INTERFACE "gdi32" "comdlg32")
else()
find_package(PkgConfig)
pkg_check_modules(DBUS "dbus-1")
if(DBUS_FOUND)
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_DBUS")
target_include_directories(dgl-system-libs INTERFACE "${DBUS_INCLUDE_DIRS}")
target_link_libraries(dgl-system-libs INTERFACE "${DBUS_LIBRARIES}")
endif()
find_package(X11 REQUIRED)
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11")
target_include_directories(dgl-system-libs INTERFACE "${X11_INCLUDE_DIR}")
target_link_libraries(dgl-system-libs INTERFACE "${X11_X11_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_X11")
if(X11_Xcursor_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XCURSOR")
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xcursor_LIB}")
endif()
if(X11_Xext_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XEXT")
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xext_LIB}")
endif()
if(X11_Xrandr_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XRANDR")
target_link_libraries(dgl-system-libs INTERFACE "${X11_Xrandr_LIB}")
endif()
if(X11_XSync_FOUND)
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}")
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_XSYNC")
target_link_libraries(dgl-system-libs INTERFACE "${X11_XSync_LIB}")
endif()
endif()



+ 5
- 0
dpf/dgl/Color.hpp View File

@@ -94,6 +94,11 @@ struct Color {
*/
Color plus(float value) const noexcept;

/**
Create a new color based on this one but colors inverted.
*/
Color invert() const noexcept;

/**
Create a color specified by hue, saturation and lightness.
Values must in [0..1] range.


+ 1
- 1
dpf/dgl/EventHandlers.hpp View File

@@ -156,7 +156,7 @@ public:
bool scrollEvent(const Widget::ScrollEvent& ev);

protected:
State getState() const noexcept;
State getState() const noexcept;

private:
struct PrivateData;


+ 89
- 81
dpf/dgl/Makefile View File

@@ -26,58 +26,66 @@ endif

# ---------------------------------------------------------------------------------------------------------------------

ifeq ($(MODGUI_BUILD),true)
BUILD_DIR_SUFFIX = -modgui
endif

BUILD_DIR = ../build$(BUILD_DIR_SUFFIX)

# ---------------------------------------------------------------------------------------------------------------------

OBJS_common = \
../build/dgl/Application.cpp.o \
../build/dgl/ApplicationPrivateData.cpp.o \
../build/dgl/Color.cpp.o \
../build/dgl/EventHandlers.cpp.o \
../build/dgl/Geometry.cpp.o \
../build/dgl/ImageBase.cpp.o \
../build/dgl/ImageBaseWidgets.cpp.o \
../build/dgl/Layout.cpp.o \
../build/dgl/Resources.cpp.o \
../build/dgl/SubWidget.cpp.o \
../build/dgl/SubWidgetPrivateData.cpp.o \
../build/dgl/TopLevelWidget.cpp.o \
../build/dgl/TopLevelWidgetPrivateData.cpp.o \
../build/dgl/Widget.cpp.o \
../build/dgl/WidgetPrivateData.cpp.o \
../build/dgl/Window.cpp.o \
../build/dgl/WindowPrivateData.cpp.o
$(BUILD_DIR)/dgl/Application.cpp.o \
$(BUILD_DIR)/dgl/ApplicationPrivateData.cpp.o \
$(BUILD_DIR)/dgl/Color.cpp.o \
$(BUILD_DIR)/dgl/EventHandlers.cpp.o \
$(BUILD_DIR)/dgl/Geometry.cpp.o \
$(BUILD_DIR)/dgl/ImageBase.cpp.o \
$(BUILD_DIR)/dgl/ImageBaseWidgets.cpp.o \
$(BUILD_DIR)/dgl/Layout.cpp.o \
$(BUILD_DIR)/dgl/Resources.cpp.o \
$(BUILD_DIR)/dgl/SubWidget.cpp.o \
$(BUILD_DIR)/dgl/SubWidgetPrivateData.cpp.o \
$(BUILD_DIR)/dgl/TopLevelWidget.cpp.o \
$(BUILD_DIR)/dgl/TopLevelWidgetPrivateData.cpp.o \
$(BUILD_DIR)/dgl/Widget.cpp.o \
$(BUILD_DIR)/dgl/WidgetPrivateData.cpp.o \
$(BUILD_DIR)/dgl/Window.cpp.o \
$(BUILD_DIR)/dgl/WindowPrivateData.cpp.o

# ---------------------------------------------------------------------------------------------------------------------

OBJS_cairo = $(OBJS_common) \
../build/dgl/Cairo.cpp.cairo.o
$(BUILD_DIR)/dgl/Cairo.cpp.cairo.o

ifeq ($(MACOS),true)
OBJS_cairo += ../build/dgl/pugl.mm.cairo.o
OBJS_cairo += $(BUILD_DIR)/dgl/pugl.mm.cairo.o
else
OBJS_cairo += ../build/dgl/pugl.cpp.cairo.o
OBJS_cairo += $(BUILD_DIR)/dgl/pugl.cpp.cairo.o
endif

# ---------------------------------------------------------------------------------------------------------------------

OBJS_opengl = $(OBJS_common) \
../build/dgl/OpenGL.cpp.opengl.o \
../build/dgl/NanoVG.cpp.opengl.o
$(BUILD_DIR)/dgl/OpenGL.cpp.opengl.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.opengl.o

ifeq ($(MACOS),true)
OBJS_opengl += ../build/dgl/pugl.mm.opengl.o
OBJS_opengl += $(BUILD_DIR)/dgl/pugl.mm.opengl.o
else
OBJS_opengl += ../build/dgl/pugl.cpp.opengl.o
OBJS_opengl += $(BUILD_DIR)/dgl/pugl.cpp.opengl.o
endif

# ---------------------------------------------------------------------------------------------------------------------

OBJS_opengl3 = $(OBJS_common) \
../build/dgl/OpenGL.cpp.opengl3.o \
../build/dgl/NanoVG.cpp.opengl3.o
$(BUILD_DIR)/dgl/OpenGL.cpp.opengl3.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.opengl3.o

ifeq ($(MACOS),true)
OBJS_opengl3 += ../build/dgl/pugl.mm.opengl3.o
OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.mm.opengl3.o
else
OBJS_opengl3 += ../build/dgl/pugl.cpp.opengl3.o
OBJS_opengl3 += $(BUILD_DIR)/dgl/pugl.cpp.opengl3.o
endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -85,170 +93,170 @@ endif
OBJS_stub = $(OBJS_common)

ifeq ($(MACOS),true)
OBJS_stub += ../build/dgl/pugl.mm.o
OBJS_stub += $(BUILD_DIR)/dgl/pugl.mm.o
else
OBJS_stub += ../build/dgl/pugl.cpp.o
OBJS_stub += $(BUILD_DIR)/dgl/pugl.cpp.o
endif

# ---------------------------------------------------------------------------------------------------------------------

OBJS_vulkan = $(OBJS_common) \
../build/dgl/Vulkan.cpp.vulkan.o
$(BUILD_DIR)/dgl/Vulkan.cpp.vulkan.o

ifeq ($(MACOS),true)
OBJS_vulkan += ../build/dgl/pugl.mm.vulkan.o
OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.mm.vulkan.o
else
OBJS_vulkan += ../build/dgl/pugl.cpp.vulkan.o
OBJS_vulkan += $(BUILD_DIR)/dgl/pugl.cpp.vulkan.o
endif

# ---------------------------------------------------------------------------------------------------------------------

ifeq ($(HAVE_CAIRO),true)
TARGETS += ../build/libdgl-cairo.a
TARGETS += $(BUILD_DIR)/libdgl-cairo.a
endif

ifeq ($(HAVE_OPENGL),true)
TARGETS += ../build/libdgl-opengl.a
TARGETS += $(BUILD_DIR)/libdgl-opengl.a
# Compat name, to be removed soon
TARGETS += ../build/libdgl.a
TARGETS += $(BUILD_DIR)/libdgl.a
endif

ifeq ($(HAVE_STUB),true)
TARGETS += ../build/libdgl-stub.a
TARGETS += $(BUILD_DIR)/libdgl-stub.a
endif

ifeq ($(HAVE_VULKAN),true)
TARGETS += ../build/libdgl-vulkan.a
TARGETS += $(BUILD_DIR)/libdgl-vulkan.a
endif

# ---------------------------------------------------------------------------------------------------------------------

all: $(TARGETS)

cairo: ../build/libdgl-cairo.a
opengl: ../build/libdgl-opengl.a
opengl3: ../build/libdgl-opengl3.a
stub: ../build/libdgl-stub.a
vulkan: ../build/libdgl-vulkan.a
cairo: $(BUILD_DIR)/libdgl-cairo.a
opengl: $(BUILD_DIR)/libdgl-opengl.a
opengl3: $(BUILD_DIR)/libdgl-opengl3.a
stub: $(BUILD_DIR)/libdgl-stub.a
vulkan: $(BUILD_DIR)/libdgl-vulkan.a

# ---------------------------------------------------------------------------------------------------------------------

../build/libdgl-cairo.a: $(OBJS_cairo)
-@mkdir -p ../build
$(BUILD_DIR)/libdgl-cairo.a: $(OBJS_cairo)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-cairo.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-opengl.a: $(OBJS_opengl)
-@mkdir -p ../build
$(BUILD_DIR)/libdgl-opengl.a: $(OBJS_opengl)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-opengl.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-opengl3.a: $(OBJS_opengl3)
-@mkdir -p ../build
$(BUILD_DIR)/libdgl-opengl3.a: $(OBJS_opengl3)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-opengl3.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-stub.a: $(OBJS_stub)
-@mkdir -p ../build
$(BUILD_DIR)/libdgl-stub.a: $(OBJS_stub)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-stub.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

../build/libdgl-vulkan.a: $(OBJS_vulkan)
-@mkdir -p ../build
$(BUILD_DIR)/libdgl-vulkan.a: $(OBJS_vulkan)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-vulkan.a"
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

# Compat name, to be removed soon
../build/libdgl.a: ../build/libdgl-opengl.a
$(BUILD_DIR)/libdgl.a: $(BUILD_DIR)/libdgl-opengl.a
@echo "Symlinking libdgl.a"
$(SILENT)ln -sf $< $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.c.o: src/%.c
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.c.o: src/%.c
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CC) $< $(BUILD_C_FLAGS) -c -o $@

../build/dgl/%.cpp.o: src/%.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.cpp.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

../build/dgl/%.mm.o: src/%.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.mm.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/pugl.cpp.o: src/pugl.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/pugl.cpp.o: src/pugl.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -o $@

../build/dgl/pugl.mm.o: src/pugl.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/pugl.mm.o: src/pugl.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.cairo.o: src/%.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.cpp.cairo.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -o $@

../build/dgl/%.mm.cairo.o: src/%.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.mm.cairo.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (Cairo variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(CAIRO_FLAGS) -DDGL_CAIRO -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.opengl.o: src/%.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.cpp.opengl.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -o $@

../build/dgl/%.mm.opengl.o: src/%.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.mm.opengl.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.opengl3.o: src/%.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.cpp.opengl3.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -o $@

../build/dgl/%.mm.opengl3.o: src/%.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.mm.opengl3.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

../build/dgl/%.cpp.vulkan.o: src/%.cpp
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.cpp.vulkan.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -o $@

../build/dgl/%.mm.vulkan.o: src/%.mm
-@mkdir -p ../build/dgl
$(BUILD_DIR)/dgl/%.mm.vulkan.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (Vulkan variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(VULKAN_FLAGS) -DDGL_VULKAN -c -ObjC++ -o $@

# ---------------------------------------------------------------------------------------------------------------------

clean:
rm -rf ../build/dgl ../build/libdgl*.*
rm -rf $(BUILD_DIR)/dgl $(BUILD_DIR)/libdgl*.*

debug:
$(MAKE) DEBUG=true


+ 4
- 2
dpf/dgl/src/ApplicationPrivateData.cpp View File

@@ -67,9 +67,11 @@ Application::PrivateData::PrivateData(const bool standalone)
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

puglSetWorldHandle(world, this);
#ifndef __EMSCRIPTEN__
#ifdef __EMSCRIPTEN__
puglSetClassName(world, "canvas");
#else
puglSetClassName(world, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));
#endif
#endif
}

Application::PrivateData::~PrivateData()


+ 9
- 0
dpf/dgl/src/Color.cpp View File

@@ -163,6 +163,15 @@ Color Color::plus(const float value) const noexcept
return color;
}

Color Color::invert() const noexcept
{
Color color(*this);
color.red = 1.f - color.red;
color.green = 1.f - color.green;
color.blue = 1.f - color.blue;
return color;
}

Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
{
float m1, m2;


+ 1
- 1
dpf/dgl/src/EventHandlers.cpp View File

@@ -441,7 +441,7 @@ struct KnobEventHandler::PrivateData {
}

if (d_isZero(movDiff))
return false;
return true;

const float divisor = (ev.mod & kModifierControl) ? accel * 10.f : accel;
valueTmp += (maximum - minimum) / divisor * movDiff;


+ 0
- 492
dpf/dgl/src/pugl-custom/pugl.h View File

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

+ 0
- 441
dpf/dgl/src/pugl-custom/pugl_haiku.cpp View File

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

+ 0
- 263
dpf/dgl/src/pugl-custom/pugl_internal.h View File

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

+ 0
- 974
dpf/dgl/src/pugl-custom/pugl_osx.m View File

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

+ 0
- 565
dpf/dgl/src/pugl-custom/pugl_win.cpp View File

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

+ 0
- 739
dpf/dgl/src/pugl-custom/pugl_x11.c View File

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

+ 0
- 29
dpf/dgl/src/pugl-extra/extras.c View File

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

+ 0
- 50
dpf/dgl/src/pugl-extra/extras.h View File

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

+ 342
- 21
dpf/dgl/src/pugl-extra/haiku.cpp View File

@@ -1,28 +1,348 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-2020 Filipe Coelho <falktx@falktx.com>

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file mac.cpp HaikuOS implementation.
*/
// Copyright 2012-2022 David Robillard <d@drobilla.net>
// Copyright 2021-2022 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

#include "haiku.h"

#include "pugl/detail/implementation.h"
#include "../pugl-upstream/src/internal.h"

class PuglHaikuView : public BView
{
PuglView* const view;

public:
PuglHaikuView(PuglView* const v)
: BView(NULL, B_FULL_UPDATE_ON_RESIZE|B_FRAME_EVENTS|B_NAVIGABLE|B_INPUT_METHOD_AWARE),
view(v) {}

protected:
void GetPreferredSize(float* width, float* height) override
{
d_stdout("%s %i", __func__, __LINE__);
if (width != nullptr)
*width = view->frame.width;
if (height != nullptr)
*height = view->frame.height;
d_stdout("%s %i", __func__, __LINE__);
}
};

class PuglHaikuWindow : public BWindow
{
PuglView* const view;

public:
PuglHaikuWindow(PuglView* const v)
: BWindow(BRect(1.0f), "DPF-Window", B_TITLED_WINDOW, 0x0),
view(v) {}

// protected:
// bool QuitRequested() override
// {
// d_stdout("%s %i", __func__, __LINE__);
// if (puglView->closeFunc) {
// puglView->closeFunc(puglView);
// puglView->redisplay = false;
// }
// needsQuit = false;
// d_stdout("%s %i", __func__, __LINE__);
// return true;
// }
};

BApplication* s_app = NULL;

PuglWorldInternals*
puglInitWorldInternals(const PuglWorldType type, const PuglWorldFlags flags)
{
if (!s_app) {
status_t status;
s_app = new BApplication("application/x-vnd.pugl-application", &status);
if (status != B_OK) {
delete s_app;
return NULL;
}
}

PuglWorldInternals* impl =
(PuglWorldInternals*)calloc(1, sizeof(PuglWorldInternals));

impl->app = s_app;
return impl;
}

void*
puglGetNativeWorld(PuglWorld* const world)
{
return world->impl->app;
}

PuglInternals*
puglInitViewInternals(PuglWorld* const world)
{
PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));

return impl;
}

PuglStatus
puglRealize(PuglView* const view)
{
PuglInternals* const impl = view->impl;
PuglStatus st = PUGL_SUCCESS;

// Ensure that we're unrealized and that a reasonable backend has been set
if (impl->view) {
return PUGL_FAILURE;
}
if (!view->backend || !view->backend->configure) {
return PUGL_BAD_BACKEND;
}

// Set the size to the default if it has not already been set
if (view->frame.width <= 0.0 || view->frame.height <= 0.0) {
const PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
if (!defaultSize.width || !defaultSize.height) {
return PUGL_BAD_CONFIGURATION;
}

view->frame.width = defaultSize.width;
view->frame.height = defaultSize.height;
}

// Center top-level windows if a position has not been set
if (!view->parent && !view->frame.x && !view->frame.y) {
// TODO
}

if (!view->parent) {
impl->window = new PuglHaikuWindow(view);
impl->window->Lock();
}

impl->view = new PuglHaikuView(view);

if (view->parent) {
BView* const pview = (BView*)view->parent;
pview->AddChild(impl->view);
} else {
impl->window->AddChild(impl->view);
}

// Configure and create the backend
if ((st = view->backend->configure(view)) || (st = view->backend->create(view))) {
view->backend->destroy(view);
return st;
}

if (view->title) {
puglSetWindowTitle(view, view->title);
}

if (view->transientParent) {
puglSetTransientParent(view, view->transientParent);
}

puglDispatchSimpleEvent(view, PUGL_CREATE);

if (impl->window) {
impl->window->Unlock();
}

return PUGL_SUCCESS;
}

PuglStatus
puglShow(PuglView* const view)
{
PuglInternals* const impl = view->impl;
if (impl->window) {
impl->window->Show();
} else {
impl->view->Show();
}
return PUGL_SUCCESS;
}

PuglStatus
puglHide(PuglView* const view)
{
PuglInternals* const impl = view->impl;
if (impl->window) {
impl->window->Hide();
} else {
impl->view->Hide();
}
return PUGL_SUCCESS;
}

void
puglFreeViewInternals(PuglView* const view)
{
if (view && view->impl) {
PuglInternals* const impl = view->impl;
if (view->backend) {
view->backend->destroy(view);
}
if (impl->view) {
if (impl->window) {
impl->window->RemoveChild(impl->view);
}
delete impl->view;
delete impl->window;
}
free(impl);
}
}

void
puglFreeWorldInternals(PuglWorld* const world)
{
// if (world->impl->app != nullptr && world->impl->app->CountWindows() == 0) {
// delete world->impl->app;
// s_app = NULL;
// }
free(world->impl);
}

PuglStatus
puglGrabFocus(PuglView*)
{
return PUGL_UNSUPPORTED;
}

double
puglGetScaleFactor(const PuglView* const view)
{
return 1.0;
}

double
puglGetTime(const PuglWorld* const world)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) -
world->startTime;
}

PuglStatus
puglUpdate(PuglWorld* const world, const double timeout)
{
return PUGL_UNSUPPORTED;
}

PuglStatus
puglPostRedisplay(PuglView* const view)
{
return PUGL_UNSUPPORTED;
}

PuglStatus
puglPostRedisplayRect(PuglView* const view, const PuglRect rect)
{
return PUGL_UNSUPPORTED;
}

PuglNativeView
puglGetNativeView(PuglView* const view)
{
return 0;
}

PuglStatus
puglSetWindowTitle(PuglView* const view, const char* const title)
{
puglSetString(&view->title, title);
return PUGL_UNSUPPORTED;
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
{
view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;
return PUGL_SUCCESS;
}

PuglStatus
puglStartTimer(PuglView* const view, const uintptr_t id, const double timeout)
{
return PUGL_UNSUPPORTED;
}

PuglStatus
puglStopTimer(PuglView* const view, const uintptr_t id)
{
return PUGL_UNSUPPORTED;
}

PuglStatus
puglPaste(PuglView* const view)
{
return PUGL_UNSUPPORTED;
}

PuglStatus
puglAcceptOffer(PuglView* const view,
const PuglDataOfferEvent* const offer,
const uint32_t typeIndex)
{
return PUGL_UNSUPPORTED;
}

uint32_t
puglGetNumClipboardTypes(const PuglView* const view)
{
return 0u;
}

const char*
puglGetClipboardType(const PuglView* const view, const uint32_t typeIndex)
{
return NULL;
}

const void*
puglGetClipboard(PuglView* const view,
const uint32_t typeIndex,
size_t* const len)
{
return NULL;
}

PuglStatus
puglSetClipboard(PuglView* const view,
const char* const type,
const void* const data,
const size_t len)
{
return PUGL_FAILURE;
}

PuglStatus
puglSetCursor(PuglView* const view, const PuglCursor cursor)
{
return PUGL_FAILURE;
}

PuglStatus
puglSetTransientParent(PuglView* const view, const PuglNativeView parent)
{
return PUGL_FAILURE;
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
{
return PUGL_FAILURE;
}

#if 0
PuglStatus
puglGrabFocus(PuglView* view)
{
@@ -79,3 +399,4 @@ void setVisible(const bool yesNo)
bView->Hide();
}
}
#endif

+ 12
- 22
dpf/dgl/src/pugl-extra/haiku.h View File

@@ -1,35 +1,25 @@
/*
Copyright 2012-2019 David Robillard <http://drobilla.net>
Copyright 2019-2020 Filipe Coelho <falktx@falktx.com>
// Copyright 2012-2022 David Robillard <d@drobilla.net>
// Copyright 2021-2022 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
#ifndef PUGL_SRC_HAIKU_H
#define PUGL_SRC_HAIKU_H

THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/**
@file haiku.h Shared definitions for HaikuOS implementation.
*/
#include "../pugl-upstream/src/types.h"

#include "pugl/pugl.h"

#include <Application.h>
#include <Window.h>
// using? interface/

struct PuglWorldInternalsImpl {
BApplication* app;
BApplication* app;
};

struct PuglInternalsImpl {
BViewType* view;
BWindow* window;
PuglSurface* surface;
BView* view;
BWindow* window;
};

#endif // PUGL_SRC_HAIKU_H

+ 87
- 0
dpf/dgl/src/pugl-extra/haiku_gl.cpp View File

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

+ 24
- 0
dpf/dgl/src/pugl-extra/haiku_stub.cpp View File

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

+ 0
- 48
dpf/dgl/src/pugl-extra/mac.m View File

@@ -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)];
}
}

+ 1141
- 0
dpf/dgl/src/pugl-extra/wasm.c
File diff suppressed because it is too large
View File


+ 45
- 0
dpf/dgl/src/pugl-extra/wasm.h View File

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

+ 228
- 0
dpf/dgl/src/pugl-extra/wasm_gl.c View File

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

+ 24
- 0
dpf/dgl/src/pugl-extra/wasm_stub.c View File

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

+ 0
- 118
dpf/dgl/src/pugl-extra/win.c View File

@@ -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);
}

+ 0
- 111
dpf/dgl/src/pugl-extra/x11.c View File

@@ -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);
}

+ 35
- 11
dpf/dgl/src/pugl.cpp View File

@@ -37,7 +37,14 @@
#include <cstring>
#include <ctime>

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
# include <Application.h>
# include <Window.h>
# ifdef DGL_OPENGL
# include <GL/gl.h>
# include <opengl/GLView.h>
# endif
#elif defined(DISTRHO_OS_MAC)
# import <Cocoa/Cocoa.h>
# include <dlfcn.h>
# include <mach/mach_time.h>
@@ -119,7 +126,13 @@ START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
# include "pugl-extra/haiku.cpp"
# include "pugl-extra/haiku_stub.cpp"
# ifdef DGL_OPENGL
# include "pugl-extra/haiku_gl.cpp"
# endif
#elif defined(DISTRHO_OS_MAC)
# ifndef DISTRHO_MACOS_NAMESPACE_MACRO
# define DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, SEP, INTERFACE) NS ## SEP ## INTERFACE
# define DISTRHO_MACOS_NAMESPACE_MACRO(NS, INTERFACE) DISTRHO_MACOS_NAMESPACE_MACRO_HELPER(NS, _, INTERFACE)
@@ -146,10 +159,10 @@ START_NAMESPACE_DGL
# endif
# pragma clang diagnostic pop
#elif defined(DISTRHO_OS_WASM)
# include "pugl-upstream/src/wasm.c"
# include "pugl-upstream/src/wasm_stub.c"
# include "pugl-extra/wasm.c"
# include "pugl-extra/wasm_stub.c"
# ifdef DGL_OPENGL
# include "pugl-upstream/src/wasm_gl.c"
# include "pugl-extra/wasm_gl.c"
# endif
#elif defined(DISTRHO_OS_WINDOWS)
# include "pugl-upstream/src/win.c"
@@ -237,7 +250,8 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view)

void puglRaiseWindow(PuglView* const view)
{
#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (NSWindow* const window = view->impl->window ? view->impl->window
: [view->impl->wrapperView window])
[window orderFrontRegardless];
@@ -257,7 +271,10 @@ void puglRaiseWindow(PuglView* const view)
double puglGetScaleFactorFromParent(const PuglView* const view)
{
const PuglNativeView parent = view->parent ? view->parent : view->transientParent ? view->transientParent : 0;
#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
// TODO
return 1.0;
#elif defined(DISTRHO_OS_MAC)
// some of these can return 0 as backingScaleFactor, pick the most relevant valid one
const NSWindow* possibleWindows[] = {
parent != 0 ? [(NSView*)parent window] : nullptr,
@@ -296,7 +313,8 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co
view->sizeHints[PUGL_FIXED_ASPECT].height = height;
}

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (view->impl->window)
{
PuglStatus status;
@@ -328,7 +346,8 @@ void puglSetResizable(PuglView* const view, const bool resizable)
{
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (PuglWindow* const window = view->impl->window)
{
const uint style = (NSClosableWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask)
@@ -361,7 +380,8 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height)
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->frame.width = static_cast<PuglSpan>(width);
view->sizeHints[PUGL_DEFAULT_SIZE].height = view->frame.height = static_cast<PuglSpan>(height);

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
// mostly matches upstream pugl, simplified
PuglInternals* const impl = view->impl;

@@ -456,7 +476,11 @@ void puglFallbackOnResize(PuglView* const view)

// --------------------------------------------------------------------------------------------------------------------

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)

// --------------------------------------------------------------------------------------------------------------------

#elif defined(DISTRHO_OS_MAC)

// --------------------------------------------------------------------------------------------------------------------
// macOS specific, add another view's window as child


+ 5
- 1
dpf/dgl/src/pugl.hpp View File

@@ -73,7 +73,11 @@ void puglOnDisplayPrepare(PuglView* view);
// DGL specific, build-specific fallback resize
void puglFallbackOnResize(PuglView* view);

#if defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)

// nothing here yet

#elif defined(DISTRHO_OS_MAC)

// macOS specific, add another view's window as child
PuglStatus puglMacOSAddChildWindow(PuglView* view, PuglView* child);


+ 1031
- 0
dpf/distrho/DistrhoDetails.hpp
File diff suppressed because it is too large
View File


+ 2
- 934
dpf/distrho/DistrhoPlugin.hpp View File

@@ -17,944 +17,12 @@
#ifndef DISTRHO_PLUGIN_HPP_INCLUDED
#define DISTRHO_PLUGIN_HPP_INCLUDED

#include "extra/String.hpp"
#include "DistrhoDetails.hpp"
#include "extra/LeakDetector.hpp"
#include "src/DistrhoPluginChecks.h"

START_NAMESPACE_DISTRHO

/* ------------------------------------------------------------------------------------------------------------
* Audio Port Hints */

/**
@defgroup AudioPortHints Audio Port Hints

Various audio port hints.
@see AudioPort::hints
@{
*/

/**
Audio port can be used as control voltage (LV2 and JACK standalone only).
*/
static const uint32_t kAudioPortIsCV = 0x1;

/**
Audio port should be used as sidechan (LV2 and VST3 only).
This hint should not be used with CV style ports.
@note non-sidechain audio ports must exist in the plugin if this flag is set.
*/
static const uint32_t kAudioPortIsSidechain = 0x2;

/**
CV port has bipolar range (-1 to +1, or -5 to +5 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasBipolarRange = 0x10;

/**
CV port has negative unipolar range (-1 to 0, or -10 to 0 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasNegativeUnipolarRange = 0x20;

/**
CV port has positive unipolar range (0 to +1, or 0 to +10 if scaled).
This is merely a hint to tell the host what value range to expect.
*/
static const uint32_t kCVPortHasPositiveUnipolarRange = 0x40;

/**
CV port has scaled range to match real values (-5 to +5v bipolar, +/-10 to 0v unipolar).
One other range flag is required if this flag is set.

When enabled, this makes the port a mod:CVPort, compatible with the MOD Devices platform.
*/
static const uint32_t kCVPortHasScaledRange = 0x80;

/**
CV port is optional, allowing hosts that do no CV ports to load the plugin.
When loaded in hosts that don't support CV, the float* buffer for this port will be null.
*/
static const uint32_t kCVPortIsOptional = 0x100;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* Parameter Hints */

/**
@defgroup ParameterHints Parameter Hints

Various parameter hints.
@see Parameter::hints
@{
*/

/**
Parameter is automatable (real-time safe).
@see Plugin::setParameterValue(uint32_t, float)
*/
static const uint32_t kParameterIsAutomatable = 0x01;

/** It was a typo, sorry.. */
DISTRHO_DEPRECATED_BY("kParameterIsAutomatable")
static const uint32_t kParameterIsAutomable = kParameterIsAutomatable;

/**
Parameter value is boolean.@n
It's always at either minimum or maximum value.
*/
static const uint32_t kParameterIsBoolean = 0x02;

/**
Parameter value is integer.
*/
static const uint32_t kParameterIsInteger = 0x04;

/**
Parameter value is logarithmic.
*/
static const uint32_t kParameterIsLogarithmic = 0x08;

/**
Parameter is of output type.@n
When unset, parameter is assumed to be of input type.

Parameter inputs are changed by the host and typically should not be changed by the plugin.@n
One exception is when changing programs, see Plugin::loadProgram().@n
The other exception is with parameter change requests, see Plugin::requestParameterValueChange().@n
Outputs are changed by the plugin and never modified by the host.

If you are targetting VST2, make sure to order your parameters so that all inputs are before any outputs.
*/
static const uint32_t kParameterIsOutput = 0x10;

/**
Parameter value is a trigger.@n
This means the value resets back to its default after each process/run call.@n
Cannot be used for output parameters.

@note Only officially supported under LV2. For other formats DPF simulates the behaviour.
*/
static const uint32_t kParameterIsTrigger = 0x20 | kParameterIsBoolean;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* State Hints */

/**
@defgroup StateHints State Hints

Various state hints.
@see State::hints
@{
*/

/**
State is visible and readable by hosts that support string-type plugin parameters.
*/
static const uint32_t kStateIsHostReadable = 0x01;

/**
State is writable by the host, allowing users to arbitrarily change the state.@n
For obvious reasons a writable state is also readable by the host.
*/
static const uint32_t kStateIsHostWritable = 0x02 | kStateIsHostReadable;

/**
State is a filename path instead of a regular string.@n
The readable and writable hints are required for filenames to work, and thus are automatically set.
*/
static const uint32_t kStateIsFilenamePath = 0x04 | kStateIsHostWritable;

/**
State is a base64 encoded string.
*/
static const uint32_t kStateIsBase64Blob = 0x08;

/**
State is for Plugin/DSP side only, meaning there is never a need to notify the UI when it changes.
*/
static const uint32_t kStateIsOnlyForDSP = 0x10;

/**
State is for UI side only.@n
If the DSP and UI are separate and the UI is not available, this property won't be saved.
*/
static const uint32_t kStateIsOnlyForUI = 0x20;

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* Base Plugin structs */

/**
@defgroup BasePluginStructs Base Plugin Structs
@{
*/

/**
Parameter designation.@n
Allows a parameter to be specially designated for a task, like bypass.

Each designation is unique, there must be only one parameter that uses it.@n
The use of designated parameters is completely optional.

@note Designated parameters have strict ranges.
@see ParameterRanges::adjustForDesignation()
*/
enum ParameterDesignation {
/**
Null or unset designation.
*/
kParameterDesignationNull = 0,

/**
Bypass designation.@n
When on (> 0.5f), it means the plugin must run in a bypassed state.
*/
kParameterDesignationBypass = 1
};

/**
Predefined Port Groups Ids.

This enumeration provides a few commonly used groups for convenient use in plugins.
For preventing conflicts with user code, negative values are used here.
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.

@see PortGroup
*/
enum PredefinedPortGroupsIds {
/**
Null or unset port group.
*/
kPortGroupNone = (uint32_t)-1,

/**
A single channel audio group.
*/
kPortGroupMono = (uint32_t)-2,

/**
A 2-channel discrete stereo audio group,
where the 1st audio port is the left channel and the 2nd port is the right channel.
*/
kPortGroupStereo = (uint32_t)-3
};

/**
Audio Port.

Can be used as CV port by specifying kAudioPortIsCV in hints,@n
but this is only supported in LV2 and JACK standalone formats.
*/
struct AudioPort {
/**
Hints describing this audio port.
@see AudioPortHints
*/
uint32_t hints;

/**
The name of this audio port.@n
An audio port name can contain any character, but hosts might have a hard time with non-ascii ones.@n
The name doesn't have to be unique within a plugin instance, but it's recommended.
*/
String name;

/**
The symbol of this audio port.@n
An audio port symbol is a short restricted name used as a machine and human readable identifier.@n
The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9.
@note Audio port and parameter symbols MUST be unique within a plugin instance.
*/
String symbol;

/**
The group id that this audio/cv port belongs to.
No group is assigned by default.

You can use a group from PredefinedPortGroups or roll your own.@n
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.
@see PortGroup, Plugin::initPortGroup
*/
uint32_t groupId;

/**
Default constructor for a regular audio port.
*/
AudioPort() noexcept
: hints(0x0),
name(),
symbol(),
groupId(kPortGroupNone) {}
};

/**
Parameter ranges.@n
This is used to set the default, minimum and maximum values of a parameter.

By default a parameter has 0.0 as minimum, 1.0 as maximum and 0.0 as default.@n
When changing this struct values you must ensure maximum > minimum and default is within range.
*/
struct ParameterRanges {
/**
Default value.
*/
float def;

/**
Minimum value.
*/
float min;

/**
Maximum value.
*/
float max;

/**
Default constructor, using 0.0 as default, 0.0 as minimum, 1.0 as maximum.
*/
ParameterRanges() noexcept
: def(0.0f),
min(0.0f),
max(1.0f) {}

/**
Constructor using custom values.
*/
ParameterRanges(float df, float mn, float mx) noexcept
: def(df),
min(mn),
max(mx) {}

/**
Fix the default value within range.
*/
void fixDefault() noexcept
{
fixValue(def);
}

/**
Fix a value within range.
*/
void fixValue(float& value) const noexcept
{
if (value < min)
value = min;
else if (value > max)
value = max;
}

/**
Get a fixed value within range.
*/
float getFixedValue(const float& value) const noexcept
{
if (value <= min)
return min;
if (value >= max)
return max;
return value;
}

/**
Get a value normalized to 0.0<->1.0.
*/
float getNormalizedValue(const float& value) const noexcept
{
const float normValue = (value - min) / (max - min);

if (normValue <= 0.0f)
return 0.0f;
if (normValue >= 1.0f)
return 1.0f;
return normValue;
}

/**
Get a value normalized to 0.0<->1.0.
Overloaded function using double precision values.
*/
double getNormalizedValue(const double& value) const noexcept
{
const double normValue = (value - min) / (max - min);

if (normValue <= 0.0)
return 0.0;
if (normValue >= 1.0)
return 1.0;
return normValue;
}

/**
Get a value normalized to 0.0<->1.0, fixed within range.
*/
float getFixedAndNormalizedValue(const float& value) const noexcept
{
if (value <= min)
return 0.0f;
if (value >= max)
return 1.0f;

const float normValue = (value - min) / (max - min);

if (normValue <= 0.0f)
return 0.0f;
if (normValue >= 1.0f)
return 1.0f;

return normValue;
}

/**
Get a value normalized to 0.0<->1.0, fixed within range.
Overloaded function using double precision values.
*/
double getFixedAndNormalizedValue(const double& value) const noexcept
{
if (value <= min)
return 0.0;
if (value >= max)
return 1.0;

const double normValue = (value - min) / (max - min);

if (normValue <= 0.0)
return 0.0;
if (normValue >= 1.0)
return 1.0;

return normValue;
}

/**
Get a proper value previously normalized to 0.0<->1.0.
*/
float getUnnormalizedValue(const float& value) const noexcept
{
if (value <= 0.0f)
return min;
if (value >= 1.0f)
return max;

return value * (max - min) + min;
}

/**
Get a proper value previously normalized to 0.0<->1.0.
Overloaded function using double precision values.
*/
double getUnnormalizedValue(const double& value) const noexcept
{
if (value <= 0.0)
return min;
if (value >= 1.0)
return max;

return value * (max - min) + min;
}
};

/**
Parameter enumeration value.@n
A string representation of a plugin parameter value.@n
Used together can be used to give meaning to parameter values, working as an enumeration.
*/
struct ParameterEnumerationValue {
/**
Parameter value.
*/
float value;

/**
String representation of this value.
*/
String label;

/**
Default constructor, using 0.0 as value and empty label.
*/
ParameterEnumerationValue() noexcept
: value(0.0f),
label() {}

/**
Constructor using custom values.
*/
ParameterEnumerationValue(float v, const char* l) noexcept
: value(v),
label(l) {}
};

/**
Collection of parameter enumeration values.@n
Handy class to handle the lifetime and count of all enumeration values.
*/
struct ParameterEnumerationValues {
/**
Number of elements allocated in @values.
*/
uint8_t count;

/**
Wherever the host is to be restricted to only use enumeration values.

@note This mode is only a hint! Not all hosts and plugin formats support this mode.
*/
bool restrictedMode;

/**
Array of @ParameterEnumerationValue items.@n
This pointer must be null or have been allocated on the heap with `new ParameterEnumerationValue[count]`.
*/
ParameterEnumerationValue* values;

/**
Default constructor, for zero enumeration values.
*/
ParameterEnumerationValues() noexcept
: count(0),
restrictedMode(false),
values() {}

/**
Constructor using custom values.@n
The pointer to @values must have been allocated on the heap with `new`.
*/
ParameterEnumerationValues(uint32_t c, bool r, ParameterEnumerationValue* v) noexcept
: count(c),
restrictedMode(r),
values(v) {}

~ParameterEnumerationValues() noexcept
{
count = 0;
restrictedMode = false;

if (values != nullptr)
{
delete[] values;
values = nullptr;
}
}

DISTRHO_DECLARE_NON_COPYABLE(ParameterEnumerationValues)
};

/**
Parameter.
*/
struct Parameter {
/**
Hints describing this parameter.
@see ParameterHints
*/
uint32_t hints;

/**
The name of this parameter.@n
A parameter name can contain any character, but hosts might have a hard time with non-ascii ones.@n
The name doesn't have to be unique within a plugin instance, but it's recommended.
*/
String name;

/**
The short name of this parameter.@n
Used when displaying the parameter name in a very limited space.
@note This value is optional, the full name is used when the short one is missing.
*/
String shortName;

/**
The symbol of this parameter.@n
A parameter symbol is a short restricted name used as a machine and human readable identifier.@n
The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9.
@note Parameter symbols MUST be unique within a plugin instance.
*/
String symbol;

/**
The unit of this parameter.@n
This means something like "dB", "kHz" and "ms".@n
Can be left blank if a unit does not apply to this parameter.
*/
String unit;

/**
An extensive description/comment about the parameter.
@note This value is optional and only used for LV2.
*/
String description;

/**
Ranges of this parameter.@n
The ranges describe the default, minimum and maximum values.
*/
ParameterRanges ranges;

/**
Enumeration values.@n
Can be used to give meaning to parameter values, working as an enumeration.
*/
ParameterEnumerationValues enumValues;

/**
Designation for this parameter.
*/
ParameterDesignation designation;

/**
MIDI CC to use by default on this parameter.@n
A value of 0 or 32 (bank change) is considered invalid.@n
Must also be less or equal to 120.
@note This value is only a hint! Hosts might map it automatically or completely ignore it.
*/
uint8_t midiCC;

/**
The group id that this parameter belongs to.
No group is assigned by default.

You can use a group from PredefinedPortGroups or roll your own.@n
When rolling your own port groups, you MUST start their group ids from 0 and they MUST be sequential.
@see PortGroup, Plugin::initPortGroup
*/
uint32_t groupId;

/**
Default constructor for a null parameter.
*/
Parameter() noexcept
: hints(0x0),
name(),
shortName(),
symbol(),
unit(),
ranges(),
enumValues(),
designation(kParameterDesignationNull),
midiCC(0),
groupId(kPortGroupNone) {}

/**
Constructor using custom values.
*/
Parameter(uint32_t h, const char* n, const char* s, const char* u, float def, float min, float max) noexcept
: hints(h),
name(n),
shortName(),
symbol(s),
unit(u),
ranges(def, min, max),
enumValues(),
designation(kParameterDesignationNull),
midiCC(0),
groupId(kPortGroupNone) {}

/**
Initialize a parameter for a specific designation.
*/
void initDesignation(ParameterDesignation d) noexcept
{
designation = d;

switch (d)
{
case kParameterDesignationNull:
break;
case kParameterDesignationBypass:
hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger;
name = "Bypass";
shortName = "Bypass";
symbol = "dpf_bypass";
unit = "";
midiCC = 0;
groupId = kPortGroupNone;
ranges.def = 0.0f;
ranges.min = 0.0f;
ranges.max = 1.0f;
break;
}
}
};

/**
Port Group.@n
Allows to group together audio/cv ports or parameters.

Each unique group MUST have an unique symbol and a name.
A group can be applied to both inputs and outputs (at the same time).
The same group cannot be used in audio ports and parameters.

When both audio and parameter groups are used, audio groups MUST be defined first.
That is, group indexes start with audio ports, then parameters.

An audio port group logically combines ports which should be considered part of the same stream.@n
For example, two audio ports in a group may form a stereo stream.

A parameter group provides meta-data to the host to indicate that some parameters belong together.

The use of port groups is completely optional.

@see Plugin::initPortGroup, AudioPort::group, Parameter::group
*/
struct PortGroup {
/**
The name of this port group.@n
A port group name can contain any character, but hosts might have a hard time with non-ascii ones.@n
The name doesn't have to be unique within a plugin instance, but it's recommended.
*/
String name;

/**
The symbol of this port group.@n
A port group symbol is a short restricted name used as a machine and human readable identifier.@n
The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9.
@note Port group symbols MUST be unique within a plugin instance.
*/
String symbol;
};

/**
State.

In DPF states refer to key:value string pairs, used to store arbitrary non-parameter data.@n
By default states are completely internal to the plugin and not visible by the host.@n
Flags can be set to allow hosts to see and/or change them.

TODO API under construction
*/
struct State {
/**
Hints describing this state.
@note Changing these hints can break compatibility with previously saved data.
@see StateHints
*/
uint32_t hints;

/**
The key or "symbol" of this state.@n
A state key is a short restricted name used as a machine and human readable identifier.
@note State keys MUST be unique within a plugin instance.
TODO define rules for allowed characters, must be usable as URI non-encoded parameters
*/
String key;

/**
The default value of this state.@n
Can be left empty if considered a valid initial state.
*/
String defaultValue;

/**
String representation of this state.
*/
String label;

/**
An extensive description/comment about this state.
@note This value is optional and only used for LV2.
*/
String description;

#ifdef __MOD_DEVICES__
/**
The file types that a filename path state supports, written as a comma-separated string without whitespace.
Currently supported file types are:
- audioloop: Audio Loops, meant to be used for looper-style plugins
- audiorecording: Audio Recordings, triggered by plugins and stored in the unit
- audiosample: One-shot Audio Samples, meant to be used for sampler-style plugins
- audiotrack: Audio Tracks, meant to be used as full-performance/song or backtrack
- cabsim: Speaker Cabinets, meant as small IR audio files
- h2drumkit: Hydrogen Drumkits, must use h2drumkit file extension
- ir: Impulse Responses
- midiclip: MIDI Clips, to be used in sync with host tempo, must have mid or midi file extension
- midisong: MIDI Songs, meant to be used as full-performance/song or backtrack
- sf2: SF2 Instruments, must have sf2 or sf3 file extension
- sfz: SFZ Instruments, must have sfz file extension

@note This is a custom extension only valid in builds MOD Audio.
*/
String fileTypes;
#endif

/**
Default constructor for a null state.
*/
State() noexcept
: hints(0x0),
key(),
defaultValue(),
label(),
description() {}
};

/**
MIDI event.
*/
struct MidiEvent {
/**
Size of internal data.
*/
static const uint32_t kDataSize = 4;

/**
Time offset in frames.
*/
uint32_t frame;

/**
Number of bytes used.
*/
uint32_t size;

/**
MIDI data.@n
If size > kDataSize, dataExt is used (otherwise null).

When dataExt is used, the event holder is responsible for
keeping the pointer valid during the entirety of the run function.
*/
uint8_t data[kDataSize];
const uint8_t* dataExt;
};

/**
Time position.@n
The @a playing and @a frame values are always valid.@n
BBT values are only valid when @a bbt.valid is true.

This struct is inspired by the [JACK Transport API](https://jackaudio.org/api/structjack__position__t.html).
*/
struct TimePosition {
/**
Wherever the host transport is playing/rolling.
*/
bool playing;

/**
Current host transport position in frames.
@note This value is not always monotonic,
with some plugin hosts assigning it based on a source that can accumulate rounding errors.
*/
uint64_t frame;

/**
Bar-Beat-Tick time position.
*/
struct BarBeatTick {
/**
Wherever the host transport is using BBT.@n
If false you must not read from this struct.
*/
bool valid;

/**
Current bar.@n
Should always be > 0.@n
The first bar is bar '1'.
*/
int32_t bar;

/**
Current beat within bar.@n
Should always be > 0 and <= @a beatsPerBar.@n
The first beat is beat '1'.
*/
int32_t beat;

/**
Current tick within beat.@n
Should always be >= 0 and < @a ticksPerBeat.@n
The first tick is tick '0'.
@note Fraction part of tick is only available on some plugin formats.
*/
double tick;

/**
Number of ticks that have elapsed between frame 0 and the first beat of the current measure.
*/
double barStartTick;

/**
Time signature "numerator".
*/
float beatsPerBar;

/**
Time signature "denominator".
*/
float beatType;

/**
Number of ticks within a beat.@n
Usually a moderately large integer with many denominators, such as 1920.0.
*/
double ticksPerBeat;

/**
Number of beats per minute.
*/
double beatsPerMinute;

/**
Default constructor for a null BBT time position.
*/
BarBeatTick() noexcept
: valid(false),
bar(0),
beat(0),
tick(0),
barStartTick(0.0),
beatsPerBar(0.0f),
beatType(0.0f),
ticksPerBeat(0.0),
beatsPerMinute(0.0) {}

/**
Reinitialize this position using the default null initialization.
*/
void clear() noexcept
{
valid = false;
bar = 0;
beat = 0;
tick = 0;
barStartTick = 0.0;
beatsPerBar = 0.0f;
beatType = 0.0f;
ticksPerBeat = 0.0;
beatsPerMinute = 0.0;
}
} bbt;

/**
Default constructor for a time position.
*/
TimePosition() noexcept
: playing(false),
frame(0),
bbt() {}

/**
Reinitialize this position using the default null initialization.
*/
void clear() noexcept
{
playing = false;
frame = 0;
bbt.clear();
}
};

/** @} */

/* ------------------------------------------------------------------------------------------------------------
* DPF Plugin */

@@ -984,7 +52,7 @@ struct TimePosition {
When enabled you need to implement initProgramName() and loadProgram().

DISTRHO_PLUGIN_WANT_STATE activates internal state features.@n
When enabled you need to implement initStateKey() and setState().
When enabled you need to implement initState() and setState().

The process function run() changes wherever DISTRHO_PLUGIN_WANT_MIDI_INPUT is enabled or not.@n
When enabled it provides midi input events.


+ 2
- 1
dpf/distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,6 +17,7 @@
#ifndef DISTRHO_UI_HPP_INCLUDED
#define DISTRHO_UI_HPP_INCLUDED

#include "DistrhoDetails.hpp"
#include "extra/LeakDetector.hpp"
#include "src/DistrhoPluginChecks.h"



+ 4
- 0
dpf/distrho/DistrhoUIMain.cpp View File

@@ -16,6 +16,10 @@

#include "src/DistrhoUI.cpp"

#if ! DISTRHO_PLUGIN_HAS_UI
# error Trying to build UI without DISTRHO_PLUGIN_HAS_UI set to 1
#endif

#if defined(DISTRHO_PLUGIN_TARGET_CARLA)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
#elif defined(DISTRHO_PLUGIN_TARGET_CLAP)


+ 1
- 0
dpf/distrho/DistrhoUI_macOS.mm View File

@@ -16,6 +16,7 @@

// A few utils declared in DistrhoUI.cpp but defined here because they use Obj-C

#include "DistrhoDetails.hpp"
#include "src/DistrhoPluginChecks.h"
#include "src/DistrhoDefines.h"



+ 10
- 11
dpf/distrho/DistrhoUtils.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -58,7 +58,7 @@ inline float round(float __x)
#define DISTRHO_MACRO_AS_STRING_VALUE(MACRO) #MACRO
#define DISTRHO_MACRO_AS_STRING(MACRO) DISTRHO_MACRO_AS_STRING_VALUE(MACRO)

/* ------------------------------------------------------------------------------------------------------------
/* --------------------------------------------------------------------------------------------------------------------
* misc functions */

/**
@@ -94,7 +94,7 @@ void d_pass() noexcept {}

/** @} */

/* ------------------------------------------------------------------------------------------------------------
/* --------------------------------------------------------------------------------------------------------------------
* string print functions */

/**
@@ -240,7 +240,7 @@ void d_safe_exception(const char* const exception, const char* const file, const

/** @} */

/* ------------------------------------------------------------------------------------------------------------
/* --------------------------------------------------------------------------------------------------------------------
* math functions */

/**
@@ -254,7 +254,7 @@ void d_safe_exception(const char* const exception, const char* const file, const
Returns true if they match.
*/
template<typename T>
static inline
static inline constexpr
bool d_isEqual(const T& v1, const T& v2)
{
return std::abs(v1-v2) < std::numeric_limits<T>::epsilon();
@@ -265,7 +265,7 @@ bool d_isEqual(const T& v1, const T& v2)
Returns true if they don't match.
*/
template<typename T>
static inline
static inline constexpr
bool d_isNotEqual(const T& v1, const T& v2)
{
return std::abs(v1-v2) >= std::numeric_limits<T>::epsilon();
@@ -275,7 +275,7 @@ bool d_isNotEqual(const T& v1, const T& v2)
Safely check if a floating point number is zero.
*/
template<typename T>
static inline
static inline constexpr
bool d_isZero(const T& value)
{
return std::abs(value) < std::numeric_limits<T>::epsilon();
@@ -285,7 +285,7 @@ bool d_isZero(const T& value)
Safely check if a floating point number is not zero.
*/
template<typename T>
static inline
static inline constexpr
bool d_isNotZero(const T& value)
{
return std::abs(value) >= std::numeric_limits<T>::epsilon();
@@ -311,7 +311,8 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept

/** @} */

// -----------------------------------------------------------------------
/* --------------------------------------------------------------------------------------------------------------------
* math functions */

#ifndef DONT_SET_USING_DISTRHO_NAMESPACE
// If your code uses a lot of DISTRHO classes, then this will obviously save you
@@ -320,6 +321,4 @@ uint32_t d_nextPowerOf2(uint32_t size) noexcept
using namespace DISTRHO_NAMESPACE;
#endif

// -----------------------------------------------------------------------

#endif // DISTRHO_UTILS_HPP_INCLUDED

+ 3
- 3
dpf/distrho/extra/FileBrowserDialogImpl.hpp View File

@@ -92,7 +92,7 @@ struct FileBrowserOptions {
@p windowId: The native window id to attach this dialog to as transient parent (X11 Window, HWND or NSView*)
@p scaleFactor: Scale factor to use (only used on X11)
@p options: Extra options, optional
By default the file browser dialog will be work as "open file" in the current working directory.
By default the file browser dialog will work as "open file" in the current working directory.
*/
FileBrowserHandle fileBrowserCreate(bool isEmbed,
uintptr_t windowId,
@@ -102,13 +102,13 @@ FileBrowserHandle fileBrowserCreate(bool isEmbed,
/**
Idle the file browser dialog handle.@n
Returns true if dialog was closed (with or without a file selection),
in which case the handle must not be used afterwards.
in which case this idle function must not be called anymore for this handle.
You can then call fileBrowserGetPath to know the selected file (or null if cancelled).
*/
bool fileBrowserIdle(const FileBrowserHandle handle);

/**
Close the file browser dialog, handle must not be used afterwards.
Close and free the file browser dialog, handle must not be used afterwards.
*/
void fileBrowserClose(const FileBrowserHandle handle);



+ 112
- 0
dpf/distrho/extra/ScopedDenormalDisable.hpp View File

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

+ 15
- 1
dpf/distrho/extra/String.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -22,6 +22,10 @@

#include <algorithm>

#if __cplusplus >= 201703L
# include <string_view>
#endif

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
@@ -87,6 +91,16 @@ public:
_dup(strBuf);
}

#if __cplusplus >= 201703L
/*
* constexpr compatible variant.
*/
explicit constexpr String(const std::string_view& strView) noexcept
: fBuffer(const_cast<char*>(strView.data())),
fBufferLen(strView.size()),
fBufferAlloc(false) {}
#endif

/*
* Integer.
*/


+ 204
- 0
dpf/distrho/extra/ValueSmoother.hpp View File

@@ -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
- 1
dpf/distrho/src/DistrhoPlugin.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this


+ 33
- 34
dpf/distrho/src/DistrhoPluginCLAP.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -211,7 +211,7 @@ public:
#endif
const bool isFloating)
: fPlugin(plugin),
fPluinEventQueue(eventQueue),
fPluginEventQueue(eventQueue),
fEventQueue(eventQueue->fEventQueue),
fCachedParameters(eventQueue->fCachedParameters),
#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -382,7 +382,7 @@ public:
*width = minimumWidth;
if (minimumHeight > *height)
*height = minimumHeight;
return true;
}

@@ -534,7 +534,7 @@ public:
private:
// Plugin and UI
PluginExporter& fPlugin;
ClapEventQueue* const fPluinEventQueue;
ClapEventQueue* const fPluginEventQueue;
ClapEventQueue::Queue& fEventQueue;
ClapEventQueue::CachedParameters& fCachedParameters;
#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -578,7 +578,7 @@ private:
setStateCallback,
sendNoteCallback,
setSizeCallback,
fileRequestCallback,
nullptr, // TODO fileRequestCallback,
d_nextBundlePath,
fPlugin.getInstancePointer(),
fScaleFactor);
@@ -682,7 +682,7 @@ private:
#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const key, const char* const value)
{
fPluinEventQueue->setStateFromUI(key, value);
fPluginEventQueue->setStateFromUI(key, value);
}

static void setStateCallback(void* const ptr, const char* key, const char* value)
@@ -708,6 +708,7 @@ private:
}
#endif

/* TODO
bool fileRequest(const char*)
{
return true;
@@ -717,6 +718,7 @@ private:
{
return static_cast<ClapUI*>(ptr)->fileRequest(key);
}
*/
};

// --------------------------------------------------------------------------------------------------------------------
@@ -977,6 +979,7 @@ public:
case CLAP_EVENT_PARAM_GESTURE_BEGIN:
case CLAP_EVENT_PARAM_GESTURE_END:
case CLAP_EVENT_TRANSPORT:
break;
case CLAP_EVENT_MIDI:
DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_midi_t),
event->size, sizeof(clap_event_midi_t));
@@ -1232,7 +1235,7 @@ public:

DISTRHO_SAFE_ASSERT_UINT2_BREAK(event->size == sizeof(clap_event_param_value),
event->size, sizeof(clap_event_param_value));
setParameterValueFromEvent(static_cast<const clap_event_param_value*>(static_cast<const void*>(event)));
}
}
@@ -2068,7 +2071,7 @@ static const char* const kSupportedAPIs[] = {
};

// TODO DPF external UI
static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
static bool CLAP_ABI clap_gui_is_api_supported(const clap_plugin_t*, const char* const api, bool)
{
for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
{
@@ -2080,14 +2083,14 @@ static bool clap_gui_is_api_supported(const clap_plugin_t*, const char* const ap
}

// TODO DPF external UI
static bool clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
static bool CLAP_ABI clap_gui_get_preferred_api(const clap_plugin_t*, const char** const api, bool* const is_floating)
{
*api = kSupportedAPIs[0];
*is_floating = false;
return true;
}

static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
static bool CLAP_ABI clap_gui_create(const clap_plugin_t* const plugin, const char* const api, const bool is_floating)
{
for (size_t i=0; i<ARRAY_SIZE(kSupportedAPIs); ++i)
{
@@ -2101,13 +2104,13 @@ static bool clap_gui_create(const clap_plugin_t* const plugin, const char* const
return false;
}

static void clap_gui_destroy(const clap_plugin_t* const plugin)
static void CLAP_ABI clap_gui_destroy(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
instance->destroyUI();
}

static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
static bool CLAP_ABI clap_gui_set_scale(const clap_plugin_t* const plugin, const double scale)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2121,7 +2124,7 @@ static bool clap_gui_set_scale(const clap_plugin_t* const plugin, const double s
#endif
}

static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
static bool CLAP_ABI clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2129,7 +2132,7 @@ static bool clap_gui_get_size(const clap_plugin_t* const plugin, uint32_t* const
return gui->getSize(width, height);
}

static bool clap_gui_can_resize(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_can_resize(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2137,7 +2140,7 @@ static bool clap_gui_can_resize(const clap_plugin_t* const plugin)
return gui->canResize();
}

static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
static bool CLAP_ABI clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gui_resize_hints_t* const hints)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2145,7 +2148,7 @@ static bool clap_gui_get_resize_hints(const clap_plugin_t* const plugin, clap_gu
return gui->getResizeHints(hints);
}

static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
static bool CLAP_ABI clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* const width, uint32_t* const height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2153,7 +2156,7 @@ static bool clap_gui_adjust_size(const clap_plugin_t* const plugin, uint32_t* co
return gui->adjustSize(width, height);
}

static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
static bool CLAP_ABI clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t width, const uint32_t height)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2161,7 +2164,7 @@ static bool clap_gui_set_size(const clap_plugin_t* const plugin, const uint32_t
return gui->setSizeFromHost(width, height);
}

static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
static bool CLAP_ABI clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_window_t* const window)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2169,7 +2172,7 @@ static bool clap_gui_set_parent(const clap_plugin_t* const plugin, const clap_wi
return gui->setParent(window);
}

static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
static bool CLAP_ABI clap_gui_set_transient(const clap_plugin_t* const plugin, const clap_window_t* const window)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2177,7 +2180,7 @@ static bool clap_gui_set_transient(const clap_plugin_t* const plugin, const clap
return gui->setTransient(window);
}

static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
static void CLAP_ABI clap_gui_suggest_title(const clap_plugin_t* const plugin, const char* const title)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2185,7 +2188,7 @@ static void clap_gui_suggest_title(const clap_plugin_t* const plugin, const char
return gui->suggestTitle(title);
}

static bool clap_gui_show(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_show(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2193,7 +2196,7 @@ static bool clap_gui_show(const clap_plugin_t* const plugin)
return gui->show();
}

static bool clap_gui_hide(const clap_plugin_t* const plugin)
static bool CLAP_ABI clap_gui_hide(const clap_plugin_t* const plugin)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2223,7 +2226,7 @@ static const clap_plugin_gui_t clap_plugin_gui = {
// plugin timer

#if DPF_CLAP_USING_HOST_TIMER
static void clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
static void CLAP_ABI clap_plugin_on_timer(const clap_plugin_t* const plugin, clap_id)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
ClapUI* const gui = instance->getUI();
@@ -2242,14 +2245,14 @@ static const clap_plugin_timer_support_t clap_timer = {
// plugin audio ports

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
static uint32_t clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
static uint32_t CLAP_ABI clap_plugin_audio_ports_count(const clap_plugin_t* const plugin, const bool is_input)
{
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
return is_input ? instance->getAudioPortCount<true>()
: instance->getAudioPortCount<false>();
}

static bool clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
static bool CLAP_ABI clap_plugin_audio_ports_get(const clap_plugin_t* const plugin,
const uint32_t index,
const bool is_input,
clap_audio_port_info_t* const info)
@@ -2269,13 +2272,13 @@ static const clap_plugin_audio_ports_t clap_plugin_audio_ports = {
// plugin note ports

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT+DISTRHO_PLUGIN_WANT_MIDI_OUTPUT != 0
static uint32_t clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
static uint32_t CLAP_ABI clap_plugin_note_ports_count(const clap_plugin_t*, const bool is_input)
{
return (is_input ? DISTRHO_PLUGIN_WANT_MIDI_INPUT : DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) != 0 ? 1 : 0;
}

static bool clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
const bool is_input, clap_note_port_info_t* const info)
static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
const bool is_input, clap_note_port_info_t* const info)
{
if (is_input)
{
@@ -2370,7 +2373,6 @@ static const clap_plugin_latency_t clap_plugin_latency = {
};
#endif

#if DISTRHO_PLUGIN_WANT_STATE
// --------------------------------------------------------------------------------------------------------------------
// plugin state

@@ -2390,7 +2392,6 @@ static const clap_plugin_state_t clap_plugin_state = {
clap_plugin_state_save,
clap_plugin_state_load
};
#endif

// --------------------------------------------------------------------------------------------------------------------
// plugin
@@ -2452,6 +2453,8 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons
{
if (std::strcmp(id, CLAP_EXT_PARAMS) == 0)
return &clap_plugin_params;
if (std::strcmp(id, CLAP_EXT_STATE) == 0)
return &clap_plugin_state;
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
if (std::strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0)
return &clap_plugin_audio_ports;
@@ -2464,10 +2467,6 @@ static const void* CLAP_ABI clap_plugin_get_extension(const clap_plugin_t*, cons
if (std::strcmp(id, CLAP_EXT_LATENCY) == 0)
return &clap_plugin_latency;
#endif
#if DISTRHO_PLUGIN_WANT_STATE
if (std::strcmp(id, CLAP_EXT_STATE) == 0)
return &clap_plugin_state;
#endif
#if DISTRHO_PLUGIN_HAS_UI
if (std::strcmp(id, CLAP_EXT_GUI) == 0)
return &clap_plugin_gui;


+ 5
- 1
dpf/distrho/src/DistrhoPluginChecks.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -17,6 +17,10 @@
#ifndef DISTRHO_PLUGIN_CHECKS_H_INCLUDED
#define DISTRHO_PLUGIN_CHECKS_H_INCLUDED

#ifndef DISTRHO_DETAILS_HPP_INCLUDED
# error wrong include order
#endif

#include "DistrhoPluginInfo.h"

// -----------------------------------------------------------------------


+ 1
- 1
dpf/distrho/src/DistrhoPluginInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this


+ 25
- 14
dpf/distrho/src/DistrhoPluginJACK.cpp View File

@@ -152,23 +152,25 @@ public:
fClient(client)
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 || DISTRHO_PLUGIN_NUM_OUTPUTS > 0
char strBuf[0xff+1];
strBuf[0xff] = '\0';

# if DISTRHO_PLUGIN_NUM_INPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
{
const AudioPort& port(fPlugin.getAudioPort(true, i));
fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
ulong hints = JackPortIsInput;
if (port.hints & kAudioPortIsCV)
hints |= JackPortIsControlVoltage;
fPortAudioIns[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0);
setAudioPortMetadata(port, fPortAudioIns[i], i);
}
# endif
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
{
std::snprintf(strBuf, 0xff, "out%i", i+1);
const AudioPort& port(fPlugin.getAudioPort(false, i));
fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
ulong hints = JackPortIsOutput;
if (port.hints & kAudioPortIsCV)
hints |= JackPortIsControlVoltage;
fPortAudioOuts[i] = jackbridge_port_register(fClient, port.symbol, JACK_DEFAULT_AUDIO_TYPE, hints, 0);
setAudioPortMetadata(port, fPortAudioOuts[i], DISTRHO_PLUGIN_NUM_INPUTS+i);
}
# endif
@@ -623,7 +625,8 @@ private:

{
char strBuf[0xff];
snprintf(strBuf, sizeof(0xff)-1, "%u", index);
snprintf(strBuf, 0xff - 2, "%u", index);
strBuf[0xff - 1] = '\0';
jackbridge_set_property(fClient, uuid, JACK_METADATA_ORDER, strBuf, "http://www.w3.org/2001/XMLSchema#integer");
}

@@ -807,7 +810,7 @@ public:
protected:
void run() override
{
plugin.setBufferSize(256);
plugin.setBufferSize(256, true);
plugin.activate();

float buffer[256];
@@ -862,8 +865,8 @@ bool runSelfTests()

plugin.activate();
plugin.deactivate();
plugin.setBufferSize(128);
plugin.setSampleRate(48000);
plugin.setBufferSize(128, true);
plugin.setSampleRate(48000, true);
plugin.activate();

float buffer[128] = {};
@@ -1030,6 +1033,12 @@ int main(int argc, char* argv[])
jack_status_t status = jack_status_t(0x0);
jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status);

#ifdef HAVE_JACK
#define STANDALONE_NAME "JACK client"
#else
#define STANDALONE_NAME "Native audio driver"
#endif

if (client == nullptr)
{
String errorString;
@@ -1060,20 +1069,22 @@ int main(int argc, char* argv[])
errorString += "Backend Error;\n";
if (status & JackClientZombie)
errorString += "Client is being shutdown against its will;\n";
if (status & JackBridgeNativeFailed)
errorString += "Native audio driver was unable to start;\n";

if (errorString.isNotEmpty())
{
errorString[errorString.length()-2] = '.';
d_stderr("Failed to create the JACK client, reason was:\n%s", errorString.buffer());
d_stderr("Failed to create the " STANDALONE_NAME ", reason was:\n%s", errorString.buffer());
}
else
d_stderr("Failed to create the JACK client, cannot continue!");
d_stderr("Failed to create the " STANDALONE_NAME ", cannot continue!");

#if defined(DISTRHO_OS_MAC)
CFStringRef errorTitleRef = CFStringCreateWithCString(nullptr,
DISTRHO_PLUGIN_NAME ": Error", kCFStringEncodingUTF8);
CFStringRef errorStringRef = CFStringCreateWithCString(nullptr,
String("Failed to create JACK client, reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);
String("Failed to create " STANDALONE_NAME ", reason was:\n" + errorString).buffer(), kCFStringEncodingUTF8);

CFUserNotificationDisplayAlert(0, kCFUserNotificationCautionAlertLevel,
nullptr, nullptr, nullptr,
@@ -1097,7 +1108,7 @@ int main(int argc, char* argv[])
FreeLibrary(user32);
}

const String win32error = "Failed to create JACK client, reason was:\n" + errorString;
const String win32error = "Failed to create " STANDALONE_NAME ", reason was:\n" + errorString;
MessageBoxA(nullptr, win32error.buffer(), "", MB_ICONERROR);
#endif



+ 11
- 7
dpf/distrho/src/DistrhoPluginLADSPA+DSSI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -22,7 +22,7 @@
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error Cannot use MIDI Output with LADSPA or DSSI
#endif
#if DISTRHO_PLUGIN_WANT_FULL_STATE
#if DISTRHO_PLUGIN_WANT_FULL_STATE && !defined(DISTRHO_PLUGIN_WANT_FULL_STATE_WITH_LADSPA)
# error Cannot use full state with LADSPA or DSSI
#endif

@@ -618,13 +618,19 @@ static const struct DescriptorInitializer
else
portDescriptors[port] |= LADSPA_PORT_INPUT;

const uint32_t hints = plugin.getParameterHints(i);

{
const ParameterRanges& ranges(plugin.getParameterRanges(i));
const float defValue = ranges.def;

portRangeHints[port].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
portRangeHints[port].LowerBound = ranges.min;
portRangeHints[port].UpperBound = ranges.max;
// LADSPA doesn't allow bounded hints on toggles
portRangeHints[port].HintDescriptor = hints & kParameterIsBoolean
? 0
: LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;

portRangeHints[port].LowerBound = ranges.min;
portRangeHints[port].UpperBound = ranges.max;

/**/ if (d_isZero(defValue))
portRangeHints[port].HintDescriptor |= LADSPA_HINT_DEFAULT_0;
@@ -654,8 +660,6 @@ static const struct DescriptorInitializer
}

{
const uint32_t hints = plugin.getParameterHints(i);

if (hints & kParameterIsBoolean)
{
portRangeHints[port].HintDescriptor |= LADSPA_HINT_TOGGLED;


+ 5
- 3
dpf/distrho/src/DistrhoPluginLV2.cpp View File

@@ -821,7 +821,7 @@ public:
if (options[i].type == fURIDs.atomInt)
{
const int32_t bufferSize(*(const int32_t*)options[i].value);
fPlugin.setBufferSize(bufferSize);
fPlugin.setBufferSize(bufferSize, true);
}
else
{
@@ -833,7 +833,7 @@ public:
if (options[i].type == fURIDs.atomInt)
{
const int32_t bufferSize(*(const int32_t*)options[i].value);
fPlugin.setBufferSize(bufferSize);
fPlugin.setBufferSize(bufferSize, true);
}
else
{
@@ -846,7 +846,7 @@ public:
{
const float sampleRate(*(const float*)options[i].value);
fSampleRate = sampleRate;
fPlugin.setSampleRate(sampleRate);
fPlugin.setSampleRate(sampleRate, true);
}
else
{
@@ -1070,6 +1070,7 @@ public:

setState(key, filename);

/* FIXME host should be responsible for updating UI side, not us
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
{
if (fPlugin.getStateKey(i) == key)
@@ -1079,6 +1080,7 @@ public:
break;
}
}
*/

return LV2_WORKER_SUCCESS;
}


+ 343
- 14
dpf/distrho/src/DistrhoPluginLV2export.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -42,7 +42,11 @@
# include "mod-license.h"
#endif

#ifndef DISTRHO_OS_WINDOWS
#ifdef DISTRHO_OS_WINDOWS
# include <direct.h>
#else
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
#endif

@@ -65,6 +69,10 @@
# define DISTRHO_PLUGIN_USES_MODGUI 0
#endif

#ifndef DISTRHO_PLUGIN_USES_CUSTOM_MODGUI
# define DISTRHO_PLUGIN_USES_CUSTOM_MODGUI 0
#endif

#if DISTRHO_PLUGIN_HAS_EMBED_UI
# if DISTRHO_OS_HAIKU
# define DISTRHO_LV2_UI_TYPE "BeUI"
@@ -331,7 +339,7 @@ void lv2_generate_ttl(const char* const basename)
}
#endif

manifestFile << manifestString << std::endl;
manifestFile << manifestString;
manifestFile.close();
std::cout << " done!" << std::endl;
}
@@ -748,6 +756,8 @@ void lv2_generate_ttl(const char* const basename)

if (! designated)
{
const uint32_t hints = plugin.getParameterHints(i);

// name and symbol
const String& paramName(plugin.getParameterName(i));

@@ -772,13 +782,35 @@ void lv2_generate_ttl(const char* const basename)
// ranges
const ParameterRanges& ranges(plugin.getParameterRanges(i));

if (plugin.getParameterHints(i) & kParameterIsInteger)
if (hints & kParameterIsInteger)
{
if (plugin.isParameterInput(i))
pluginString += " lv2:default " + String(int(ranges.def)) + " ;\n";
pluginString += " lv2:minimum " + String(int(ranges.min)) + " ;\n";
pluginString += " lv2:maximum " + String(int(ranges.max)) + " ;\n";
}
else if (hints & kParameterIsLogarithmic)
{
if (plugin.isParameterInput(i))
{
if (d_isNotZero(ranges.def))
pluginString += " lv2:default " + String(ranges.def) + " ;\n";
else if (d_isEqual(ranges.def, ranges.max))
pluginString += " lv2:default -0.0001 ;\n";
else
pluginString += " lv2:default 0.0001 ;\n";
}

if (d_isNotZero(ranges.min))
pluginString += " lv2:minimum " + String(ranges.min) + " ;\n";
else
pluginString += " lv2:minimum 0.0001 ;\n";

if (d_isNotZero(ranges.max))
pluginString += " lv2:maximum " + String(ranges.max) + " ;\n";
else
pluginString += " lv2:maximum -0.0001 ;\n";
}
else
{
if (plugin.isParameterInput(i))
@@ -809,7 +841,7 @@ void lv2_generate_ttl(const char* const basename)
else
pluginString += " rdfs:label \"" + enumValue.label + "\" ;\n";

if (plugin.getParameterHints(i) & kParameterIsInteger)
if (hints & kParameterIsInteger)
{
const int rounded = (int)(enumValue.value + (enumValue.value < 0.0f ? -0.5f : 0.5f));
pluginString += " rdf:value " + String(rounded) + " ;\n";
@@ -877,7 +909,7 @@ void lv2_generate_ttl(const char* const basename)
pluginString += " a unit:Unit ;\n";
pluginString += " rdfs:label \"" + unit + "\" ;\n";
pluginString += " unit:symbol \"" + unit + "\" ;\n";
if (plugin.getParameterHints(i) & kParameterIsInteger)
if (hints & kParameterIsInteger)
pluginString += " unit:render \"%d " + unit + "\" ;\n";
else
pluginString += " unit:render \"%f " + unit + "\" ;\n";
@@ -897,8 +929,6 @@ void lv2_generate_ttl(const char* const basename)
}

// hints
const uint32_t hints = plugin.getParameterHints(i);

if (hints & kParameterIsBoolean)
{
if ((hints & kParameterIsTrigger) == kParameterIsTrigger)
@@ -909,6 +939,8 @@ void lv2_generate_ttl(const char* const basename)
pluginString += " lv2:portProperty lv2:integer ;\n";
if (hints & kParameterIsLogarithmic)
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__logarithmic "> ;\n";
if (hints & kParameterIsHidden)
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__notOnGUI "> ;\n";
if ((hints & kParameterIsAutomatable) == 0 && plugin.isParameterInput(i))
{
pluginString += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ,\n";
@@ -944,11 +976,11 @@ void lv2_generate_ttl(const char* const basename)
}
}

#ifdef DISTRHO_PLUGIN_BRAND
#ifdef DISTRHO_PLUGIN_BRAND
// MOD
pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n";
pluginString += " mod:label \"" DISTRHO_PLUGIN_NAME "\" ;\n\n";
#endif
#endif

// name
{
@@ -1209,11 +1241,308 @@ void lv2_generate_ttl(const char* const basename)
}
}

pluginFile << pluginString << std::endl;
pluginFile << pluginString;
pluginFile.close();
std::cout << " done!" << std::endl;
}

#if DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI
{
std::cout << "Writing modgui.ttl..."; std::cout.flush();
std::fstream modguiFile("modgui.ttl", std::ios::out);

String modguiString;
modguiString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
modguiString += "@prefix modgui: <http://moddevices.com/ns/modgui#> .\n";
modguiString += "\n";

modguiString += "<" DISTRHO_PLUGIN_URI ">\n";
modguiString += " modgui:gui [\n";
#ifdef DISTRHO_PLUGIN_BRAND
modguiString += " modgui:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n";
#endif
modguiString += " modgui:label \"" DISTRHO_PLUGIN_NAME "\" ;\n";
modguiString += " modgui:resourcesDirectory <modgui> ;\n";
modguiString += " modgui:iconTemplate <modgui/icon.html> ;\n";
modguiString += " modgui:javascript <modgui/javascript.js> ;\n";
modguiString += " modgui:stylesheet <modgui/stylesheet.css> ;\n";
modguiString += " modgui:screenshot <modgui/screenshot.png> ;\n";
modguiString += " modgui:thumbnail <modgui/thumbnail.png> ;\n";

uint32_t numParametersOutputs = 0;
for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i)
{
if (plugin.isParameterOutput(i))
++numParametersOutputs;
}
if (numParametersOutputs != 0)
{
modguiString += " modgui:monitoredOutputs [\n";
for (uint32_t i=0, j=0, count=plugin.getParameterCount(); i < count; ++i)
{
if (!plugin.isParameterOutput(i))
continue;
modguiString += " lv2:symbol \"" + plugin.getParameterSymbol(i) + "\" ;\n";
if (++j != numParametersOutputs)
modguiString += " ] , [\n";
}
modguiString += " ] ;\n";
}

modguiString += " ] .\n";

modguiFile << modguiString;
modguiFile.close();
std::cout << " done!" << std::endl;
}

#ifdef DISTRHO_OS_WINDOWS
::_mkdir("modgui");
#else
::mkdir("modgui", 0755);
#endif

{
std::cout << "Writing modgui/javascript.js..."; std::cout.flush();
std::fstream jsFile("modgui/javascript.js", std::ios::out);

String jsString;
jsString += "function(e,f){\n";
jsString += "'use strict';\nvar ps=[";

for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
jsString += "'lv2_" + plugin.getAudioPort(false, i).symbol + "',";
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
jsString += "'lv2_" + plugin.getAudioPort(true, i).symbol + "',";
#if DISTRHO_LV2_USE_EVENTS_IN
jsString += "'lv2_events_in',";
#endif
#if DISTRHO_LV2_USE_EVENTS_OUT
jsString += "'lv2_events_out',";
#endif
#if DISTRHO_PLUGIN_WANT_LATENCY
jsString += "'lv2_latency',";
#endif

int32_t enabledIndex = INT32_MAX;
for (uint32_t i=0, count=plugin.getParameterCount(); i < count; ++i)
{
jsString += "'" + plugin.getParameterSymbol(i) + "',";
if (plugin.getParameterDesignation(i) == kParameterDesignationBypass)
enabledIndex = i;
}
jsString += "];\n";
jsString += "var ei=" + String(enabledIndex != INT32_MAX ? enabledIndex : -1) + ";\n\n";
jsString += "if(e.type==='start'){\n";
jsString += "e.data.p={p:{},c:{},};\n\n";
jsString += "var err=[];\n";
jsString += "if(typeof(WebAssembly)==='undefined'){err.push('WebAssembly unsupported');}\n";
jsString += "else{\n";
jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,4,1,96,0,0,3,2,1,0,5,3,1,0,1,10,14,1,12,0,65,0,65,0,65,0,252,10,0,0,11])))";
jsString += "err.push('Bulk Memory Operations unsupported');\n";
jsString += "if(!WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,2,8,1,1,97,1,98,3,127,1,6,6,1,127,1,65,0,11,7,5,1,1,97,3,1])))";
jsString += "err.push('Importable/Exportable mutable globals unsupported');\n";
jsString += "}\n";
jsString += "if(err.length!==0){e.icon.find('.canvas_wrapper').html('<h2>'+err.join('<br>')+'</h2>');return;}\n\n";
jsString += "var s=document.createElement('script');\n";
jsString += "s.setAttribute('async',true);\n";
jsString += "s.setAttribute('src',e.api_version>=3?f.get_custom_resource_filename('module.js'):('/resources/module.js?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION));\n";
jsString += "s.setAttribute('type','text/javascript');\n";
jsString += "s.onload=function(){\n";
jsString += " Module_" DISTRHO_PLUGIN_MODGUI_CLASS_NAME "({\n";
jsString += " locateFile: function(p,_){return e.api_version>=3?f.get_custom_resource_filename(p):('/resources/'+p+'?uri='+escape(\"" DISTRHO_PLUGIN_URI "\")+'&r='+VERSION)},\n";
jsString += " postRun:function(m){\n";
jsString += " var cn=e.icon.attr('mod-instance').replaceAll('/','_');\n";
jsString += " var cnl=m.lengthBytesUTF8(cn) + 1;\n";
jsString += " var cna=m._malloc(cnl);\n";
jsString += " m.stringToUTF8(cn, cna, cnl);\n";
jsString += " e.icon.find('canvas')[0].id=cn;\n";
jsString += " var a=m.addFunction(function(i,v){f.set_port_value(ps[i],v);},'vif');\n";
jsString += " var b=m.addFunction(function(u,v){f.patch_set(m.UTF8ToString(u),'s',m.UTF8ToString(v));},'vpp');\n";
jsString += " var h=m._modgui_init(cna,a,b);\n";
jsString += " m._free(cna);\n";
jsString += " e.data.h=h;\n";
jsString += " e.data.m=m;\n";
jsString += " for(var u in e.data.p.p){\n";
jsString += " var ul=m.lengthBytesUTF8(u)+1,ua=m._malloc(ul),v=e.data.p.p[u],vl=m.lengthBytesUTF8(v)+1,va=m._malloc(vl);\n";
jsString += " m.stringToUTF8(u,ua,ul);\n";
jsString += " m.stringToUTF8(v,va,vl);\n";
jsString += " m._modgui_patch_set(h, ua, va);\n";
jsString += " m._free(ua);\n";
jsString += " m._free(va);\n";
jsString += " }\n";
jsString += " for(var symbol in e.data.p.c){m._modgui_param_set(h,ps.indexOf(symbol),e.data.p.c[symbol]);}\n";
jsString += " delete e.data.p;\n";
jsString += " window.dispatchEvent(new Event('resize'));\n";
jsString += " },\n";
jsString += " canvas:(function(){var c=e.icon.find('canvas')[0];c.addEventListener('webglcontextlost',function(e2){alert('WebGL context lost. You will need to reload the page.');e2.preventDefault();},false);return c;})(),\n";
jsString += " });\n";
jsString += "};\n";
jsString += "document.head.appendChild(s);\n\n";
jsString += "}else if(e.type==='change'){\n\n";
jsString += "if(e.data.h && e.data.m){\n";
jsString += " var m=e.data.m;\n";
jsString += " if(e.uri){\n";
jsString += " var ul=m.lengthBytesUTF8(e.uri)+1,ua=m._malloc(ul),vl=m.lengthBytesUTF8(e.value)+1,va=m._malloc(vl);\n";
jsString += " m.stringToUTF8(e.uri,ua,ul);\n";
jsString += " m.stringToUTF8(e.value,va,vl);\n";
jsString += " m._modgui_patch_set(e.data.h,ua,va);\n";
jsString += " m._free(ua);\n";
jsString += " m._free(va);\n";
jsString += " }else if(e.symbol===':bypass'){return;\n";
jsString += " }else{m._modgui_param_set(e.data.h,ps.indexOf(e.symbol),e.value);}\n";
jsString += "}else{\n";
jsString += " if(e.symbol===':bypass')return;\n";
jsString += " if(e.uri){e.data.p.p[e.uri]=e.value;}else{e.data.p.c[e.symbol]=e.value;}\n";
jsString += "}\n\n";
jsString += "}else if(e.type==='end'){\n";
jsString += " if(e.data.h && e.data.m){\n";
jsString += " var h = e.data.h;\n";
jsString += " var m = e.data.m;\n";
jsString += " e.data.h = e.data.m = null;\n";
jsString += " m._modgui_cleanup(h);\n";
jsString += "}\n\n";
jsString += "}\n}\n";
jsFile << jsString;
jsFile.close();
std::cout << " done!" << std::endl;
}

{
std::cout << "Writing modgui/icon.html..."; std::cout.flush();
std::fstream iconFile("modgui/icon.html", std::ios::out);

iconFile << "<div class='" DISTRHO_PLUGIN_MODGUI_CLASS_NAME " mod-pedal'>" << std::endl;
iconFile << " <div mod-role='drag-handle' class='mod-drag-handle'></div>" << std::endl;
iconFile << " <div class='mod-plugin-title'><h1>{{#brand}}{{brand}} | {{/brand}}{{label}}</h1></div>" << std::endl;
iconFile << " <div class='mod-light on' mod-role='bypass-light'></div>" << std::endl;
iconFile << " <div class='mod-control-group mod-switch'>" << std::endl;
iconFile << " <div class='mod-control-group mod-switch-image mod-port transport' mod-role='bypass' mod-widget='film'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " <div class='canvas_wrapper'>" << std::endl;
iconFile << " <canvas oncontextmenu='event.preventDefault()' tabindex=-1></canvas>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " <div class='mod-pedal-input'>" << std::endl;
iconFile << " {{#effect.ports.audio.input}}" << std::endl;
iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.audio.input}}" << std::endl;
iconFile << " {{#effect.ports.midi.input}}" << std::endl;
iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.midi.input}}" << std::endl;
iconFile << " {{#effect.ports.cv.input}}" << std::endl;
iconFile << " <div class='mod-input mod-input-disconnected' title='{{name}}' mod-role='input-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-input-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.cv.input}}" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " <div class='mod-pedal-output'>" << std::endl;
iconFile << " {{#effect.ports.audio.output}}" << std::endl;
iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-audio-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.audio.output}}" << std::endl;
iconFile << " {{#effect.ports.midi.output}}" << std::endl;
iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-midi-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.midi.output}}" << std::endl;
iconFile << " {{#effect.ports.cv.output}}" << std::endl;
iconFile << " <div class='mod-output mod-output-disconnected' title='{{name}}' mod-role='output-cv-port' mod-port-symbol='{{symbol}}'>" << std::endl;
iconFile << " <div class='mod-pedal-output-image'></div>" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << " {{/effect.ports.cv.output}}" << std::endl;
iconFile << " </div>" << std::endl;
iconFile << "</div>" << std::endl;

iconFile.close();
std::cout << " done!" << std::endl;
}

{
std::cout << "Writing modgui/stylesheet.css..."; std::cout.flush();
std::fstream stylesheetFile("modgui/stylesheet.css", std::ios::out);

stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal{" << std::endl;
stylesheetFile << " padding:0;" << std::endl;
stylesheetFile << " margin:0;" << std::endl;
stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl;
stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT + 50) + "px;" << std::endl;
stylesheetFile << " background:#2a2e32;" << std::endl;
stylesheetFile << " border-radius:20px 20px 0 0;" << std::endl;
stylesheetFile << " color:#fff;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper{" << std::endl;
stylesheetFile << " --device-pixel-ratio:1;" << std::endl;
stylesheetFile << " /*image-rendering:pixelated;*/" << std::endl;
stylesheetFile << " /*image-rendering:crisp-edges;*/" << std::endl;
stylesheetFile << " background:#000;" << std::endl;
stylesheetFile << " position:absolute;" << std::endl;
stylesheetFile << " top:50px;" << std::endl;
stylesheetFile << " transform-origin:0 0 0;" << std::endl;
stylesheetFile << " transform:scale(calc(1/var(--device-pixel-ratio)));" << std::endl;
stylesheetFile << " width:" + String(DISTRHO_UI_DEFAULT_WIDTH) + "px;" << std::endl;
stylesheetFile << " height:" + String(DISTRHO_UI_DEFAULT_HEIGHT) + "px;" << std::endl;
stylesheetFile << " text-align:center;" << std::endl;
stylesheetFile << " z-index:21;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "/*" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .canvas_wrapper:focus-within{" << std::endl;
stylesheetFile << " z-index:21;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "*/" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-plugin-title{" << std::endl;
stylesheetFile << " position:absolute;" << std::endl;
stylesheetFile << " text-align:center;" << std::endl;
stylesheetFile << " width:100%;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal h1{" << std::endl;
stylesheetFile << " font-size:20px;" << std::endl;
stylesheetFile << " font-weight:bold;" << std::endl;
stylesheetFile << " line-height:50px;" << std::endl;
stylesheetFile << " margin:0;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-control-group{" << std::endl;
stylesheetFile << " position:absolute;" << std::endl;
stylesheetFile << " left:5px;" << std::endl;
stylesheetFile << " z-index:35;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-input," << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-pedal-output{" << std::endl;
stylesheetFile << " top:75px;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-input," << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-audio-output{" << std::endl;
stylesheetFile << " margin-bottom:25px;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .jack-disconnected{" << std::endl;
stylesheetFile << " top:0px!important;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image{" << std::endl;
stylesheetFile << " background-image: url(/img/switch.png);" << std::endl;
stylesheetFile << " background-position: left center;" << std::endl;
stylesheetFile << " background-repeat: no-repeat;" << std::endl;
stylesheetFile << " background-size: auto 50px;" << std::endl;
stylesheetFile << " font-weight: bold;" << std::endl;
stylesheetFile << " width: 100px;" << std::endl;
stylesheetFile << " height: 50px;" << std::endl;
stylesheetFile << " cursor: pointer;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.off{" << std::endl;
stylesheetFile << " background-position: right center !important;" << std::endl;
stylesheetFile << "}" << std::endl;
stylesheetFile << "." DISTRHO_PLUGIN_MODGUI_CLASS_NAME ".mod-pedal .mod-switch-image.on{" << std::endl;
stylesheetFile << " background-position: left center !important;" << std::endl;
stylesheetFile << "}" << std::endl;

stylesheetFile.close();
std::cout << " done!" << std::endl;
}
#endif // DISTRHO_PLUGIN_USES_MODGUI && !DISTRHO_PLUGIN_USES_CUSTOM_MODGUI

// ---------------------------------------------

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
@@ -1234,7 +1563,7 @@ void lv2_generate_ttl(const char* const basename)
addAttribute(uiString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4);
addAttribute(uiString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true);

uiFile << uiString << std::endl;
uiFile << uiString;
uiFile.close();
std::cout << " done!" << std::endl;
}
@@ -1310,7 +1639,7 @@ void lv2_generate_ttl(const char* const basename)

presetString += " <";

if (plugin.getStateHints(i) & kStateIsHostReadable)
if (plugin.getStateHints(j) & kStateIsHostReadable)
presetString += DISTRHO_PLUGIN_URI "#";
else
presetString += DISTRHO_PLUGIN_LV2_STATE_PREFIX;
@@ -1371,7 +1700,7 @@ void lv2_generate_ttl(const char* const basename)
presetsString += presetString;
}

presetsFile << presetsString << std::endl;
presetsFile << presetsString;
presetsFile.close();
std::cout << " done!" << std::endl;
}


+ 1
- 1
dpf/distrho/src/DistrhoPluginVST2.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this


+ 2
- 2
dpf/distrho/src/DistrhoPluginVST3.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -4810,7 +4810,7 @@ struct dpf_factory : v3_plugin_factory_cpp {
d_strncpy(info->name, sPlugin->getName(), ARRAY_SIZE(info->name));
d_strncpy(info->vendor, sPlugin->getMaker(), ARRAY_SIZE(info->vendor));
d_strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
d_strncpy(info->sdk_version, "Travesty 3.7.4", ARRAY_SIZE(info->sdk_version));
d_strncpy(info->sdk_version, "VST 3.7.4", ARRAY_SIZE(info->sdk_version));

if (idx == 0)
{


+ 2
- 1
dpf/distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -14,6 +14,7 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "DistrhoDetails.hpp"
#include "src/DistrhoPluginChecks.h"
#include "src/DistrhoDefines.h"



+ 3
- 2
dpf/distrho/src/DistrhoUIInternal.hpp View File

@@ -57,9 +57,10 @@ public:
void* const dspPtr = nullptr,
const double scaleFactor = 0.0,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff)
const uint32_t fgColor = 0xffffffff,
const char* const appClassName = nullptr)
: ui(nullptr),
uiData(new UI::PrivateData())
uiData(new UI::PrivateData(appClassName))
{
uiData->sampleRate = sampleRate;
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr;


+ 224
- 13
dpf/distrho/src/DistrhoUILV2.cpp View File

@@ -78,7 +78,8 @@ public:
const float sampleRate,
const float scaleFactor,
const uint32_t bgColor,
const uint32_t fgColor)
const uint32_t fgColor,
const char* const appClassName)
: fUridMap(uridMap),
fUridUnmap(getLv2Feature<LV2_URID_Unmap>(features, LV2_URID__unmap)),
fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
@@ -97,7 +98,7 @@ public:
sendNoteCallback,
nullptr, // resize is very messy, hosts can do it without extensions
fileRequestCallback,
bundlePath, dspPtr, scaleFactor, bgColor, fgColor)
bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName)
{
if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
@@ -113,10 +114,11 @@ public:
// if winId == 0 then options must not be null
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);

#ifndef __EMSCRIPTEN__
const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId);

bool hasTitle = false;
const char* windowTitle = nullptr;

for (int i=0; options[i].key != 0; ++i)
{
@@ -134,19 +136,18 @@ public:
{
if (options[i].type == fURIDs.atomString)
{
if (const char* const windowTitle = (const char*)options[i].value)
{
hasTitle = true;
fUI.setWindowTitle(windowTitle);
}
windowTitle = (const char*)options[i].value;
}
else
d_stderr("Host provides windowTitle but has wrong value type");
}
}

if (! hasTitle)
fUI.setWindowTitle(DISTRHO_PLUGIN_NAME);
if (windowTitle == nullptr)
windowTitle = DISTRHO_PLUGIN_NAME;

fUI.setWindowTitle(windowTitle);
#endif
}

// -------------------------------------------------------------------
@@ -213,9 +214,14 @@ public:
fUI.stateChanged(key, value);
}
}
else if (atom->type == fURIDs.midiEvent)
{
// ignore
}
else
{
d_stdout("DPF :: received atom not handled");
d_stdout("DPF :: received atom not handled :: %s",
fUridUnmap != nullptr ? fUridUnmap->unmap(fUridUnmap->handle, atom->type) : "(null)");
}
}
#endif
@@ -258,7 +264,7 @@ public:
if (options[i].type == fURIDs.atomFloat)
{
const float sampleRate = *(const float*)options[i].value;
fUI.setSampleRate(sampleRate);
fUI.setSampleRate(sampleRate, true);
continue;
}
else
@@ -559,17 +565,20 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
float scaleFactor = 0.0f;
uint32_t bgColor = 0;
uint32_t fgColor = 0xffffffff;
const char* appClassName = nullptr;

if (options != nullptr)
{
const LV2_URID uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int);
const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float);
const LV2_URID uridAtomString = uridMap->map(uridMap->handle, LV2_ATOM__String);
const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
const LV2_URID uridBgColor = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
const LV2_URID uridFgColor = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
#ifndef DISTRHO_OS_MAC
const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
#endif
const LV2_URID uridClassName = uridMap->map(uridMap->handle, "urn:distrho:className");

for (int i=0; options[i].key != 0; ++i)
{
@@ -603,6 +612,13 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
d_stderr("Host provides UI scale factor but has wrong value type");
}
#endif
else if (options[i].key == uridClassName)
{
if (options[i].type == uridAtomString)
appClassName = (const char*)options[i].value;
else
d_stderr("Host provides UI scale factor but has wrong value type");
}
}
}

@@ -614,7 +630,7 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,

return new UiLv2(bundlePath, winId, options, uridMap, features,
controller, writeFunction, widget, instance,
sampleRate, scaleFactor, bgColor, fgColor);
sampleRate, scaleFactor, bgColor, fgColor, appClassName);
}

#define uiPtr ((UiLv2*)ui)
@@ -715,4 +731,199 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
return (index == 0) ? &sLv2UiDescriptor : nullptr;
}

#if defined(__MOD_DEVICES__) && defined(__EMSCRIPTEN__)
#include <emscripten/html5.h>
#include <string>

typedef void (*_custom_param_set)(uint32_t port_index, float value);
typedef void (*_custom_patch_set)(const char* uri, const char* value);

struct ModguiHandle {
LV2UI_Handle handle;
long loop_id;
_custom_param_set param_set;
_custom_patch_set patch_set;
};

enum URIs {
kUriNull,
kUriAtomEventTransfer,
kUriDpfKeyValue,
};

static std::vector<std::string> kURIs;

static LV2_URID lv2_urid_map(LV2_URID_Map_Handle, const char* const uri)
{
for (size_t i=0, size=kURIs.size(); i<size; ++i)
{
if (kURIs[i] == uri)
return i;
}

kURIs.push_back(uri);
return kURIs.size() - 1u;
}

static const char* lv2_urid_unmap(LV2_URID_Map_Handle, const LV2_URID urid)
{
return kURIs[urid].c_str();
}

static void lv2ui_write_function(LV2UI_Controller controller,
uint32_t port_index,
uint32_t buffer_size,
uint32_t port_protocol,
const void* buffer)
{
DISTRHO_SAFE_ASSERT_RETURN(buffer_size >= 1,);

// d_stdout("lv2ui_write_function %p %u %u %u %p", controller, port_index, buffer_size, port_protocol, buffer);
ModguiHandle* const mhandle = static_cast<ModguiHandle*>(controller);

switch (port_protocol)
{
case kUriNull:
mhandle->param_set(port_index, *static_cast<const float*>(buffer));
break;
case kUriAtomEventTransfer:
if (const LV2_Atom* const atom = static_cast<const LV2_Atom*>(buffer))
{
// d_stdout("lv2ui_write_function %u %u:%s", atom->size, atom->type, kURIs[atom->type].c_str());

// if (kURIs[atom->type] == "urn:distrho:KeyValueState")
{
const char* const key = (const char*)(atom + 1);
const char* const value = key + (std::strlen(key) + 1U);
// d_stdout("lv2ui_write_function %s %s", key, value);

String urikey;
urikey = DISTRHO_PLUGIN_URI "#";
urikey += key;

mhandle->patch_set(urikey, value);
}
}
break;
}
}

static void app_idle(void* const handle)
{
static_cast<UiLv2*>(handle)->lv2ui_idle();
}

DISTRHO_PLUGIN_EXPORT
LV2UI_Handle modgui_init(const char* const className, _custom_param_set param_set, _custom_patch_set patch_set)
{
d_stdout("init \"%s\"", className);
DISTRHO_SAFE_ASSERT_RETURN(className != nullptr, nullptr);

static LV2_URID_Map uridMap = { nullptr, lv2_urid_map };
static LV2_URID_Unmap uridUnmap = { nullptr, lv2_urid_unmap };

// known first URIDs, matching URIs
if (kURIs.empty())
{
kURIs.push_back("");
kURIs.push_back("http://lv2plug.in/ns/ext/atom#eventTransfer");
kURIs.push_back(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState");
}

static float sampleRateValue = 48000.f;
static LV2_Options_Option options[3] = {
{
LV2_OPTIONS_INSTANCE,
0,
uridMap.map(uridMap.handle, LV2_PARAMETERS__sampleRate),
sizeof(float),
uridMap.map(uridMap.handle, LV2_ATOM__Float),
&sampleRateValue
},
{
LV2_OPTIONS_INSTANCE,
0,
uridMap.map(uridMap.handle, "urn:distrho:className"),
std::strlen(className) + 1,
uridMap.map(uridMap.handle, LV2_ATOM__String),
className
},
{}
};

static const LV2_Feature optionsFt = { LV2_OPTIONS__options, static_cast<void*>(options) };
static const LV2_Feature uridMapFt = { LV2_URID__map, static_cast<void*>(&uridMap) };
static const LV2_Feature uridUnmapFt = { LV2_URID__unmap, static_cast<void*>(&uridUnmap) };

static const LV2_Feature* features[] = {
&optionsFt,
&uridMapFt,
&uridUnmapFt,
nullptr
};

ModguiHandle* const mhandle = new ModguiHandle;
mhandle->handle = nullptr;
mhandle->loop_id = 0;
mhandle->param_set = param_set;
mhandle->patch_set = patch_set;

LV2UI_Widget widget;
const LV2UI_Handle handle = lv2ui_instantiate(&sLv2UiDescriptor,
DISTRHO_PLUGIN_URI,
"", // bundlePath
lv2ui_write_function,
mhandle,
&widget,
features);
mhandle->handle = handle;

static_cast<UiLv2*>(handle)->lv2ui_show();
mhandle->loop_id = emscripten_set_interval(app_idle, 1000.0/60, handle);

return mhandle;
}

DISTRHO_PLUGIN_EXPORT
void modgui_param_set(const LV2UI_Handle handle, const uint32_t index, const float value)
{
lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle, index, sizeof(float), kUriNull, &value);
}

DISTRHO_PLUGIN_EXPORT
void modgui_patch_set(const LV2UI_Handle handle, const char* const uri, const char* const value)
{
static const constexpr uint32_t URI_PREFIX_LEN = sizeof(DISTRHO_PLUGIN_URI);
DISTRHO_SAFE_ASSERT_RETURN(std::strncmp(uri, DISTRHO_PLUGIN_URI "#", URI_PREFIX_LEN) == 0,);

const uint32_t keySize = std::strlen(uri + URI_PREFIX_LEN) + 1;
const uint32_t valueSize = std::strlen(value) + 1;
const uint32_t atomSize = sizeof(LV2_Atom) + keySize + valueSize;

LV2_Atom* const atom = static_cast<LV2_Atom*>(std::malloc(atomSize));
atom->size = atomSize;
atom->type = kUriDpfKeyValue;

std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)), uri + URI_PREFIX_LEN, keySize);
std::memcpy(static_cast<uint8_t*>(static_cast<void*>(atom + 1)) + keySize, value, valueSize);

lv2ui_port_event(static_cast<ModguiHandle*>(handle)->handle,
DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS, // events input port
atomSize, kUriAtomEventTransfer, atom);

std::free(atom);
}

DISTRHO_PLUGIN_EXPORT
void modgui_cleanup(const LV2UI_Handle handle)
{
d_stdout("cleanup");
ModguiHandle* const mhandle = static_cast<ModguiHandle*>(handle);
if (mhandle->loop_id != 0)
emscripten_clear_interval(mhandle->loop_id);
lv2ui_cleanup(mhandle->handle);
delete mhandle;
}
#endif

// -----------------------------------------------------------------------

+ 54
- 35
dpf/distrho/src/DistrhoUIPrivateData.hpp View File

@@ -32,6 +32,11 @@
# include "../../dgl/src/pugl.hpp"
#endif

#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
# include <map>
# include <string>
#endif

#if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
# define DISTRHO_UI_IS_STANDALONE 1
#else
@@ -60,7 +65,7 @@ struct PluginApplication
DGL_NAMESPACE::IdleCallback* idleCallback;
UI* ui;

explicit PluginApplication()
explicit PluginApplication(const char*)
: idleCallback(nullptr),
ui(nullptr) {}

@@ -105,20 +110,26 @@ struct PluginApplication
class PluginApplication : public DGL_NAMESPACE::Application
{
public:
explicit PluginApplication()
explicit PluginApplication(const char* className)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE)
{
#ifndef DISTRHO_OS_WASM
const char* const className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
#if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__)
if (className == nullptr)
{
className = (
#ifdef DISTRHO_PLUGIN_BRAND
DISTRHO_PLUGIN_BRAND
#else
DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
#endif
"-" DISTRHO_PLUGIN_NAME
);
}
setClassName(className);
#endif
#else
// unused
(void)className;
#endif
}

void triggerIdleCallbacks()
@@ -320,9 +331,10 @@ struct UI::PrivateData {
uint fgColor;
double scaleFactor;
uintptr_t winId;
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
char* uiStateFileKeyRequest;
#endif
std::map<std::string,std::string> lastUsedDirnames;
#endif
char* bundlePath;

// Ignore initial resize events while initializing
@@ -337,8 +349,8 @@ struct UI::PrivateData {
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData() noexcept
: app(),
PrivateData(const char* const appClassName) noexcept
: app(appClassName),
window(nullptr),
sampleRate(0),
parameterOffset(0),
@@ -347,9 +359,9 @@ struct UI::PrivateData {
fgColor(0xffffffff),
scaleFactor(1.0),
winId(0),
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
uiStateFileKeyRequest(nullptr),
#endif
#endif
bundlePath(nullptr),
initializing(true),
callbacksPtr(nullptr),
@@ -360,32 +372,32 @@ struct UI::PrivateData {
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
#if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
# if DISTRHO_PLUGIN_WANT_LATENCY
#if DISTRHO_PLUGIN_WANT_LATENCY
parameterOffset += 1;
# endif
#endif
#endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_LV2
# if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
#ifdef DISTRHO_PLUGIN_TARGET_LV2
#if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
# if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
#endif
#if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
parameterOffset += 1;
# endif
#endif
#endif
#endif

#ifdef DISTRHO_PLUGIN_TARGET_VST3
#ifdef DISTRHO_PLUGIN_TARGET_VST3
parameterOffset += kVst3InternalParameterCount;
#endif
#endif
}

~PrivateData() noexcept
{
#if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
std::free(uiStateFileKeyRequest);
#endif
#endif
std::free(bundlePath);
}

@@ -438,7 +450,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
if (fileRequestCallbackFunc != nullptr)
return fileRequestCallbackFunc(callbacksPtr, key);

#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
std::free(uiStateFileKeyRequest);
uiStateFileKeyRequest = strdup(key);
DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);
@@ -449,8 +461,10 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)

DGL_NAMESPACE::FileBrowserOptions opts;
opts.title = title;
if (lastUsedDirnames.count(key))
opts.startDir = lastUsedDirnames[key].c_str();
return window->openFileBrowser(opts);
#endif
#endif

return false;
}
@@ -466,7 +480,7 @@ inline void PluginWindow::onFileSelected(const char* const filename)
if (initializing)
return;

#if DISTRHO_PLUGIN_WANT_STATE
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
if (char* const key = ui->uiData->uiStateFileKeyRequest)
{
ui->uiData->uiStateFileKeyRequest = nullptr;
@@ -474,8 +488,13 @@ inline void PluginWindow::onFileSelected(const char* const filename)
{
// notify DSP
ui->setState(key, filename);

// notify UI
ui->stateChanged(key, filename);

// save dirname for next time
if (const char* const lastsep = std::strrchr(filename, DISTRHO_OS_SEP))
ui->uiData->lastUsedDirnames[key] = std::string(filename, lastsep-filename);
}
std::free(key);
return;


+ 1
- 1
dpf/distrho/src/DistrhoUIVST3.cpp View File

@@ -233,7 +233,7 @@ private:
HWND fTimerWindow;
String fTimerWindowClassName;

WINAPI static void platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
static void WINAPI platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
{
reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback();
}


+ 20
- 1
dpf/distrho/src/jackbridge/JackBridge.cpp View File

@@ -934,8 +934,27 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options,
delete nativeBridge;
#endif
#endif

if (status != nullptr)
*status = JackServerError;
{
int err = JackServerError;

#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT))
if (nativeBridge != nullptr)
{
err
#ifdef HAVE_JACK
|=
#else
=
#endif
JackBridgeNativeFailed;
}
#endif

*status = static_cast<jack_status_t>(err);
}

return nullptr;
}



+ 2
- 1
dpf/distrho/src/jackbridge/JackBridge.hpp View File

@@ -126,7 +126,8 @@ enum JackStatus {
JackShmFailure = 0x0200,
JackVersionError = 0x0400,
JackBackendError = 0x0800,
JackClientZombie = 0x1000
JackClientZombie = 0x1000,
JackBridgeNativeFailed = 0x10000
};

enum JackLatencyCallbackMode {


+ 63
- 34
dpf/distrho/src/jackbridge/NativeBridge.hpp View File

@@ -1,6 +1,6 @@
/*
* Native Bridge for DPF
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -21,6 +21,18 @@

#include "../../extra/RingBuffer.hpp"

#if DISTRHO_PLUGIN_NUM_INPUTS > 2
# define DISTRHO_PLUGIN_NUM_INPUTS_2 2
#else
# define DISTRHO_PLUGIN_NUM_INPUTS_2 DISTRHO_PLUGIN_NUM_INPUTS
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 2
# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 2
#else
# define DISTRHO_PLUGIN_NUM_OUTPUTS_2 DISTRHO_PLUGIN_NUM_OUTPUTS
#endif

using DISTRHO_NAMESPACE::HeapRingBuffer;

struct NativeBridge {
@@ -31,6 +43,8 @@ struct NativeBridge {
// Port caching information
uint numAudioIns;
uint numAudioOuts;
uint numCvIns;
uint numCvOuts;
uint numMidiIns;
uint numMidiOuts;

@@ -43,9 +57,10 @@ struct NativeBridge {
// Runtime buffers
enum PortMask {
kPortMaskAudio = 0x1000,
kPortMaskMIDI = 0x2000,
kPortMaskInput = 0x4000,
kPortMaskOutput = 0x8000,
kPortMaskCV = 0x2000,
kPortMaskMIDI = 0x4000,
kPortMaskInput = 0x10000,
kPortMaskOutput = 0x20000,
kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI,
kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI,
};
@@ -71,6 +86,8 @@ struct NativeBridge {
sampleRate(0),
numAudioIns(0),
numAudioOuts(0),
numCvIns(0),
numCvOuts(0),
numMidiIns(0),
numMidiOuts(0),
jackProcessCallback(nullptr),
@@ -211,27 +228,27 @@ struct NativeBridge {

if (audio)
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)];
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
audioBufferStorage = new float[bufferSize*(DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS)];

for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
audioBuffers[i] = audioBufferStorage + (bufferSize * i);
#endif
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
audioBuffers[i] = audioBufferStorage + (bufferSize * i);
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS);
#endif
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
std::memset(audioBufferStorage, 0, sizeof(float)*bufferSize*DISTRHO_PLUGIN_NUM_INPUTS);
#endif
}

if (midi)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.createBuffer(2048);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.createBuffer(2048);
#endif
}
}

@@ -252,27 +269,39 @@ struct NativeBridge {

jack_port_t* registerPort(const char* const type, const ulong flags)
{
bool isAudio, isInput;

/**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0)
isAudio = true;
else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0)
isAudio = false;
else
return nullptr;
uintptr_t ret = 0;

/**/ if (flags & JackPortIsInput)
isInput = true;
ret |= kPortMaskInput;
else if (flags & JackPortIsOutput)
isInput = false;
ret |= kPortMaskOutput;
else
return nullptr;

const uintptr_t ret = (isAudio ? kPortMaskAudio : kPortMaskMIDI)
| (isInput ? kPortMaskInput : kPortMaskOutput);
/**/ if (std::strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0)
{
if (flags & JackPortIsControlVoltage)
{
ret |= kPortMaskAudio;
ret += flags & JackPortIsInput ? numAudioIns++ : numAudioOuts++;
}
else
{
ret |= kPortMaskCV;
ret += flags & JackPortIsInput ? numCvIns++ : numCvOuts++;
}
}
else if (std::strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0)
{
ret |= kPortMaskMIDI;
ret += flags & JackPortIsInput ? numMidiIns++ : numMidiOuts++;
}
else
{
return nullptr;
}

return (jack_port_t*)(ret + (isAudio ? (isInput ? numAudioIns++ : numAudioOuts++)
: (isInput ? numMidiIns++ : numMidiOuts++)));
return (jack_port_t*)ret;
}

void* getPortBuffer(jack_port_t* const port)
@@ -281,7 +310,7 @@ struct NativeBridge {
DISTRHO_SAFE_ASSERT_RETURN(portMask != 0x0, nullptr);

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (portMask & kPortMaskAudio)
if (portMask & (kPortMaskAudio|kPortMaskCV))
return audioBuffers[(portMask & kPortMaskInput ? 0 : DISTRHO_PLUGIN_NUM_INPUTS) + (portMask & 0x0fff)];
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT


+ 26
- 12
dpf/distrho/src/jackbridge/RtAudioBridge.hpp View File

@@ -28,9 +28,9 @@
# define RTAUDIO_API_TYPE MACOSX_CORE
# define RTMIDI_API_TYPE MACOSX_CORE
#elif defined(DISTRHO_OS_WINDOWS) && !defined(_MSC_VER)
# define __WINDOWS_DS__
# define __WINDOWS_WASAPI__
# define __WINDOWS_MM__
# define RTAUDIO_API_TYPE WINDOWS_DS
# define RTAUDIO_API_TYPE WINDOWS_WASAPI
# define RTMIDI_API_TYPE WINDOWS_MM
#else
# if defined(HAVE_PULSEAUDIO)
@@ -50,7 +50,9 @@
# include "rtmidi/RtMidi.h"
# include "../../extra/ScopedPointer.hpp"
# include "../../extra/String.hpp"
# include "../../extra/ScopedDenormalDisable.hpp"

using DISTRHO_NAMESPACE::ScopedDenormalDisable;
using DISTRHO_NAMESPACE::ScopedPointer;
using DISTRHO_NAMESPACE::String;

@@ -281,13 +283,20 @@ struct RtAudioBridge : NativeBridge {
return ok;
}

bool _open(const bool withInput)
bool _open(const bool withInput, RtAudio* tryingAgain = nullptr)
{
ScopedPointer<RtAudio> rtAudio;

try {
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);
if (tryingAgain == nullptr)
{
try {
rtAudio = new RtAudio(RtAudio::RTAUDIO_API_TYPE);
} DISTRHO_SAFE_EXCEPTION_RETURN("new RtAudio()", false);
}
else
{
rtAudio = tryingAgain;
}

uint rtAudioBufferFrames = nextBufferSize;

@@ -300,15 +309,15 @@ struct RtAudioBridge : NativeBridge {
if (withInput)
{
inParams.deviceId = rtAudio->getDefaultInputDevice();
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS;
inParams.nChannels = DISTRHO_PLUGIN_NUM_INPUTS_2;
inParamsPtr = &inParams;
}
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
RtAudio::StreamParameters outParams;
outParams.deviceId = rtAudio->getDefaultOutputDevice();
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS;
outParams.deviceId = tryingAgain != nullptr ? 1 : rtAudio->getDefaultOutputDevice();
outParams.nChannels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
RtAudio::StreamParameters* const outParamsPtr = &outParams;
#else
RtAudio::StreamParameters* const outParamsPtr = nullptr;
@@ -330,6 +339,10 @@ struct RtAudioBridge : NativeBridge {
rtAudio->openStream(outParamsPtr, inParamsPtr, RTAUDIO_FLOAT32, 48000, &rtAudioBufferFrames,
RtAudioCallback, this, &opts, nullptr);
} catch (const RtAudioError& err) {
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (outParams.deviceId == 0 && rtAudio->getDeviceCount() > 1)
return _open(withInput, rtAudio.release());
#endif
d_safe_exception(err.getMessage().c_str(), __FILE__, __LINE__);
return false;
} DISTRHO_SAFE_EXCEPTION_RETURN("rtAudio->openStream()", false);
@@ -357,14 +370,14 @@ struct RtAudioBridge : NativeBridge {
if (self->jackProcessCallback == nullptr)
{
if (outputBuffer != nullptr)
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS);
std::memset((float*)outputBuffer, 0, sizeof(float)*numFrames*DISTRHO_PLUGIN_NUM_OUTPUTS_2);
return 0;
}

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
if (float* const insPtr = static_cast<float*>(inputBuffer))
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
self->audioBuffers[i] = insPtr + (i * numFrames);
}
#endif
@@ -372,11 +385,12 @@ struct RtAudioBridge : NativeBridge {
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
if (float* const outsPtr = static_cast<float*>(outputBuffer))
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i] = outsPtr + (i * numFrames);
}
#endif

const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

return 0;


+ 44
- 29
dpf/distrho/src/jackbridge/SDL2Bridge.hpp View File

@@ -1,6 +1,6 @@
/*
* SDL Bridge for DPF
* Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
* or without fee is hereby granted, provided that the above copyright notice and this
@@ -18,6 +18,7 @@
#define SDL_BRIDGE_HPP_INCLUDED

#include "NativeBridge.hpp"
#include "../../extra/ScopedDenormalDisable.hpp"

#include <SDL.h>

@@ -67,7 +68,7 @@ struct SDL2Bridge : NativeBridge {

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Capure");
requested.channels = DISTRHO_PLUGIN_NUM_INPUTS;
requested.channels = DISTRHO_PLUGIN_NUM_INPUTS_2;
requested.callback = AudioInputCallback;

SDL_AudioSpec receivedCapture;
@@ -75,11 +76,12 @@ struct SDL2Bridge : NativeBridge {
SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE);
if (captureDeviceId == 0)
{
d_stderr2("Failed to open SDL playback device, error was: %s", SDL_GetError());
d_stderr2("Failed to open SDL capture device, error was: %s", SDL_GetError());
#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
return false;
#endif
}

if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS)
else if (receivedCapture.channels != DISTRHO_PLUGIN_NUM_INPUTS_2)
{
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
@@ -91,7 +93,7 @@ struct SDL2Bridge : NativeBridge {
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
SDL_AudioSpec receivedPlayback;
SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, "Playback");
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS;
requested.channels = DISTRHO_PLUGIN_NUM_OUTPUTS_2;
requested.callback = AudioOutputCallback;

playbackDeviceId = SDL_OpenAudioDevice(nullptr, 0, &requested, &receivedPlayback,
@@ -102,7 +104,7 @@ struct SDL2Bridge : NativeBridge {
return false;
}

if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS)
if (receivedPlayback.channels != DISTRHO_PLUGIN_NUM_OUTPUTS_2)
{
SDL_CloseAudioDevice(playbackDeviceId);
playbackDeviceId = 0;
@@ -113,30 +115,39 @@ struct SDL2Bridge : NativeBridge {

#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
// if using both input and output, make sure they match
if (receivedCapture.samples != receivedPlayback.samples)
if (receivedCapture.samples != receivedPlayback.samples && captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
SDL_CloseAudioDevice(playbackDeviceId);
captureDeviceId = playbackDeviceId = 0;
d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedCapture.samples);
d_stderr2("Mismatch buffer size %u vs %u", receivedCapture.samples, receivedPlayback.samples);
return false;
}
if (receivedCapture.freq != receivedPlayback.freq)
if (receivedCapture.freq != receivedPlayback.freq && captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
SDL_CloseAudioDevice(playbackDeviceId);
captureDeviceId = playbackDeviceId = 0;
d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedCapture.freq);
d_stderr2("Mismatch sample rate %u vs %u", receivedCapture.freq, receivedPlayback.freq);
return false;
}
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS > 0
bufferSize = receivedCapture.samples;
sampleRate = receivedCapture.freq;
#else
bufferSize = receivedPlayback.samples;
sampleRate = receivedPlayback.freq;
if (captureDeviceId != 0)
{
bufferSize = receivedCapture.samples;
sampleRate = receivedCapture.freq;
}
#endif
#if DISTRHO_PLUGIN_NUM_INPUTS > 0 && DISTRHO_PLUGIN_NUM_OUTPUTS > 0
else
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
{
bufferSize = receivedPlayback.samples;
sampleRate = receivedPlayback.freq;
}
#endif

allocBuffers(true, false);
@@ -146,9 +157,11 @@ struct SDL2Bridge : NativeBridge {
bool close() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
if (captureDeviceId != 0)
{
SDL_CloseAudioDevice(captureDeviceId);
captureDeviceId = 0;
}
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
@@ -163,8 +176,8 @@ struct SDL2Bridge : NativeBridge {
bool activate() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_PauseAudioDevice(captureDeviceId, 0);
if (captureDeviceId != 0)
SDL_PauseAudioDevice(captureDeviceId, 0);
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
@@ -176,8 +189,8 @@ struct SDL2Bridge : NativeBridge {
bool deactivate() override
{
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(captureDeviceId != 0, false);
SDL_PauseAudioDevice(captureDeviceId, 1);
if (captureDeviceId != 0)
SDL_PauseAudioDevice(captureDeviceId, 1);
#endif
#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
DISTRHO_SAFE_ASSERT_RETURN(playbackDeviceId != 0, false);
@@ -198,19 +211,20 @@ struct SDL2Bridge : NativeBridge {
if (self->jackProcessCallback == nullptr)
return;

const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS);
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_INPUTS_2);
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);

const float* const fstream = (const float*)stream;

for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_INPUTS_2; ++i)
{
for (uint j=0; j<numFrames; ++j)
self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS + i];
self->audioBuffers[i][j] = fstream[j * DISTRHO_PLUGIN_NUM_INPUTS_2 + i];
}

#if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
// if there are no outputs, run process callback now
const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);
#endif
}
@@ -231,17 +245,18 @@ struct SDL2Bridge : NativeBridge {
return;
}

const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS);
const uint numFrames = static_cast<uint>(len / sizeof(float) / DISTRHO_PLUGIN_NUM_OUTPUTS_2);
DISTRHO_SAFE_ASSERT_UINT2_RETURN(numFrames == self->bufferSize, numFrames, self->bufferSize,);

const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

float* const fstream = (float*)stream;

for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
{
for (uint j=0; j < numFrames; ++j)
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
fstream[j * DISTRHO_PLUGIN_NUM_OUTPUTS_2 + i] = self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i][j];
}
}
#endif


+ 34
- 14
dpf/distrho/src/jackbridge/WebBridge.hpp View File

@@ -163,7 +163,7 @@ struct WebBridge : NativeBridge {
if (WAB.audioContext.state === 'suspended')
WAB.audioContext.resume();
});
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this);
}, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this);

return true;
}
@@ -217,11 +217,11 @@ struct WebBridge : NativeBridge {
constraints['audio'] = true;
constraints['video'] = false;
constraints['autoGainControl'] = {};
constraints['autoGainControl']['exact'] = false;
constraints['autoGainControl']['ideal'] = false;
constraints['echoCancellation'] = {};
constraints['echoCancellation']['exact'] = false;
constraints['echoCancellation']['ideal'] = false;
constraints['noiseSuppression'] = {};
constraints['noiseSuppression']['exact'] = false;
constraints['noiseSuppression']['ideal'] = false;
constraints['channelCount'] = {};
constraints['channelCount']['min'] = 0;
constraints['channelCount']['ideal'] = numInputs;
@@ -236,6 +236,25 @@ struct WebBridge : NativeBridge {
constraints['googAutoGainControl'] = false;

var success = function(stream) {
var track = stream.getAudioTracks()[0];

// try to force as much as we can
track.applyConstraints({'autoGainControl': { 'exact': false } })
.then(function(){console.log("Mic/Input auto-gain control has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input auto-gain")});

track.applyConstraints({'echoCancellation': { 'exact': false } })
.then(function(){console.log("Mic/Input echo-cancellation has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input echo-cancellation")});

track.applyConstraints({'noiseSuppression': { 'exact': false } })
.then(function(){console.log("Mic/Input noise-suppression has been disabled")})
.catch(function(){console.log("Cannot disable Mic/Input noise-suppression")});

track.applyConstraints({'googAutoGainControl': { 'exact': false } })
.then(function(){})
.catch(function(){});

WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream);
WAB.captureStreamNode.connect(WAB.processor);
};
@@ -247,7 +266,7 @@ struct WebBridge : NativeBridge {
} else if (navigator.webkitGetUserMedia !== undefined) {
navigator.webkitGetUserMedia(constraints, success, fail);
}
}, DISTRHO_PLUGIN_NUM_INPUTS);
}, DISTRHO_PLUGIN_NUM_INPUTS_2);

return true;
}
@@ -279,7 +298,7 @@ struct WebBridge : NativeBridge {
WAB.captureStreamNode.disconnect(WAB.processor);

return 1;
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, newBufferSize) != 0;
}, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, newBufferSize) != 0;

if (!success)
return false;
@@ -292,9 +311,10 @@ struct WebBridge : NativeBridge {
bufferSizeCallback(newBufferSize, jackBufferSizeArg);

EM_ASM({
var numInputs = $0;
var numOutputs = $1;
var bufferSize = $2;
var numInputsR = $0;
var numInputs = $1;
var numOutputs = $2;
var bufferSize = $3;
var WAB = Module['WebAudioBridge'];

// store the new processor
@@ -309,13 +329,13 @@ struct WebBridge : NativeBridge {
var buffer = e['inputBuffer']['getChannelData'](i);
for (var j = 0; j < bufferSize; ++j) {
// setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float');
HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j];
HEAPF32[$4 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j];
}
}
dynCall('vi', $4, [$5]);
dynCall('vi', $5, [$6]);
for (var i = 0; i < numOutputs; ++i) {
var buffer = e['outputBuffer']['getChannelData'](i);
var offset = bufferSize * (numInputs + i);
var offset = bufferSize * (numInputsR + i);
for (var j = 0; j < bufferSize; ++j) {
buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2];
}
@@ -329,7 +349,7 @@ struct WebBridge : NativeBridge {
if (WAB.captureStreamNode)
WAB.captureStreamNode.connect(WAB.processor);

}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this);
}, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_INPUTS_2, DISTRHO_PLUGIN_NUM_OUTPUTS_2, bufferSize, audioBufferStorage, WebAudioCallback, this);

return true;
}
@@ -452,7 +472,7 @@ struct WebBridge : NativeBridge {
}
else
{
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
for (uint i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS_2; ++i)
std::memset(self->audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + i], 0, sizeof(float)*numFrames);
}
}


+ 2
- 5
dpf/distrho/src/jackbridge/rtaudio/RtAudio.cpp View File

@@ -4640,13 +4640,10 @@ void RtApiWasapi::stopStream( void )
stream_.state = STREAM_STOPPING;

// wait until stream thread is stopped
while( stream_.state != STREAM_STOPPED ) {
Sleep( 1 );
for (int i=0; i < 2 && stream_.state != STREAM_STOPPED; ++i ) {
Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
}

// Wait for the last buffer to play before stopping.
Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );

// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";


+ 4
- 2
dpf/utils/generate-ttl.sh View File

@@ -33,7 +33,9 @@ FOLDERS=`find . -type d -name \*.lv2`

for i in ${FOLDERS}; do
cd ${i}
FILE="$(ls *.${EXT} | sort | head -n 1)"
${EXE_WRAPPER} "${GEN}" "./${FILE}"
FILE="$(ls *.${EXT} 2>/dev/null | sort | head -n 1)"
if [ -n "${FILE}" ]; then
${EXE_WRAPPER} "${GEN}" "./${FILE}"
fi
cd ..
done

Loading…
Cancel
Save