Browse Source

Merge branch 'develop' into wasm-worker

pull/509/head
dreamer 5 days ago
parent
commit
eae2e6729f
95 changed files with 5477 additions and 1621 deletions
  1. +44
    -10
      .github/workflows/build.yml
  2. +42
    -62
      .github/workflows/cmake.yml
  3. +9
    -9
      .github/workflows/example-plugins.yml
  4. +6
    -5
      .gitignore
  5. +7
    -7
      CMakeLists.txt
  6. +1
    -1
      LICENSE
  7. +1
    -0
      Makefile
  8. +44
    -38
      Makefile.base.mk
  9. +129
    -53
      Makefile.plugins.mk
  10. +1
    -1
      README.md
  11. +526
    -39
      cmake/DPF-plugin.cmake
  12. +32
    -4
      dgl/Application.hpp
  13. +7
    -3
      dgl/Base.hpp
  14. +3
    -4
      dgl/Cairo.hpp
  15. +17
    -1
      dgl/Color.hpp
  16. +10
    -1
      dgl/EventHandlers.hpp
  17. +9
    -9
      dgl/Geometry.hpp
  18. +10
    -6
      dgl/Image.hpp
  19. +85
    -0
      dgl/Makefile
  20. +5
    -0
      dgl/NanoVG.hpp
  21. +6
    -0
      dgl/OpenGL-include.hpp
  22. +140
    -17
      dgl/OpenGL.hpp
  23. +28
    -0
      dgl/WebView.hpp
  24. +26
    -1
      dgl/Window.hpp
  25. +65
    -3
      dgl/src/Application.cpp
  26. +5
    -13
      dgl/src/ApplicationPrivateData.cpp
  27. +8
    -5
      dgl/src/ApplicationPrivateData.hpp
  28. +64
    -24
      dgl/src/Cairo.cpp
  29. +17
    -1
      dgl/src/Color.cpp
  30. +101
    -8
      dgl/src/EventHandlers.cpp
  31. +10
    -6
      dgl/src/Layout.cpp
  32. +10
    -0
      dgl/src/NanoVG.cpp
  33. +35
    -562
      dgl/src/OpenGL.cpp
  34. +570
    -0
      dgl/src/OpenGL2.cpp
  35. +1025
    -0
      dgl/src/OpenGL3.cpp
  36. +53
    -6
      dgl/src/Stub.cpp
  37. +53
    -7
      dgl/src/Vulkan.cpp
  38. +23
    -0
      dgl/src/WebViewWin32.cpp
  39. +51
    -32
      dgl/src/Window.cpp
  40. +118
    -45
      dgl/src/WindowPrivateData.cpp
  41. +19
    -4
      dgl/src/WindowPrivateData.hpp
  42. +24
    -20
      dgl/src/nanovg/nanovg_gl.h
  43. +92
    -34
      dgl/src/pugl-extra/wasm.c
  44. +1
    -1
      dgl/src/pugl-upstream
  45. +109
    -102
      dgl/src/pugl.cpp
  46. +3
    -3
      dgl/src/pugl.hpp
  47. +28
    -4
      distrho/DistrhoDetails.hpp
  48. +24
    -0
      distrho/DistrhoInfo.hpp
  49. +3
    -4
      distrho/DistrhoPluginMain.cpp
  50. +32
    -3
      distrho/DistrhoPluginUtils.hpp
  51. +10
    -7
      distrho/DistrhoUI.hpp
  52. +14
    -3
      distrho/DistrhoUIMain.cpp
  53. +1
    -0
      distrho/DistrhoUI_win32.cpp
  54. +8
    -1
      distrho/DistrhoUtils.hpp
  55. +12
    -6
      distrho/extra/Base64.hpp
  56. +33
    -10
      distrho/extra/ChildProcess.hpp
  57. +114
    -0
      distrho/extra/Filesystem.hpp
  58. +6
    -2
      distrho/extra/RingBuffer.hpp
  59. +5
    -0
      distrho/extra/ScopedPointer.hpp
  60. +72
    -62
      distrho/extra/String.hpp
  61. +20
    -11
      distrho/extra/WebViewImpl.cpp
  62. +11
    -2
      distrho/extra/WebViewImpl.hpp
  63. +26
    -5
      distrho/extra/WebViewWin32.hpp
  64. +184
    -86
      distrho/src/DistrhoPluginAU.cpp
  65. +67
    -17
      distrho/src/DistrhoPluginCLAP.cpp
  66. +14
    -10
      distrho/src/DistrhoPluginChecks.h
  67. +78
    -65
      distrho/src/DistrhoPluginInternal.hpp
  68. +4
    -8
      distrho/src/DistrhoPluginJACK.cpp
  69. +40
    -9
      distrho/src/DistrhoPluginLV2export.cpp
  70. +158
    -0
      distrho/src/DistrhoPluginMAPI.cpp
  71. +6
    -5
      distrho/src/DistrhoPluginVST2.cpp
  72. +6
    -0
      distrho/src/DistrhoPluginVST3.cpp
  73. +19
    -8
      distrho/src/DistrhoUIAU.mm
  74. +3
    -7
      distrho/src/DistrhoUIDSSI.cpp
  75. +8
    -7
      distrho/src/DistrhoUIInternal.hpp
  76. +16
    -12
      distrho/src/DistrhoUILV2.cpp
  77. +21
    -12
      distrho/src/DistrhoUIPrivateData.hpp
  78. +4
    -3
      distrho/src/DistrhoUIVST3.cpp
  79. +241
    -6
      distrho/src/DistrhoUtils.cpp
  80. +28
    -17
      distrho/src/jackbridge/JackBridge.cpp
  81. +46
    -20
      distrho/src/jackbridge/NativeBridge.hpp
  82. +29
    -5
      distrho/src/jackbridge/RtAudioBridge.hpp
  83. +47
    -44
      distrho/src/jackbridge/WebBridge.hpp
  84. +2
    -1
      distrho/src/lv2/lv2_kxstudio_properties.h
  85. +73
    -0
      distrho/src/mapi/mapi.h
  86. +1
    -5
      examples/EmbedExternalUI/DistrhoPluginInfo.h
  87. +203
    -0
      utils/emscripten.html.in
  88. +77
    -0
      utils/install-plugins-symlinks.sh
  89. +6
    -0
      utils/symbols/mapi.def
  90. +5
    -0
      utils/symbols/mapi.exp
  91. +4
    -0
      utils/symbols/mapi.version
  92. +0
    -2
      utils/symbols/shared.def
  93. +0
    -1
      utils/symbols/shared.exp
  94. +0
    -4
      utils/symbols/shared.version
  95. +24
    -0
      utils/valgrind-dpf.supp

+ 44
- 10
.github/workflows/build.yml View File

@@ -13,10 +13,9 @@ jobs:
strategy:
matrix:
os:
# wrong use of AU MIDIPacket
# - macos-12
# - macos-13
# - macos-14
- macos-13
- macos-14
- macos-15
# webgui failure
# - ubuntu-20.04
- ubuntu-22.04
@@ -99,24 +98,59 @@ jobs:
run: |
make clean >/dev/null
make -j ${{ env.JOBS }}
- name: With OpenGL 3.x
- name: Without Cairo
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
make -j ${{ env.JOBS }} USE_OPENGL3=true
- name: Without Cairo
make -j ${{ env.JOBS }} HAVE_CAIRO=false
- name: Without OpenGL
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
make -j ${{ env.JOBS }} HAVE_CAIRO=false
- name: Without OpenGL
make -j ${{ env.JOBS }} HAVE_OPENGL=false
- name: With UI_TYPE=cairo
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
make -j ${{ env.JOBS }} HAVE_OPENGL=false
env UI_TYPE=cairo make -j ${{ env.JOBS }} -C examples/Parameters jack
- name: With UI_TYPE=generic
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
env UI_TYPE=generic make -j ${{ env.JOBS }} -C examples/Parameters jack
- name: With UI_TYPE=gles2
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
env UI_TYPE=gles2 make -j ${{ env.JOBS }} -C examples/Parameters jack
- name: With UI_TYPE=gles3
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
env UI_TYPE=gles3 make -j ${{ env.JOBS }} -C examples/Parameters jack
- name: With UI_TYPE=opengl
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
env UI_TYPE=opengl make -j ${{ env.JOBS }} -C examples/Parameters jack
- name: With UI_TYPE=opengl3
env:
CFLAGS: -Werror
CXXFLAGS: -Werror
run: |
make clean >/dev/null
env UI_TYPE=opengl3 make -j ${{ env.JOBS }} -C examples/Parameters jack

+ 42
- 62
.github/workflows/cmake.yml View File

@@ -8,9 +8,6 @@ on:
branches:
- '*'

env:
BUILD_TYPE: Release

jobs:
ubuntu-22-04:
strategy:
@@ -42,11 +39,11 @@ jobs:
suffix: _24_04
target: ${{ matrix.target }}

macos-12:
macos-13:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-12
runs-on: macos-13
steps:
- uses: actions/checkout@v4
with:
@@ -54,14 +51,14 @@ jobs:
- uses: distrho/dpf-cmake-action@v1
with:
dpf_path: .
suffix: _12
suffix: _13
target: ${{ matrix.target }}

macos-13:
macos-14:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
@@ -69,14 +66,14 @@ jobs:
- uses: distrho/dpf-cmake-action@v1
with:
dpf_path: .
suffix: _13
suffix: _14
target: ${{ matrix.target }}

macos-14:
macos-15:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-14
runs-on: macos-15
steps:
- uses: actions/checkout@v4
with:
@@ -84,81 +81,64 @@ jobs:
- uses: distrho/dpf-cmake-action@v1
with:
dpf_path: .
suffix: _14
suffix: _15
target: ${{ matrix.target }}

cmake_win32:
runs-on: windows-2019
msvc-win32:
runs-on: windows-2022
steps:
- name: Set environment
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Configure
run: |
echo "release_arch=Win32" >> "${Env:GITHUB_ENV}"
echo "vcpkg_triplet=x86-windows-static" >> "${Env:GITHUB_ENV}"
echo "vcpkg_git_commit_id=a3db16a4475b963cacf0260068c497fb72c8f3c0" >> "${Env:GITHUB_ENV}"
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v6
cmake -S . -B build -G"Visual Studio 17 2022" -A"Win32" -DCMAKE_BUILD_TYPE="Release"
- name: Build
run: cmake --build build --config "Release" -j 2
- name: Show built files
working-directory: build/bin
run: tree
- uses: actions/upload-artifact@v4
with:
setupOnly: true
vcpkgTriplet: ${{env.vcpkg_triplet}}
vcpkgGitCommitId: ${{env.vcpkg_git_commit_id}}
#- name: Install packages
# run: |
# & "${Env:VCPKG_ROOT}/vcpkg" install "cairo:${Env:vcpkg_triplet}"
name: msvc-win32
path: build/bin/
msvc-win64:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Create Build Environment
working-directory: ${{runner.workspace}}
run: cmake -E make_directory build
- name: Configure CMake
working-directory: ${{runner.workspace}}/build
run: |
cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DVCPKG_TARGET_TRIPLET="${Env:vcpkg_triplet}" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake"
cmake -S . -B build -G"Visual Studio 17 2022" -A"x64" -DCMAKE_BUILD_TYPE="Release"
- name: Build all
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2
working-directory: build
run: cmake --build . --config "Release" -j 2
- name: Show built files
working-directory: ${{runner.workspace}}/build/bin
working-directory: build/bin
run: tree
- uses: actions/upload-artifact@v4
with:
name: Win32 artifacts
path: ${{runner.workspace}}/build/bin/
name: msvc-win64
path: build/bin/

cmake_win64:
runs-on: windows-2019
msvc-arm64:
runs-on: windows-11-arm
steps:
- name: Set environment
run: |
echo "release_arch=x64" >> "${Env:GITHUB_ENV}"
echo "vcpkg_triplet=x64-windows-static" >> "${Env:GITHUB_ENV}"
echo "vcpkg_git_commit_id=a3db16a4475b963cacf0260068c497fb72c8f3c0" >> "${Env:GITHUB_ENV}"
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v6
with:
setupOnly: true
vcpkgTriplet: ${{env.vcpkg_triplet}}
vcpkgGitCommitId: ${{env.vcpkg_git_commit_id}}
#- name: Install packages
# run: |
# & "${Env:VCPKG_ROOT}/vcpkg" install "cairo:${Env:vcpkg_triplet}"
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Create Build Environment
working-directory: ${{runner.workspace}}
run: cmake -E make_directory build
- name: Configure CMake
working-directory: ${{runner.workspace}}/build
run: |
cmake "${Env:GITHUB_WORKSPACE}" -G"Visual Studio 16 2019" -A"${Env:release_arch}" -DCMAKE_BUILD_TYPE="${Env:BUILD_TYPE}" -DVCPKG_TARGET_TRIPLET="${Env:vcpkg_triplet}" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake"
cmake -S . -B build -G"Visual Studio 17 2022" -A"ARM64" -DCMAKE_BUILD_TYPE="Release"
- name: Build all
working-directory: ${{runner.workspace}}/build
run: cmake --build . --config "${Env:BUILD_TYPE}" -j 2
working-directory: build
run: cmake --build . --config "Release" -j 2
- name: Show built files
working-directory: ${{runner.workspace}}/build/bin
working-directory: build/bin
run: tree
- uses: actions/upload-artifact@v4
with:
name: Win64 artifacts
path: ${{runner.workspace}}/build/bin/
name: msvc-arm64
path: build/bin/

+ 9
- 9
.github/workflows/example-plugins.yml View File

@@ -39,11 +39,11 @@ jobs:
suffix: _24_04
target: ${{ matrix.target }}

macos-12:
macos-13:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-12
runs-on: macos-13
steps:
- uses: actions/checkout@v4
with:
@@ -51,14 +51,14 @@ jobs:
- uses: distrho/dpf-makefile-action@v1
with:
dpf_path: .
suffix: _12
suffix: _13
target: ${{ matrix.target }}

macos-13:
macos-14:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v4
with:
@@ -66,14 +66,14 @@ jobs:
- uses: distrho/dpf-makefile-action@v1
with:
dpf_path: .
suffix: _13
suffix: _14
target: ${{ matrix.target }}

macos-14:
macos-15:
strategy:
matrix:
target: [macos-intel, macos-universal, macos-10.15]
runs-on: macos-14
runs-on: macos-15
steps:
- uses: actions/checkout@v4
with:
@@ -81,5 +81,5 @@ jobs:
- uses: distrho/dpf-makefile-action@v1
with:
dpf_path: .
suffix: _14
suffix: _15
target: ${{ matrix.target }}

+ 6
- 5
.gitignore View File

@@ -15,8 +15,9 @@ CMakeFiles
CMakeSettings.json
cmake_install.cmake

bin/
build/
docs/
utils/lv2_ttl_generator
utils/lv2_ttl_generator.dSYM/
/bin/
/build/
/docs/
/khronos/
/utils/lv2_ttl_generator
/utils/lv2_ttl_generator.dSYM/

+ 7
- 7
CMakeLists.txt View File

@@ -1,10 +1,10 @@
# DISTRHO Plugin Framework (DPF)
# Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
# Copyright (C) 2022-2024 Filipe Coelho <falktx@falktx.com>
# Copyright (C) 2022-2025 Filipe Coelho <falktx@falktx.com>
#
# SPDX-License-Identifier: ISC

cmake_minimum_required(VERSION 3.7)
cmake_minimum_required(VERSION 3.8...3.31)

project(DPF)

@@ -30,19 +30,19 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(DPF-plugin)

if(DPF_LIBRARIES)
find_package(PkgConfig)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND AND (NOT HAIKU))
dpf__add_dgl_cairo(TRUE, TRUE)
dpf__add_dgl_cairo(TRUE, TRUE, TRUE)
endif()
endif()
dpf__add_dgl_external(TRUE)
dpf__add_dgl_opengl(TRUE, TRUE)
dpf__add_dgl_external(TRUE, TRUE)
dpf__add_dgl_opengl(TRUE, TRUE, TRUE)
endif()

if(DPF_EXAMPLES)
find_package(PkgConfig)
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(CAIRO "cairo")
if(CAIRO_FOUND AND (NOT HAIKU))


+ 1
- 1
LICENSE View File

@@ -1,4 +1,4 @@
Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
Copyright (C) 2012-2025 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


+ 1
- 0
Makefile View File

@@ -12,6 +12,7 @@ all: dgl examples gen

# needed for some example plugins
export USE_FILE_BROWSER = true
export USE_WEB_VIEW = true

ifneq ($(CROSS_COMPILING),true)
CAN_GENERATE_TTL = true


+ 44
- 38
Makefile.base.mk View File

@@ -20,16 +20,14 @@
# Tweak `nvgTextBreakLines` to allow space characters
# FIXME proper details

# NVG_FONT_TEXTURE_FLAGS=0
# FILE_BROWSER_DISABLED=true
# WINDOWS_ICON_ID=0
# USE_GLES2=true
# USE_GLES3=true
# USE_OPENGL3=true
# USE_NANOVG_FBO=true
# USE_NANOVG_FREETYPE=true
# NVG_FONT_TEXTURE_FLAGS=
# WINDOWS_ICON_ID=
# USE_NANOVG_FBO=false
# USE_NANOVG_FREETYPE=false
# USE_FILE_BROWSER=true
# USE_WEB_VIEW=true
# USE_GLES2=false
# USE_GLES3=false
# USE_WEB_VIEW=false

# STATIC_BUILD=true
# Tweak build to be able to generate fully static builds (e.g. skip use of libdl)
@@ -333,6 +331,10 @@ BASE_FLAGS += -DNDEBUG $(BASE_OPTS) -fvisibility=hidden
CXXFLAGS += -fvisibility-inlines-hidden
endif

ifeq ($(WASM),true)
LINK_OPTS += -sALLOW_MEMORY_GROWTH
endif

ifeq ($(WITH_LTO),true)
BASE_FLAGS += -fno-strict-aliasing -flto
LINK_OPTS += -fno-strict-aliasing -flto -Werror=odr
@@ -469,7 +471,7 @@ endif
else ifeq ($(WASM),true)

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

@@ -486,12 +488,10 @@ endif

else

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

ifeq ($(HAVE_X11),true)
DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11
@@ -544,14 +544,16 @@ else ifeq ($(MACOS),true)
OPENGL_FLAGS = -DGL_SILENCE_DEPRECATION=1 -Wno-deprecated-declarations
OPENGL_LIBS = -framework OpenGL
else ifeq ($(WASM),true)
ifeq ($(USE_GLES2),true)
OPENGL_FLAGS =
ifeq ($(USE_GLES3),true)
OPENGL_LIBS = -sMIN_WEBGL_VERSION=3 -sMAX_WEBGL_VERSION=3
else ifeq ($(USE_GLES2),true)
OPENGL_LIBS = -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
else
ifneq ($(USE_GLES3),true)
OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0
endif
OPENGL_LIBS = -sLEGACY_GL_EMULATION -sGL_UNSAFE_OPTS=0
endif
else ifeq ($(WINDOWS),true)
OPENGL_FLAGS =
OPENGL_LIBS = -lopengl32
else
OPENGL_FLAGS = $(shell $(PKG_CONFIG) --cflags gl x11)
@@ -581,7 +583,7 @@ DGL_FLAGS += -DHAVE_VULKAN
VULKAN_FLAGS = $(shell $(PKG_CONFIG) --cflags vulkan)
VULKAN_LIBS = $(shell $(PKG_CONFIG) --libs vulkan)

ifneq ($(WINDOWS),true)
ifneq ($(HAIKU_OR_MACOS_OR_WASM_OR_WINDOWS),true)
VULKAN_LIBS += -ldl
endif

@@ -666,13 +668,9 @@ endif

ifeq ($(USE_GLES2),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2
endif

ifeq ($(USE_GLES3),true)
else ifeq ($(USE_GLES3),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3
endif

ifeq ($(USE_OPENGL3),true)
else ifeq ($(USE_OPENGL3),true)
BUILD_CXX_FLAGS += -DDGL_USE_OPENGL3
endif

@@ -700,7 +698,7 @@ endif
# Set app extension

ifeq ($(WASM),true)
APP_EXT = .html
APP_EXT = .js
else ifeq ($(WINDOWS),true)
APP_EXT = .exe
endif
@@ -838,7 +836,7 @@ features:
# Extra rules for MOD Audio stuff

# NOTE: path must be absolute
MOD_WORKDIR ?= $(HOME)/mod-workdir
WORKDIR ?= $(HOME)/mod-workdir
MOD_ENVIRONMENT = \
AR=${1}/host/usr/bin/${2}-gcc-ar \
CC=${1}/host/usr/bin/${2}-gcc \
@@ -858,49 +856,57 @@ MOD_ENVIRONMENT = \
MOD_BUILD=true \
NOOPT=true

darkglass-anagram:
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/darkglass-anagram,aarch64-modaudio.generic-linux-gnu,aarch64)

modduo:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm)

modduo-new:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-new,arm-modaudio-linux-gnueabihf,arm)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduo-new,arm-modaudio-linux-gnueabihf,arm)

modduox:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64)

modduox-new:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-new,aarch64-modaudio-linux-gnueabi,aarch64)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduox-new,aarch64-modaudio-linux-gnueabi,aarch64)

moddwarf:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/moddwarf,aarch64-mod-linux-gnu,aarch64)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/moddwarf,aarch64-mod-linux-gnu,aarch64)

moddwarf-new:
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/moddwarf-new,aarch64-modaudio-linux-gnu,aarch64)
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/moddwarf-new,aarch64-modaudio-linux-gnu,aarch64)

modpush:
tar -C bin -chz $(subst bin/,,$(wildcard bin/*.lv2)) | base64 | curl -F 'package=@-' http://192.168.51.1/sdk/install && echo

ifneq (,$(findstring darkglass-anagram-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/darkglass-anagram,aarch64-modaudio.generic-linux-gnu,aarch64) $(subst darkglass-anagram-,,$(MAKECMDGOALS))
endif

ifneq (,$(findstring modduo-new-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-new,arm-modaudio-linux-gnueabihf,arm) $(subst modduo-new-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduo-new,arm-modaudio-linux-gnueabihf,arm) $(subst modduo-new-,,$(MAKECMDGOALS))
else ifneq (,$(findstring modduo-,$(filter-out modduo-new,$(MAKECMDGOALS))))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm) $(subst modduo-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduo-static,arm-mod-linux-gnueabihf.static,arm) $(subst modduo-,,$(MAKECMDGOALS))
endif

ifneq (,$(findstring modduox-new-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-new,aarch64-modaudio-linux-gnueabi,aarch64) $(subst modduox-new-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduox-new,aarch64-modaudio-linux-gnueabi,aarch64) $(subst modduox-new-,,$(MAKECMDGOALS))
else ifneq (,$(findstring modduox-,$(filter-out modduox-new,$(MAKECMDGOALS))))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64) $(subst modduox-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/modduox-static,aarch64-mod-linux-gnueabi.static,aarch64) $(subst modduox-,,$(MAKECMDGOALS))
endif

ifneq (,$(findstring moddwarf-new-,$(MAKECMDGOALS)))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/moddwarf-new,aarch64-modaudio-linux-gnu,aarch64) $(subst moddwarf-new-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/moddwarf-new,aarch64-modaudio-linux-gnu,aarch64) $(subst moddwarf-new-,,$(MAKECMDGOALS))
else ifneq (,$(findstring moddwarf-,$(filter-out moddwarf-new,$(MAKECMDGOALS))))
$(MAKECMDGOALS):
$(MAKE) $(call MOD_ENVIRONMENT,$(MOD_WORKDIR)/moddwarf,aarch64-mod-linux-gnu,aarch64) $(subst moddwarf-,,$(MAKECMDGOALS))
$(MAKE) $(call MOD_ENVIRONMENT,$(WORKDIR)/moddwarf,aarch64-mod-linux-gnu,aarch64) $(subst moddwarf-,,$(MAKECMDGOALS))
endif

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


+ 129
- 53
Makefile.plugins.mk View File

@@ -9,8 +9,8 @@
# extra useful variables to define before including this file:
# - DPF_BUILD_DIR: where to place temporary build files
# - DPF_TARGET_DIR: where to place final binary files
# - UI_TYPE: one of cairo, opengl, opengl3 or external, with opengl being default
# ("generic" is also allowed if only using image widgets)
# - UI_TYPE: one of cairo, external, gles2, gles3, opengl, opengl3 or webview, with opengl being default
# ("generic" is also allowed if only using basic DPF classes like image widgets)

# override the "all" target after including this file to define which plugin formats to build, like so:
# all: au clap jack lv2_sep vst2 vst3
@@ -55,6 +55,10 @@ ifeq ($(UI_TYPE),)
else ifeq ($(UI_TYPE),cairo)
else ifeq ($(UI_TYPE),external)
else ifeq ($(UI_TYPE),generic)
else ifeq ($(UI_TYPE),gles2)
USE_GLES2 = true
else ifeq ($(UI_TYPE),gles3)
USE_GLES3 = true
else ifeq ($(UI_TYPE),opengl)
else ifeq ($(UI_TYPE),opengl3)
USE_OPENGL3 = true
@@ -140,7 +144,7 @@ JACK_FLAGS += -sUSE_SDL=2
JACK_LIBS += -sUSE_SDL=2
JACK_LIBS += -sMAIN_MODULE -ldl

ifneq ($(FILE_BROWSER_DISABLED),true)
ifeq ($(USE_FILE_BROWSER),true)
JACK_LIBS += -sEXPORTED_RUNTIME_METHODS=FS,cwrap
endif

@@ -201,8 +205,12 @@ UI_TYPE = none
endif

ifeq ($(UI_TYPE),)
ifeq ($(WASM),true)
UI_TYPE = gles2
else
UI_TYPE = opengl
endif
endif

ifeq ($(UI_TYPE),generic)
ifeq ($(HAVE_OPENGL),true)
@@ -227,6 +235,30 @@ HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),gles2)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-gles2.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),gles3)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-gles3.a
HAVE_DGL = true
else
HAVE_DGL = false
endif
endif

ifeq ($(UI_TYPE),opengl)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL
@@ -241,7 +273,7 @@ endif

ifeq ($(UI_TYPE),opengl3)
ifeq ($(HAVE_OPENGL),true)
DGL_FLAGS += -DDGL_OPENGL -DDGL_USE_OPENGL3 -DHAVE_DGL
DGL_FLAGS += -DDGL_OPENGL -DHAVE_DGL -DDGL_USE_OPENGL3
DGL_FLAGS += $(OPENGL_FLAGS)
DGL_LIBS += $(OPENGL_LIBS)
DGL_LIB = $(DGL_BUILD_DIR)/libdgl-opengl3.a
@@ -340,6 +372,18 @@ ifeq ($(WINDOWS)$(HAVE_DGL),truetrue)
JACK_LIBS += -Wl,-subsystem,windows
endif

ifeq ($(WASM),true)
MAPI_EXT = -mapi.js
MAPI_SHARED = \
-sEXPORT_NAME="$(MAPI_MODULE_NAME)" \
-sEXPORTED_RUNTIME_METHODS=['addFunction','lengthBytesUTF8','stringToUTF8','UTF8ToString'] \
-sMAIN_MODULE=2 \
-sMODULARIZE=1
else
MAPI_EXT = $(LIB_EXT)
MAPI_SHARED = $(SHARED)
endif

ifeq ($(MACOS_APP_BUNDLE),true)
jack = $(TARGET_DIR)/$(NAME).app/Contents/MacOS/$(NAME)
jackfiles = $(TARGET_DIR)/$(NAME).app/Contents/Info.plist
@@ -347,19 +391,19 @@ else
jack = $(TARGET_DIR)/$(NAME)$(APP_EXT)
endif

ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT)
clap = $(TARGET_DIR)/$(CLAP_FILENAME)
dssi_dsp = $(TARGET_DIR)/$(NAME)-dssi$(LIB_EXT)
dssi_ui = $(TARGET_DIR)/$(NAME)-dssi/$(NAME)_ui$(APP_EXT)
ladspa_dsp = $(TARGET_DIR)/$(NAME)-ladspa$(LIB_EXT)
lv2 = $(TARGET_DIR)/$(NAME).lv2/$(NAME)$(LIB_EXT)
lv2_dsp = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_dsp$(LIB_EXT)
lv2_ui = $(TARGET_DIR)/$(NAME).lv2/$(NAME)_ui$(LIB_EXT)
mapi = $(TARGET_DIR)/$(NAME)$(MAPI_EXT)
static = $(TARGET_DIR)/$(NAME).a
vst2 = $(TARGET_DIR)/$(VST2_FILENAME)
ifneq ($(VST3_FILENAME),)
vst3 = $(TARGET_DIR)/$(VST3_FILENAME)
endif
clap = $(TARGET_DIR)/$(CLAP_FILENAME)
shared = $(TARGET_DIR)/$(NAME)$(LIB_EXT)
static = $(TARGET_DIR)/$(NAME).a

ifeq ($(MACOS),true)
BUNDLE_RESOURCES = Info.plist PkgInfo Resources/empty.lproj
@@ -370,6 +414,10 @@ vst3files += $(BUNDLE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst3/Contents/%)
clapfiles += $(BUNDLE_RESOURCES:%=$(TARGET_DIR)/$(NAME).clap/Contents/%)
endif

ifeq ($(WASM),true)
jackfiles += $(TARGET_DIR)/$(NAME).html
endif

ifneq ($(HAVE_DGL),true)
dssi_ui =
lv2_ui =
@@ -386,45 +434,45 @@ endif

ifeq ($(MACOS),true)
SYMBOLS_AU = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/au.exp
SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp
SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp
SYMBOLS_DSSI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/dssi.exp
SYMBOLS_LADSPA = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/ladspa.exp
SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp
SYMBOLS_LV2DSP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-dsp.exp
SYMBOLS_LV2UI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2-ui.exp
SYMBOLS_LV2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/lv2.exp
SYMBOLS_MAPI = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/mapi.exp
SYMBOLS_VST2 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst2.exp
SYMBOLS_VST3 = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/vst3.exp
SYMBOLS_CLAP = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/clap.exp
SYMBOLS_SHARED = -Wl,-exported_symbols_list,$(DPF_PATH)/utils/symbols/shared.exp
else ifeq ($(WASM),true)
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']"
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_DSSI = -sEXPORTED_FUNCTIONS="['ladspa_descriptor','dssi_descriptor']"
SYMBOLS_LADSPA = -sEXPORTED_FUNCTIONS="['ladspa_descriptor']"
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_LV2DSP = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl']"
SYMBOLS_LV2UI = -sEXPORTED_FUNCTIONS="['lv2ui_descriptor']"
SYMBOLS_LV2 = -sEXPORTED_FUNCTIONS="['lv2_descriptor','lv2_generate_ttl','lv2ui_descriptor']"
SYMBOLS_MAPI = -sEXPORTED_FUNCTIONS="['_mapi_create','_mapi_process','_mapi_set_parameter','_mapi_set_state','_mapi_destroy']"
SYMBOLS_VST2 = -sEXPORTED_FUNCTIONS="['VSTPluginMain']"
SYMBOLS_VST3 = -sEXPORTED_FUNCTIONS="['GetPluginFactory','ModuleEntry','ModuleExit']"
SYMBOLS_CLAP = -sEXPORTED_FUNCTIONS="['clap_entry']"
SYMBOLS_SHARED = -sEXPORTED_FUNCTIONS="['createSharedPlugin']"
else ifeq ($(WINDOWS),true)
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def
SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def
SYMBOLS_DSSI = $(DPF_PATH)/utils/symbols/dssi.def
SYMBOLS_LADSPA = $(DPF_PATH)/utils/symbols/ladspa.def
SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def
SYMBOLS_LV2DSP = $(DPF_PATH)/utils/symbols/lv2-dsp.def
SYMBOLS_LV2UI = $(DPF_PATH)/utils/symbols/lv2-ui.def
SYMBOLS_LV2 = $(DPF_PATH)/utils/symbols/lv2.def
SYMBOLS_MAPI = $(DPF_PATH)/utils/symbols/mapi.def
SYMBOLS_VST2 = $(DPF_PATH)/utils/symbols/vst2.def
SYMBOLS_VST3 = $(DPF_PATH)/utils/symbols/vst3.def
SYMBOLS_CLAP = $(DPF_PATH)/utils/symbols/clap.def
SYMBOLS_SHARED = $(DPF_PATH)/utils/symbols/shared.def
else ifneq ($(DEBUG),true)
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version
SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version
SYMBOLS_DSSI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/dssi.version
SYMBOLS_LADSPA = -Wl,--version-script=$(DPF_PATH)/utils/symbols/ladspa.version
SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version
SYMBOLS_LV2DSP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-dsp.version
SYMBOLS_LV2UI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2-ui.version
SYMBOLS_LV2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/lv2.version
SYMBOLS_MAPI = -Wl,--version-script=$(DPF_PATH)/utils/symbols/mapi.version
SYMBOLS_VST2 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst2.version
SYMBOLS_VST3 = -Wl,--version-script=$(DPF_PATH)/utils/symbols/vst3.version
SYMBOLS_CLAP = -Wl,--version-script=$(DPF_PATH)/utils/symbols/clap.version
SYMBOLS_SHARED = -Wl,--version-script=$(DPF_PATH)/utils/symbols/shared.version
endif

# ---------------------------------------------------------------------------------------------------------------------
@@ -500,6 +548,12 @@ DGL_POSSIBLE_DEPS = \
$(DGL_BUILD_DIR)/libdgl-cairo.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl cairo

$(DGL_BUILD_DIR)/libdgl-gles2.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl gles2 USE_GLES2=true

$(DGL_BUILD_DIR)/libdgl-gles3.a: $(DGL_POSSIBLE_DEPS)
$(MAKE) -C $(DPF_PATH)/dgl gles3 USE_GLES3=true

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

@@ -514,11 +568,21 @@ $(DGL_BUILD_DIR)/libdgl-vulkan.a: $(DGL_POSSIBLE_DEPS)

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

$(BUILD_DIR)/DistrhoPluginMain_%_single_obj.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_$* -DDISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT=1 -c -o $@

$(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_%_single_obj.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_$* -DDISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT=1 -c -o $@

$(BUILD_DIR)/DistrhoUIMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoUIMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_UI_DEPENDENCIES)
-@mkdir -p $(BUILD_DIR)
@echo "Compiling DistrhoUIMain.cpp ($*)"
@@ -534,10 +598,10 @@ $(BUILD_DIR)/DistrhoUI_win32.cpp.o: $(DPF_PATH)/distrho/DistrhoUI_win32.cpp $(EX
@echo "Compiling DistrhoUI_win32.cpp ($*)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -std=gnu++17 -c -o $@

$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: BUILD_CXX_FLAGS += $(JACK_FLAGS)

$(BUILD_DIR)/DistrhoPluginMain_AU.cpp.o: BUILD_CXX_FLAGS += -ObjC++

$(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.o: BUILD_CXX_FLAGS += $(JACK_FLAGS)

$(BUILD_DIR)/DistrhoUIMain_AU.cpp.o: BUILD_CXX_FLAGS += -ObjC++

$(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.o: BUILD_CXX_FLAGS += $(LIBLO_FLAGS)
@@ -591,7 +655,7 @@ lv2_dsp: $(lv2_dsp)
lv2_sep: $(lv2_dsp) $(lv2_ui)

ifeq ($(HAVE_DGL),true)
$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED)
$(lv2): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_LV2_single_obj.cpp.o $(BUILD_DIR)/DistrhoUIMain_LV2_single_obj.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED)
else
$(lv2): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.o
endif
@@ -767,6 +831,16 @@ endif
@echo "Creating AU component for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) -framework AudioToolbox -framework AudioUnit -framework CoreFoundation $(SHARED) $(SYMBOLS_AU) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# MAPI

mapi: $(mapi)

$(mapi): $(OBJS_DSP) $(BUILD_DIR)/DistrhoPluginMain_MAPI.cpp.o
-@mkdir -p $(shell dirname $@)
@echo "Creating MAPI for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(MAPI_SHARED) $(SYMBOLS_MAPI) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Export

@@ -779,20 +853,6 @@ endif
@echo "Creating export tool for $(NAME)"
$(SILENT)$(CXX) $^ $(BUILD_CXX_FLAGS) $(LINK_FLAGS) $(EXTRA_LIBS) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Shared

shared: $(shared)

ifeq ($(HAVE_DGL),true)
$(shared): $(OBJS_DSP) $(OBJS_UI) $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.o $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.o $(DGL_LIB) $(DGL_LIB_SHARED)
else
$(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) $(EXTRA_DSP_LIBS) $(EXTRA_UI_LIBS) $(DGL_LIBS) $(SHARED) $(SYMBOLS_SHARED) -o $@

# ---------------------------------------------------------------------------------------------------------------------
# Static

@@ -811,6 +871,8 @@ endif
# ---------------------------------------------------------------------------------------------------------------------
# macOS files

ifeq ($(MACOS),true)

$(TARGET_DIR)/%.app/Contents/Info.plist: $(DPF_PATH)/utils/plugin.app/Contents/Info.plist
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e "s/@INFO_PLIST_PROJECT_NAME@/$(NAME)/" $< > $@
@@ -827,8 +889,21 @@ $(TARGET_DIR)/%/Resources/empty.lproj: $(DPF_PATH)/utils/plugin.bundle/Contents/
-@mkdir -p $(shell dirname $@)
$(SILENT)cp $< $@

endif

# ---------------------------------------------------------------------------------------------------------------------
# wasm files

ifeq ($(WASM),true)

$(TARGET_DIR)/$(NAME).html: $(DPF_PATH)/utils/emscripten.html.in
-@mkdir -p $(shell dirname $@)
$(SILENT)sed -e 's|@NAME@|$(NAME)|g' $< > $@

endif

# ---------------------------------------------------------------------------------------------------------------------
# format-specific files
# auto-generated format-specific files

$(TARGET_DIR)/$(NAME).component/Contents/Info.plist: $(BUILD_DIR)/export$(APP_EXT)
-@mkdir -p $(shell dirname $@)
@@ -841,26 +916,27 @@ ifneq ($(UI_TYPE),)
-include $(OBJS_UI:%.o=%.d)
endif

-include $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_Export.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LADSPA.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_LV2_single_obj.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_MAPI.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_VST3.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_Export.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoPluginMain_STATIC.cpp.d

-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_DSSI.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_JACK.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_LV2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_LV2_single_obj.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST2.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_VST3.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_CLAP.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_AU.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_SHARED.cpp.d
-include $(BUILD_DIR)/DistrhoUIMain_STATIC.cpp.d

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

+ 1
- 1
README.md View File

@@ -31,7 +31,7 @@ Bug reports happen on the [DPF github project](https://github.com/DISTRHO/DPF/is

Online documentation is available at [https://distrho.github.io/DPF/](https://distrho.github.io/DPF/).

Online help and discussion about DPF happens in the [kx.studio chat, DPF room](https://chat.kx.studio/channel/dpf).
Online help and discussion about DPF happens in the [DPF github discussions](https://github.com/DISTRHO/DPF/discussions).


## List of plugins made with DPF:


+ 526
- 39
cmake/DPF-plugin.cmake View File

@@ -78,6 +78,8 @@ include(CMakeParseArguments)
# the user interface type, can be one of the following:
# - cairo
# - external
# - gles2
# - gles3
# - opengl (default)
# - opengl3
# - vulkan
@@ -102,6 +104,12 @@ include(CMakeParseArguments)
# `NO_SHARED_RESOURCES`
# do not build DPF shared resources (fonts, etc)
#
# `FORCE_NATIVE_AUDIO_FALLBACK`
# force the JACK/Standalone format to use native audio fallback instead of JACK
#
# `SKIP_NATIVE_AUDIO_FALLBACK`
# force the JACK/Standalone format to always use JACK, skipping native audio fallback
#
# `USE_FILE_BROWSER`
# enable file browser dialog APIs
#
@@ -109,7 +117,7 @@ include(CMakeParseArguments)
# enable web browser view APIs
#
function(dpf_add_plugin NAME)
set(options MONOLITHIC NO_SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
set(options MONOLITHIC NO_SHARED_RESOURCES FORCE_NATIVE_AUDIO_FALLBACK SKIP_NATIVE_AUDIO_FALLBACK USE_FILE_BROWSER USE_WEB_VIEW)
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})
@@ -121,23 +129,43 @@ function(dpf_add_plugin NAME)
set(_dgl_library)
if(_dpf_plugin_FILES_UI)
if(_dpf_plugin_UI_TYPE STREQUAL "cairo")
dpf__add_dgl_cairo($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>> $<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_cairo($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-cairo)
elseif(_dpf_plugin_UI_TYPE STREQUAL "external")
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-external)
elseif(_dpf_plugin_UI_TYPE STREQUAL "gles2")
dpf__add_dgl_gles2($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-gles2)
elseif(_dpf_plugin_UI_TYPE STREQUAL "gles3")
dpf__add_dgl_gles3($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-gles3)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl")
dpf__add_dgl_opengl($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>> $<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_opengl($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-opengl)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl3")
dpf__add_dgl_opengl3($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>> $<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_opengl3($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-opengl3)
elseif(_dpf_plugin_UI_TYPE STREQUAL "vulkan")
dpf__add_dgl_vulkan($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>> $<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_vulkan($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-vulkan)
elseif(_dpf_plugin_UI_TYPE STREQUAL "webview")
set(_dpf_plugin_USE_WEB_VIEW TRUE)
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>)
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-external)
else()
message(FATAL_ERROR "Unrecognized UI type for plugin: ${_dpf_plugin_UI_TYPE}")
@@ -173,6 +201,9 @@ function(dpf_add_plugin NAME)
target_compile_definitions("${NAME}" PUBLIC "DISTRHO_PLUGIN_MODGUI_CLASS_NAME=\"${_dpf_plugin_MODGUI_CLASS_NAME}\"")
endif()

find_package(Threads)
target_link_libraries("${NAME}" PUBLIC "${CMAKE_THREAD_LIBS_INIT}")

if((NOT WIN32) AND (NOT APPLE) AND (NOT HAIKU))
target_link_libraries("${NAME}" PRIVATE "dl")
endif()
@@ -209,7 +240,11 @@ function(dpf_add_plugin NAME)
###
foreach(_target ${_dpf_plugin_TARGETS})
if(_target STREQUAL "jack")
dpf__build_jack("${NAME}" "${_dgl_has_ui}")
dpf__build_jack("${NAME}"
"${_dgl_has_ui}"
"${_dpf_plugin_FORCE_NATIVE_AUDIO_FALLBACK}"
"${_dpf_plugin_SKIP_NATIVE_AUDIO_FALLBACK}"
"${_dpf_plugin_USE_FILE_BROWSER}")
elseif(_target STREQUAL "ladspa")
dpf__build_ladspa("${NAME}")
elseif(_target STREQUAL "dssi")
@@ -234,6 +269,138 @@ function(dpf_add_plugin NAME)
endforeach()
endfunction()

# dpf_add_executable(target <args...>)
# ------------------------------------------------------------------------------
#
# Add a simple executable built using the DISTRHO Plugin Framework.
#
# ------------------------------------------------------------------------------
# Arguments:
#
# `UI_TYPE` <type>
# the user interface type, can be one of the following:
# - cairo
# - external
# - gles2
# - gles3
# - opengl (default)
# - opengl3
# - vulkan
# - webview
#
# `NO_SHARED_RESOURCES`
# do not build DPF shared resources (fonts, etc)
#
# `USE_FILE_BROWSER`
# enable file browser dialog APIs
#
# `USE_WEB_VIEW`
# enable web browser view APIs
#
function(dpf_add_executable NAME)
set(options NO_SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
set(oneValueArgs UI_TYPE)
cmake_parse_arguments(_dpf_plugin "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if("${_dpf_plugin_UI_TYPE}" STREQUAL "")
set(_dpf_plugin_UI_TYPE "opengl")
endif()

set(_dgl_library)
if(_dpf_plugin_UI_TYPE STREQUAL "cairo")
dpf__add_dgl_cairo($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-cairo)
elseif(_dpf_plugin_UI_TYPE STREQUAL "external")
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-external)
elseif(_dpf_plugin_UI_TYPE STREQUAL "gles2")
dpf__add_dgl_gles2($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-gles2)
elseif(_dpf_plugin_UI_TYPE STREQUAL "gles3")
dpf__add_dgl_gles3($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-gles3)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl")
dpf__add_dgl_opengl($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-opengl)
elseif(_dpf_plugin_UI_TYPE STREQUAL "opengl3")
dpf__add_dgl_opengl3($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-opengl3)
elseif(_dpf_plugin_UI_TYPE STREQUAL "vulkan")
dpf__add_dgl_vulkan($<NOT:$<BOOL:${_dpf_plugin_NO_SHARED_RESOURCES}>>
$<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-vulkan)
elseif(_dpf_plugin_UI_TYPE STREQUAL "webview")
set(_dpf_plugin_USE_WEB_VIEW TRUE)
dpf__add_dgl_external($<BOOL:${_dpf_plugin_USE_FILE_BROWSER}>
$<BOOL:${_dpf_plugin_USE_WEB_VIEW}>)
set(_dgl_library dgl-external)
else()
message(FATAL_ERROR "Unrecognized UI type for executable: ${_dpf_plugin_UI_TYPE}")
endif()

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

dpf__create_dummy_source_list(_no_srcs)
dpf__add_executable("${NAME}" ${_no_srcs})
target_include_directories("${NAME}" PUBLIC "${DPF_ROOT_DIR}/distrho")
set_target_properties("${NAME}" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/$<0:>"
OUTPUT_NAME "${NAME}")

if(EMSCRIPTEN)
configure_file("${DPF_ROOT_DIR}/utils/emscripten.html.in"
"${PROJECT_BINARY_DIR}/bin/${NAME}.html" @ONLY)
target_link_options("${NAME}"
PRIVATE
-sEXPORTED_RUNTIME_METHODS=dynCall)
endif()

if(_dpf_plugin_USE_FILE_BROWSER)
target_compile_definitions("${NAME}" PUBLIC "DGL_USE_FILE_BROWSER")
if(EMSCRIPTEN)
target_link_options("${NAME}" PRIVATE -sEXPORTED_RUNTIME_METHODS=FS,cwrap)
endif()
endif()

if(_dpf_plugin_USE_WEB_VIEW)
target_compile_definitions("${NAME}" PUBLIC "DGL_USE_WEB_VIEW")
endif()

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

if(_dgl_library)
# make sure that all code will see DGL_* definitions
target_link_libraries("${NAME}" PUBLIC
"${_dgl_library}"
"${_dgl_library}-definitions"
dgl-system-libs-definitions
dgl-system-libs)
# extra linkage for linux web view
if(LINUX AND _dpf_plugin_USE_WEB_VIEW)
target_link_libraries("${NAME}" PRIVATE "rt")
endif()
# add the files containing C++17 or Objective-C classes
dpf__add_plugin_specific_ui_sources("${NAME}" "${_dpf_plugin_USE_WEB_VIEW}")
endif()
endfunction()

# ------------------------------------------------------------------------------
# DPF private functions (prefixed with `dpf__`)
# ------------------------------------------------------------------------------
@@ -247,7 +414,7 @@ endfunction()
#
# Add build rules for a JACK/Standalone program.
#
function(dpf__build_jack NAME HAS_UI)
function(dpf__build_jack NAME HAS_UI FORCE_NATIVE_AUDIO_FALLBACK SKIP_NATIVE_AUDIO_FALLBACK USE_FILE_BROWSER)
dpf__create_dummy_source_list(_no_srcs)

dpf__add_executable("${NAME}-jack" ${_no_srcs})
@@ -258,11 +425,30 @@ function(dpf__build_jack NAME HAS_UI)
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")
if(EMSCRIPTEN)
configure_file("${DPF_ROOT_DIR}/utils/emscripten.html.in"
"${PROJECT_BINARY_DIR}/bin/${NAME}.html" @ONLY)
target_link_options("${NAME}-jack"
PRIVATE
-sEXPORTED_RUNTIME_METHODS=dynCall
$<$<BOOL:${USE_FILE_BROWSER}>:-sEXPORTED_RUNTIME_METHODS=FS,cwrap>)
endif()

if(NOT FORCE_NATIVE_AUDIO_FALLBACK)
target_compile_definitions("${NAME}" PUBLIC "HAVE_JACK")
target_compile_definitions("${NAME}-jack" PRIVATE "HAVE_GETTIMEOFDAY")
endif()

find_package(PkgConfig)
pkg_check_modules(SDL2 "sdl2")
if(SKIP_NATIVE_AUDIO_FALLBACK)
return()
endif()

find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(SDL2 "sdl2")
else()
set(SDL2_FOUND FALSE)
endif()
if(SDL2_FOUND)
target_compile_definitions("${NAME}" PUBLIC "HAVE_SDL2")
target_include_directories("${NAME}-jack" PRIVATE ${SDL2_STATIC_INCLUDE_DIRS})
@@ -272,20 +458,20 @@ function(dpf__build_jack NAME HAS_UI)

if(APPLE OR WIN32)
target_compile_definitions("${NAME}" PUBLIC "HAVE_RTAUDIO")
elseif(EMSCRIPTEN)
else()
find_package(Threads)
pkg_check_modules(ALSA "alsa")
pkg_check_modules(PULSEAUDIO "libpulse-simple")
if(ALSA_FOUND)
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})
target_link_libraries("${NAME}-jack" PRIVATE ${ALSA_LIBRARIES})
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})
target_link_libraries("${NAME}-jack" PRIVATE ${PULSEAUDIO_LIBRARIES})
dpf__target_link_directories("${NAME}-jack" "${PULSEAUDIO_LIBRARY_DIRS}")
endif()
if(ALSA_FOUND OR PULSEAUDIO_FOUND)
@@ -335,8 +521,12 @@ endfunction()
# Add build rules for a DSSI plugin.
#
function(dpf__build_dssi NAME HAS_UI)
find_package(PkgConfig)
pkg_check_modules(LIBLO "liblo")
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(LIBLO "liblo")
else()
set(LIBLO_FOUND FALSE)
endif()
if(NOT LIBLO_FOUND)
dpf__warn_once_only(missing_liblo
"liblo is not found, skipping the `dssi` plugin targets")
@@ -392,6 +582,10 @@ function(dpf__build_lv2 NAME HAS_UI MONOLITHIC EXTRA_UI_LINK_OPTS)
OUTPUT_NAME "${NAME}_dsp"
PREFIX "")

# helper property for custom outside handling
set_target_properties("${NAME}" PROPERTIES
LV2_BUNDLE "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2")

if(HAS_UI)
if(MONOLITHIC)
dpf__add_ui_main("${NAME}-lv2" "lv2" "${HAS_UI}")
@@ -423,8 +617,7 @@ function(dpf__build_lv2 NAME HAS_UI MONOLITHIC EXTRA_UI_LINK_OPTS)
${CMAKE_CROSSCOMPILING_EMULATOR}
"$<TARGET_FILE:lv2_ttl_generator>"
"$<TARGET_FILE:${NAME}-lv2>"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2"
DEPENDS lv2_ttl_generator)
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.lv2")
endfunction()

# dpf__build_vst2
@@ -485,8 +678,15 @@ function(dpf__determine_vst3_package_architecture OUTPUT_VARIABLE)
endif()

# transform the processor name to a format that VST3 recognizes
# see https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Locations+Format/Plugin+Format.html
if(vst3_system_arch MATCHES "^(x86_64|amd64|AMD64|x64|X64)$")
set(vst3_package_arch "x86_64")
elseif(vst3_system_arch MATCHES "^(ARM)$")
set(vst3_package_arch "arm")
elseif(vst3_system_arch MATCHES "^(ARM64)$")
set(vst3_package_arch "arm64")
elseif(vst3_system_arch MATCHES "^(ARM64EC)$")
set(vst3_package_arch "arm64x")
elseif(vst3_system_arch MATCHES "^(i.86|x86|X86)$")
if(WIN32)
set(vst3_package_arch "x86")
@@ -619,8 +819,7 @@ function(dpf__build_au NAME HAS_UI)

add_custom_command(TARGET "${NAME}-au" POST_BUILD
COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} "$<TARGET_FILE:${NAME}-export>" "${NAME}"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.component/Contents"
DEPENDS "${NAME}-export")
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/bin/${NAME}.component/Contents")

add_dependencies("${NAME}-au" "${NAME}-export")

@@ -661,12 +860,12 @@ endfunction()
#
# Add the Cairo variant of DGL, if not already available.
#
function(dpf__add_dgl_cairo SHARED_RESOURCES USE_FILE_BROWSER)
function(dpf__add_dgl_cairo SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-cairo)
return()
endif()

find_package(PkgConfig)
find_package(PkgConfig REQUIRED)
pkg_check_modules(CAIRO "cairo" REQUIRED)

link_directories(${CAIRO_LIBRARY_DIRS})
@@ -710,6 +909,21 @@ function(dpf__add_dgl_cairo SHARED_RESOURCES USE_FILE_BROWSER)
target_compile_definitions(dgl-cairo PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-cairo PUBLIC "DGL_USE_FILE_BROWSER")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-cairo PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-cairo PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-cairo PRIVATE dgl-system-libs)

@@ -730,7 +944,7 @@ endfunction()
#
# Add the external variant of DGL, if not already available.
#
function(dpf__add_dgl_external USE_FILE_BROWSER)
function(dpf__add_dgl_external USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-external)
return()
endif()
@@ -770,6 +984,21 @@ function(dpf__add_dgl_external USE_FILE_BROWSER)
target_compile_definitions(dgl-external PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-external PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-external PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-external PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_compile_definitions(dgl-external PUBLIC "DGL_NO_SHARED_RESOURCES")
target_link_libraries(dgl-external PRIVATE dgl-system-libs)
@@ -781,12 +1010,210 @@ function(dpf__add_dgl_external USE_FILE_BROWSER)
target_link_libraries(dgl-external PRIVATE dgl-external-definitions "${OPENGL_gl_LIBRARY}")
endfunction()

# dpf__add_dgl_gles2
# ------------------------------------------------------------------------------
#
# Add the GLESv2 variant of DGL, if not already available.
#
function(dpf__add_dgl_gles2 SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-gles2)
return()
endif()

if(NOT OpenGL_GL_PREFERENCE)
set(OpenGL_GL_PREFERENCE "LEGACY")
endif()

find_package(OpenGL REQUIRED)

dpf__add_static_library(dgl-gles2 STATIC
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp"
"${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"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp"
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL3.cpp"
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp")
if(SHARED_RESOURCES)
target_sources(dgl-gles2 PRIVATE "${DPF_ROOT_DIR}/dgl/src/Resources.cpp")
else()
target_compile_definitions(dgl-gles2 PUBLIC "DGL_NO_SHARED_RESOURCES")
endif()
if(APPLE)
target_sources(dgl-gles2 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.mm")
else()
target_sources(dgl-gles2 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp")
endif()
target_include_directories(dgl-gles2 PUBLIC
"${DPF_ROOT_DIR}/dgl")
target_include_directories(dgl-gles2 PUBLIC
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include")

if(APPLE)
target_compile_definitions(dgl-gles2 PUBLIC "GL_SILENCE_DEPRECATION")
endif()

if(USE_FILE_BROWSER)
target_compile_definitions(dgl-gles2 PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-gles2 PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-gles2 PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-gles2 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-gles2 PRIVATE dgl-system-libs)
target_link_options(dgl-gles2
INTERFACE
$<$<BOOL:${EMSCRIPTEN}>:-sMIN_WEBGL_VERSION=2>
$<$<BOOL:${EMSCRIPTEN}>:-sMAX_WEBGL_VERSION=2>
)

add_library(dgl-gles2-definitions INTERFACE)
target_compile_definitions(dgl-gles2-definitions
INTERFACE
DGL_USE_OPENGL3
DGL_USE_GLES
DGL_USE_GLES2
DGL_OPENGL
HAVE_OPENGL
HAVE_DGL
)

target_include_directories(dgl-gles2 PUBLIC "${OPENGL_INCLUDE_DIR}")
target_link_libraries(dgl-gles2 PRIVATE dgl-gles2-definitions "${OPENGL_gl_LIBRARY}")
endfunction()

# dpf__add_dgl_gles3
# ------------------------------------------------------------------------------
#
# Add the GLESv3 variant of DGL, if not already available.
#
function(dpf__add_dgl_gles3 SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-gles3)
return()
endif()

if(NOT OpenGL_GL_PREFERENCE)
set(OpenGL_GL_PREFERENCE "LEGACY")
endif()

find_package(OpenGL REQUIRED)

dpf__add_static_library(dgl-gles3 STATIC
"${DPF_ROOT_DIR}/dgl/src/Application.cpp"
"${DPF_ROOT_DIR}/dgl/src/ApplicationPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Color.cpp"
"${DPF_ROOT_DIR}/dgl/src/EventHandlers.cpp"
"${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"
"${DPF_ROOT_DIR}/dgl/src/TopLevelWidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Widget.cpp"
"${DPF_ROOT_DIR}/dgl/src/WidgetPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL3.cpp"
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp")
if(SHARED_RESOURCES)
target_sources(dgl-gles3 PRIVATE "${DPF_ROOT_DIR}/dgl/src/Resources.cpp")
else()
target_compile_definitions(dgl-gles3 PUBLIC "DGL_NO_SHARED_RESOURCES")
endif()
if(APPLE)
target_sources(dgl-gles3 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.mm")
else()
target_sources(dgl-gles3 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/pugl.cpp")
endif()
target_include_directories(dgl-gles3 PUBLIC
"${DPF_ROOT_DIR}/dgl")
target_include_directories(dgl-gles3 PUBLIC
"${DPF_ROOT_DIR}/dgl/src/pugl-upstream/include")

if(APPLE)
target_compile_definitions(dgl-gles3 PUBLIC "GL_SILENCE_DEPRECATION")
endif()

if(USE_FILE_BROWSER)
target_compile_definitions(dgl-gles3 PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-gles3 PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-gles3 PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-gles3 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-gles3 PRIVATE dgl-system-libs)
target_link_options(dgl-gles3
INTERFACE
$<$<BOOL:${EMSCRIPTEN}>:-sMIN_WEBGL_VERSION=3>
$<$<BOOL:${EMSCRIPTEN}>:-sMAX_WEBGL_VERSION=3>
)

add_library(dgl-gles3-definitions INTERFACE)
target_compile_definitions(dgl-gles3-definitions
INTERFACE
DGL_USE_OPENGL3
DGL_USE_GLES
DGL_USE_GLES3
DGL_OPENGL
HAVE_OPENGL
HAVE_DGL
)

target_include_directories(dgl-gles3 PUBLIC "${OPENGL_INCLUDE_DIR}")
target_link_libraries(dgl-gles3 PRIVATE dgl-gles3-definitions "${OPENGL_gl_LIBRARY}")
endfunction()

# dpf__add_dgl_opengl
# ------------------------------------------------------------------------------
#
# Add the OpenGL variant of DGL, if not already available.
#
function(dpf__add_dgl_opengl SHARED_RESOURCES USE_FILE_BROWSER)
function(dpf__add_dgl_opengl SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-opengl)
return()
endif()
@@ -815,6 +1242,7 @@ function(dpf__add_dgl_opengl SHARED_RESOURCES USE_FILE_BROWSER)
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL2.cpp"
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp")
if(SHARED_RESOURCES)
target_sources(dgl-opengl PRIVATE "${DPF_ROOT_DIR}/dgl/src/Resources.cpp")
@@ -841,11 +1269,36 @@ function(dpf__add_dgl_opengl SHARED_RESOURCES USE_FILE_BROWSER)
target_compile_definitions(dgl-opengl PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-opengl PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-opengl PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-opengl PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-opengl PRIVATE dgl-system-libs)
target_link_options(dgl-opengl
INTERFACE
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION>
$<$<BOOL:${EMSCRIPTEN}>:-sGL_UNSAFE_OPTS=0>
)

add_library(dgl-opengl-definitions INTERFACE)
target_compile_definitions(dgl-opengl-definitions INTERFACE "DGL_OPENGL" "HAVE_OPENGL" "HAVE_DGL")
target_compile_definitions(dgl-opengl-definitions
INTERFACE
DGL_OPENGL
HAVE_OPENGL
HAVE_DGL
)

target_include_directories(dgl-opengl PUBLIC "${OPENGL_INCLUDE_DIR}")
target_link_libraries(dgl-opengl PRIVATE dgl-opengl-definitions "${OPENGL_gl_LIBRARY}")
@@ -856,7 +1309,7 @@ endfunction()
#
# Add the OpenGL3 variant of DGL, if not already available.
#
function(dpf__add_dgl_opengl3 SHARED_RESOURCES USE_FILE_BROWSER)
function(dpf__add_dgl_opengl3 SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-opengl3)
return()
endif()
@@ -885,6 +1338,7 @@ function(dpf__add_dgl_opengl3 SHARED_RESOURCES USE_FILE_BROWSER)
"${DPF_ROOT_DIR}/dgl/src/Window.cpp"
"${DPF_ROOT_DIR}/dgl/src/WindowPrivateData.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL.cpp"
"${DPF_ROOT_DIR}/dgl/src/OpenGL3.cpp"
"${DPF_ROOT_DIR}/dgl/src/NanoVG.cpp")
if(SHARED_RESOURCES)
target_sources(dgl-opengl3 PRIVATE "${DPF_ROOT_DIR}/dgl/src/Resources.cpp")
@@ -911,11 +1365,32 @@ function(dpf__add_dgl_opengl3 SHARED_RESOURCES USE_FILE_BROWSER)
target_compile_definitions(dgl-opengl3 PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-opengl3 PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-opengl3 PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-opengl3 PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-opengl3 PRIVATE dgl-system-libs)

add_library(dgl-opengl3-definitions INTERFACE)
target_compile_definitions(dgl-opengl3-definitions INTERFACE "DGL_USE_OPENGL3" "DGL_OPENGL" "HAVE_OPENGL" "HAVE_DGL")
target_compile_definitions(dgl-opengl3-definitions
INTERFACE
DGL_USE_OPENGL3
DGL_OPENGL
HAVE_OPENGL
HAVE_DGL
)

target_include_directories(dgl-opengl3 PUBLIC "${OPENGL_INCLUDE_DIR}")
target_link_libraries(dgl-opengl3 PRIVATE dgl-opengl3-definitions "${OPENGL_gl_LIBRARY}")
@@ -926,7 +1401,7 @@ endfunction()
#
# Add the Vulkan variant of DGL, if not already available.
#
function(dpf__add_dgl_vulkan SHARED_RESOURCES USE_FILE_BROWSER)
function(dpf__add_dgl_vulkan SHARED_RESOURCES USE_FILE_BROWSER USE_WEB_VIEW)
if(TARGET dgl-vulkan)
return()
endif()
@@ -976,6 +1451,21 @@ function(dpf__add_dgl_vulkan SHARED_RESOURCES USE_FILE_BROWSER)
target_compile_definitions(dgl-vulkan PUBLIC "DGL_USE_FILE_BROWSER")
endif()

if(USE_WEB_VIEW)
target_compile_definitions(dgl-vulkan PUBLIC "DGL_USE_WEB_VIEW")
if(APPLE)
find_library(APPLE_WEBKIT_FRAMEWORK "WebKit")
target_link_libraries(dgl-vulkan PRIVATE "${APPLE_WEBKIT_FRAMEWORK}")
elseif(WIN32)
target_sources(dgl-vulkan PRIVATE
"${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp")
set_source_files_properties("${DPF_ROOT_DIR}/dgl/src/WebViewWin32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
endif()
endif()

dpf__add_dgl_system_libs()
target_link_libraries(dgl-vulkan PRIVATE dgl-system-libs)

@@ -1002,13 +1492,10 @@ function(dpf__add_plugin_specific_ui_sources NAME USE_WEB_VIEW)
elseif(WIN32 AND USE_WEB_VIEW)
target_sources("${NAME}" PRIVATE
"${DPF_ROOT_DIR}/distrho/DistrhoUI_win32.cpp")
if (MSVC)
set_source_files_properties("${DPF_ROOT_DIR}/distrho/DistrhoUI_win32.cpp"
PROPERTIES COMPILE_FLAGS /std:c++17)
else()
set_source_files_properties("${DPF_ROOT_DIR}/distrho/DistrhoUI_win32.cpp"
PROPERTIES COMPILE_FLAGS -std=gnu++17)
endif()
set_source_files_properties("${DPF_ROOT_DIR}/distrho/DistrhoUI_win32.cpp"
PROPERTIES
COMPILE_FLAGS
$<IF:$<BOOL:${MSVC}>,/std:c++17,-std=gnu++17>)
target_link_libraries("${NAME}" PRIVATE "ole32" "uuid")
endif()
endfunction()
@@ -1034,7 +1521,7 @@ function(dpf__add_dgl_system_libs)
elseif(WIN32)
target_link_libraries(dgl-system-libs INTERFACE "comdlg32" "dwmapi" "gdi32")
else()
find_package(PkgConfig)
find_package(PkgConfig REQUIRED)
pkg_check_modules(DBUS "dbus-1")
if(DBUS_FOUND)
target_compile_definitions(dgl-system-libs-definitions INTERFACE "HAVE_DBUS")
@@ -1161,7 +1648,7 @@ function(dpf__set_target_defaults NAME)
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options("${NAME}" PUBLIC "-fno-gnu-unique")
endif()
if ((NOT APPLE) AND (NOT MSVC))
if ((NOT APPLE) AND (NOT EMSCRIPTEN) AND (NOT MSVC))
target_link_options("${NAME}" PUBLIC "-Wl,--no-undefined")
endif()
endfunction()


+ 32
- 4
dgl/Application.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -54,6 +54,12 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_file_browser_on)
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_file_browser_off)
#endif

#ifdef DGL_USE_WEB_VIEW
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_web_view_on)
#else
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_web_view_off)
#endif

#ifdef DGL_NO_SHARED_RESOURCES
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_on)
#else
@@ -78,10 +84,26 @@ class DISTRHO_API Application
{
public:
/**
Constructor.
Type of application to setup, either "classic" or "modern".

What this means depends on the OS.
*/
enum Type {
kTypeAuto,
kTypeClassic,
kTypeModern,
};

/**
Constructor for standalone or plugin application.
*/
// NOTE: the default value is not yet passed, so we catch where we use this
Application(bool isStandalone = true);
Application(bool isStandalone = true, Type type = kTypeAuto);

/**
Constructor for a standalone application.
This specific constructor is required if using web views in standalone applications.
*/
Application(int argc, char* argv[]);

/**
Destructor.
@@ -130,6 +152,12 @@ public:
*/
double getTime() const;

/**
Return the application type, either kTypeClassic or kTypeModern.
This function never return kTypeAuto.
*/
Type getType() const noexcept;

/**
Add a callback function to be triggered on every idle cycle.
You can add more than one, and remove them at anytime with removeIdleCallback().


+ 7
- 3
dgl/Base.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -41,14 +41,18 @@
# error typo detected use DGL_USE_FILE_BROWSER instead of DGL_USE_FILEBROWSER
#endif

#ifdef DGL_UI_USE_WEBVIEW
# error typo detected use DGL_UI_USE_WEB_VIEW instead of DGL_UI_USE_WEBVIEW
#ifdef DGL_USE_WEBVIEW
# error typo detected use DGL_USE_WEB_VIEW instead of DGL_USE_WEBVIEW
#endif

#if defined(DGL_FILE_BROWSER_DISABLED)
# error DGL_FILE_BROWSER_DISABLED has been replaced by DGL_USE_FILE_BROWSER (opt-in vs opt-out)
#endif

#ifndef DGL_ALLOW_DEPRECATED_METHODS
# define DGL_ALLOW_DEPRECATED_METHODS 1
#endif

// --------------------------------------------------------------------------------------------------------------------
// Define namespace



+ 3
- 4
dgl/Cairo.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -165,10 +165,9 @@ private:
Widget display function.
Implemented internally to pass context into the drawing function.
*/
void onDisplay() override
void onDisplay() final
{
const CairoGraphicsContext& context((const CairoGraphicsContext&)BaseWidget::getGraphicsContext());
onCairoDisplay(context);
onCairoDisplay(static_cast<const CairoGraphicsContext&>(BaseWidget::getGraphicsContext()));
}

DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CairoBaseWidget);


+ 17
- 1
dgl/Color.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -99,6 +99,11 @@ struct Color {
*/
Color invert() const noexcept;

/**
Create a new color based on this one but in grayscale (using weighted average).
*/
Color asGrayscale() const noexcept;

/**
Create a color specified by hue, saturation and lightness.
Values must in [0..1] range.
@@ -110,6 +115,17 @@ struct Color {
*/
static Color fromHTML(const char* rgb, float alpha = 1.0f) noexcept;

/**
Create a color from a RGB unsigned integer.
Basically doing:
```
uint8_t red = (color >> 24) & 0xff;
uint8_t green = (color >> 16) & 0xff;
uint8_t blue = (color >> 8) & 0xff;
```
*/
static Color fromRGB(uint color, float alpha = 1.0f) noexcept;

/**
Linearly interpolate this color against another.
*/


+ 10
- 1
dgl/EventHandlers.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -63,6 +63,9 @@ public:
bool isCheckable() const noexcept;
void setCheckable(bool checkable) noexcept;

bool isEnabled() const noexcept;
void setEnabled(bool enabled, bool appliesToEventInput = true) noexcept;

Point<double> getLastClickPosition() const noexcept;
Point<double> getLastMotionPosition() const noexcept;

@@ -121,6 +124,9 @@ public:
KnobEventHandler& operator=(const KnobEventHandler& other);
virtual ~KnobEventHandler();

bool isEnabled() const noexcept;
void setEnabled(bool enabled, bool appliesToEventInput = true) noexcept;

// if setStep(1) has been called before, this returns true
bool isInteger() const noexcept;

@@ -138,6 +144,9 @@ public:
// NOTE: value is assumed to be scaled if using log
void setDefault(float def) noexcept;

float getMinimum() const noexcept;
float getMaximum() const noexcept;

// NOTE: value is assumed to be scaled if using log
void setRange(float min, float max) noexcept;



+ 9
- 9
dgl/Geometry.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -360,25 +360,23 @@ public:
*/
bool isNotNull() const noexcept;

#ifndef DPF_TEST_POINT_CPP
/**
Draw this line using the provided graphics context, optionally specifying line width.
*/
void draw(const GraphicsContext& context, T width = 1);
#endif

Line<T>& operator=(const Line<T>& line) noexcept;
bool operator==(const Line<T>& line) const noexcept;
bool operator!=(const Line<T>& line) const noexcept;

#ifndef DPF_TEST_POINT_CPP
#ifdef DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this line using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
*/
DISTRHO_DEPRECATED_BY("draw(const GraphicsContext&)")
void draw();
#endif
#endif

private:
Point<T> posStart, posEnd;
@@ -489,7 +487,7 @@ public:
bool operator==(const Circle<T>& cir) const noexcept;
bool operator!=(const Circle<T>& cir) const noexcept;

#ifndef DPF_TEST_POINT_CPP
#ifdef DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this circle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -503,7 +501,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif
#endif

private:
Point<T> fPos;
@@ -582,7 +580,7 @@ public:
bool operator==(const Triangle<T>& tri) const noexcept;
bool operator!=(const Triangle<T>& tri) const noexcept;

#ifndef DPF_TEST_POINT_CPP
#ifdef DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this triangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -596,7 +594,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif
#endif

private:
Point<T> pos1, pos2, pos3;
@@ -813,6 +811,7 @@ public:
bool operator==(const Rectangle<T>& size) const noexcept;
bool operator!=(const Rectangle<T>& size) const noexcept;

#ifdef DGL_ALLOW_DEPRECATED_METHODS
/**
Draw this rectangle using the current OpenGL state.@n
DEPRECATED Please use draw(const GraphicsContext&) instead.
@@ -826,6 +825,7 @@ public:
*/
DISTRHO_DEPRECATED_BY("drawOutline(const GraphicsContext&)")
void drawOutline();
#endif

private:
Point<T> pos;


+ 10
- 6
dgl/Image.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,17 +17,21 @@
#ifndef DGL_IMAGE_HPP_INCLUDED
#define DGL_IMAGE_HPP_INCLUDED

#ifdef DGL_CAIRO
#include "Cairo.hpp"
#if defined(DGL_CAIRO)
# include "Cairo.hpp"
#elif defined(DGL_OPENGL)
# include "OpenGL.hpp"
#elif defined(DGL_VULKAN)
# include "Vulkan.hpp"
#else
#include "OpenGL.hpp"
# include "Base.hpp"
#endif

START_NAMESPACE_DGL

#ifdef DGL_CAIRO
#if defined(DGL_CAIRO)
typedef CairoImage Image;
#else
#elif defined(DGL_OPENGL)
typedef OpenGLImage Image;
#endif



+ 85
- 0
dgl/Makefile View File

@@ -54,6 +54,10 @@ OBJS_common = \
$(BUILD_DIR)/dgl/Window.cpp.o \
$(BUILD_DIR)/dgl/WindowPrivateData.cpp.o

ifeq ($(WINDOWS)$(USE_WEB_VIEW),truetrue)
OBJS_common += $(BUILD_DIR)/dgl/WebViewWin32.cpp.o
endif

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

OBJS_cairo = $(OBJS_common) \
@@ -67,8 +71,35 @@ endif

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

OBJS_gles2 = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.gles2.o \
$(BUILD_DIR)/dgl/OpenGL3.cpp.gles2.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.gles2.o

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

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

OBJS_gles3 = $(OBJS_common) \
$(BUILD_DIR)/dgl/OpenGL.cpp.gles3.o \
$(BUILD_DIR)/dgl/OpenGL3.cpp.gles3.o \
$(BUILD_DIR)/dgl/NanoVG.cpp.gles3.o

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

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

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

ifeq ($(MACOS),true)
@@ -81,6 +112,7 @@ endif

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

ifeq ($(MACOS),true)
@@ -118,7 +150,10 @@ TARGETS += $(BUILD_DIR)/libdgl-cairo.a
endif

ifeq ($(HAVE_OPENGL),true)
TARGETS += $(BUILD_DIR)/libdgl-gles2.a
TARGETS += $(BUILD_DIR)/libdgl-gles3.a
TARGETS += $(BUILD_DIR)/libdgl-opengl.a
TARGETS += $(BUILD_DIR)/libdgl-opengl3.a
endif

ifeq ($(HAVE_STUB),true)
@@ -134,8 +169,15 @@ endif
all: $(TARGETS)

cairo: $(BUILD_DIR)/libdgl-cairo.a
gles2: $(BUILD_DIR)/libdgl-gles2.a
gles3: $(BUILD_DIR)/libdgl-gles3.a
ifeq ($(WASM),true)
opengl: gles2
opengl3: gles3
else
opengl: $(BUILD_DIR)/libdgl-opengl.a
opengl3: $(BUILD_DIR)/libdgl-opengl3.a
endif
stub: $(BUILD_DIR)/libdgl-stub.a
vulkan: $(BUILD_DIR)/libdgl-vulkan.a
web: $(BUILD_DIR)/libdgl-web.a
@@ -148,6 +190,18 @@ $(BUILD_DIR)/libdgl-cairo.a: $(OBJS_cairo)
$(SILENT)rm -f $@
$(SILENT)$(AR) crs $@ $^

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

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

$(BUILD_DIR)/libdgl-opengl.a: $(OBJS_opengl)
-@mkdir -p $(BUILD_DIR)
@echo "Creating libdgl-opengl.a"
@@ -201,6 +255,11 @@ $(BUILD_DIR)/dgl/pugl.mm.o: src/pugl.mm
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) -c -ObjC++ -o $@

$(BUILD_DIR)/dgl/WebViewWin32.cpp.o: src/WebViewWin32.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $<"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -std=gnu++17 -c -o $@

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

$(BUILD_DIR)/dgl/%.cpp.cairo.o: src/%.cpp
@@ -215,6 +274,30 @@ $(BUILD_DIR)/dgl/%.mm.cairo.o: src/%.mm

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

$(BUILD_DIR)/dgl/%.cpp.gles2.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv2 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2 -UDGL_USE_GLES3 -c -o $@

$(BUILD_DIR)/dgl/%.mm.gles2.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv2 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES2 -UDGL_USE_GLES3 -c -ObjC++ -o $@

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

$(BUILD_DIR)/dgl/%.cpp.gles3.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3 -UDGL_USE_GLES2 -c -o $@

$(BUILD_DIR)/dgl/%.mm.gles3.o: src/%.mm
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (GLESv3 variant)"
$(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) $(PUGL_EXTRA_FLAGS) $(OPENGL_FLAGS) -DDGL_OPENGL -DDGL_USE_OPENGL3 -DDGL_USE_GLES -DDGL_USE_GLES3 -UDGL_USE_GLES2 -c -ObjC++ -o $@

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

$(BUILD_DIR)/dgl/%.cpp.opengl.o: src/%.cpp
-@mkdir -p $(BUILD_DIR)/dgl
@echo "Compiling $< (OpenGL variant)"
@@ -261,6 +344,8 @@ debug:

-include $(OBJS_common:%.o=%.d)
-include $(OBJS_cairo:%.o=%.d)
-include $(OBJS_gles2:%.o=%.d)
-include $(OBJS_gles3:%.o=%.d)
-include $(OBJS_opengl:%.o=%.d)
-include $(OBJS_opengl3:%.o=%.d)
-include $(OBJS_stub:%.o=%.d)


+ 5
- 0
dgl/NanoVG.hpp View File

@@ -112,6 +112,11 @@ public:
*/
GLuint getTextureHandle() const;

/**
Update the image data in-place.
*/
void update(const uchar* data);

private:
Handle fHandle;
Size<uint> fSize;


+ 6
- 0
dgl/OpenGL-include.hpp View File

@@ -53,6 +53,12 @@

#ifdef DISTRHO_OS_MAC
# ifdef DGL_USE_OPENGL3
// NOTE GLES with macOS is not supported
# ifdef DGL_USE_GLES
# undef DGL_USE_GLES
# undef DGL_USE_GLES2
# undef DGL_USE_GLES3
# endif
# include <OpenGL/gl3.h>
# include <OpenGL/gl3ext.h>
# else


+ 140
- 17
dgl/OpenGL.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -24,34 +24,131 @@

START_NAMESPACE_DGL

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

#ifdef DGL_USE_OPENGL3
/**
OpenGL Graphics context.
OpenGL3 Graphics context.

This provides access to the program, shaders and uniforms used by the underlying DPF implementation.
*/
struct OpenGLGraphicsContext : GraphicsContext
struct OpenGL3GraphicsContext : GraphicsContext
{
#ifdef DGL_USE_OPENGL3
#endif
/**
The OpenGL3 program used for this context.
It is activated automatically before any widget onDisplay() is called.
If changing the current OpenGL program make sure to revert back to this one at the end of your pipeline.

@code
// use custom program
glUseProgram(context.program);

// custom stuff here

// revert back
glUseProgram(context.program);
@endcode
*/
GLuint program;

/**
A vec4 uniform used to set the next drawing color.

@code
const GLfloat color[4] = { red, green, blue, alpha };
glUniform4fv(context.color, 1, color);
@endcode
*/
GLuint color;

/**
A vertex shader attribute directly linked to gl_Position.
Use this to set the bounds for drawing, normalized as -1.0 to +1.0.
The @a width and @a height provide the total window size for convenience.

@code
const GLfloat triangle[] = { x1, y1, x2, y2, x3, y3 };
glEnableVertexAttribArray(context.bounds);
glVertexAttribPointer(context.bounds, 2, GL_FLOAT, GL_FALSE, 0, triangle);
@endcode
*/
GLuint bounds;

/**
A vertex shader attribute directly linked to GL_TEXTURE0 map.
// TODO find the correct wording, map??.

@code
const GLfloat map[] = { 0.f, 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 0.f };
glEnableVertexAttribArray(context.textureMap);
glVertexAttribPointer(context.textureMap, 2, GL_FLOAT, GL_FALSE, 0, map);
@endcode
*/
GLuint textureMap;

/**
A boolean uniform used to indicate if next drawing should @a texture or @a color.
Set to 0 for color mode, 1 for texture.
Default mode is color, if changed make sure to revert to color mode at the end of your pipeline.

@code
// setup for drawing based on texture
glUniform1i(context.usingTexture, 1);

// bind texture
glBindTexture(GL_TEXTURE_2D, myTextureId);
// etc..

// glDrawElements or similar

// unbind texture
glBindTexture(GL_TEXTURE_2D, 0);
// etc..

// revert to color mode
glUniform1i(context.usingTexture, 0);
@endcode
*/
GLuint usingTexture;

/**
Set of buffers created with glGenBuffers.
Used internally in DPF to draw generic shapes, can be reused in custom code.
Unbound by default, make sure to leave them unbound at the end of your pipeline.
*/
GLuint buffers[2];

/**
Total width of the window used for this context.
*/
uint width;

/**
Total height of the window used for this context.
*/
uint height;
};
#endif

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

static inline
ImageFormat asDISTRHOImageFormat(const GLenum format)
{
switch (format)
{
#ifdef DGL_USE_OPENGL3
#if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2)
case GL_RED:
#else
#else
case GL_LUMINANCE:
#endif
#endif
return kImageFormatGrayscale;
#ifndef DGL_USE_GLES
case GL_BGR:
return kImageFormatBGR;
case GL_BGRA:
return kImageFormatBGRA;
#endif
case GL_RGB:
return kImageFormatRGB;
case GL_RGBA:
@@ -69,25 +166,33 @@ GLenum asOpenGLImageFormat(const ImageFormat format)
case kImageFormatNull:
break;
case kImageFormatGrayscale:
#ifdef DGL_USE_OPENGL3
#if defined(DGL_USE_OPENGL3) && !defined(DGL_USE_GLES2)
return GL_RED;
#else
#else
return GL_LUMINANCE;
#endif
#endif
case kImageFormatBGR:
#ifndef DGL_USE_GLES
return GL_BGR;
#else
return 0;
#endif
case kImageFormatBGRA:
#ifndef DGL_USE_GLES
return GL_BGRA;
#else
return 0;
#endif
case kImageFormatRGB:
return GL_RGB;
case kImageFormatRGBA:
return GL_RGBA;
}

return 0x0;
return 0;
}

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

/**
OpenGL Image class.
@@ -144,6 +249,18 @@ public:
*/
void drawAt(const GraphicsContext& context, const Point<int>& pos) override;

#ifdef DGL_USE_GLES
/**
Get the image format.
*/
ImageFormat getFormat() const noexcept;

/**
Get the raw image data.
*/
const char* getRawData() const noexcept;
#endif

/**
TODO document this.
*/
@@ -157,6 +274,7 @@ public:
inline void drawAt(const GraphicsContext& context, int x, int y)
{ drawAt(context, Point<int>(x, y)); }

#ifdef DGL_ALLOW_DEPRECATED_METHODS
/**
Constructor using raw image data, specifying an OpenGL image format.
@note @a rawData must remain valid for the lifetime of this Image.
@@ -200,14 +318,19 @@ public:
*/
DISTRHO_DEPRECATED
GLenum getType() const noexcept { return GL_UNSIGNED_BYTE; }
#endif // DGL_ALLOW_DEPRECATED_METHODS

private:
bool setupCalled;
bool textureInit;
GLuint textureId;
#ifdef DGL_USE_GLES
mutable char* convertedData;
mutable const char* rawDataLast;
#endif
};

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

typedef ImageBaseAboutWindow<OpenGLImage> OpenGLImageAboutWindow;
typedef ImageBaseButton<OpenGLImage> OpenGLImageButton;
@@ -215,7 +338,7 @@ typedef ImageBaseKnob<OpenGLImage> OpenGLImageKnob;
typedef ImageBaseSlider<OpenGLImage> OpenGLImageSlider;
typedef ImageBaseSwitch<OpenGLImage> OpenGLImageSwitch;

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

END_NAMESPACE_DGL



+ 28
- 0
dgl/WebView.hpp View File

@@ -0,0 +1,28 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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 DGL_WEB_VIEW_HPP_INCLUDED
#define DGL_WEB_VIEW_HPP_INCLUDED

#include "Base.hpp"

START_NAMESPACE_DGL

#include "../distrho/extra/WebViewImpl.hpp"

END_NAMESPACE_DGL

#endif // DGL_WEB_VIEW_HPP_INCLUDED

+ 26
- 1
dgl/Window.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -23,6 +23,10 @@
# include "FileBrowserDialog.hpp"
#endif

#ifdef DGL_USE_WEB_VIEW
# include "WebView.hpp"
#endif

#include <vector>

#ifdef DISTRHO_NAMESPACE
@@ -407,6 +411,27 @@ public:
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options = FileBrowserOptions());
#endif

#ifdef DGL_USE_WEB_VIEW
/**
Create a new web view.

The web view will be added on top of this window.
This means it will draw on top of whatever is below it,
something to take into consideration if mixing regular widgets with web views.

Provided metrics in @p options must have scale factor pre-applied.

@p url: The URL to open, assumed to be in encoded form (e.g spaces converted to %20)
@p options: Extra options, optional
*/
bool createWebView(const char* url, const DGL_NAMESPACE::WebViewOptions& options = WebViewOptions());

/**
Evaluate/run JavaScript on the web view.
*/
void evaluateJS(const char* js);
#endif

/**
Request repaint of this window, for the entire area.
*/


+ 65
- 3
dgl/src/Application.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -24,6 +24,11 @@

START_NAMESPACE_DGL

/* define webview start */
#if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW)
int dpf_webview_start(int argc, char* argv[]);
#endif

// --------------------------------------------------------------------------------------------------------------------
// build config sentinels

@@ -42,6 +47,12 @@ BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_file_browser_on)
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_file_browser_off)
#endif

#ifdef DGL_USE_WEB_VIEW
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_web_view_on)
#else
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_use_web_view_off)
#endif

#ifdef DGL_NO_SHARED_RESOURCES
BUILD_CONFIG_SENTINEL(fail_to_link_is_mismatch_dgl_no_shared_resources_on)
#else
@@ -64,6 +75,11 @@ bool dpf_check_build_status() noexcept
#else
fail_to_link_is_mismatch_dgl_use_file_browser_off.ok &&
#endif
#ifdef DGL_USE_WEB_VIEW
fail_to_link_is_mismatch_dgl_use_web_view_on.ok &&
#else
fail_to_link_is_mismatch_dgl_use_web_view_off.ok &&
#endif
#ifdef DGL_NO_SHARED_RESOURCES
fail_to_link_is_mismatch_dgl_no_shared_resources_on.ok &&
#else
@@ -82,8 +98,8 @@ static void app_idle(void* const app)
}
#endif

Application::Application(const bool isStandalone)
: pData(new PrivateData(isStandalone))
Application::Application(const bool isStandalone, const Type type)
: pData(new PrivateData(isStandalone, type))
{
// build config sentinels
#ifdef DPF_DEBUG
@@ -96,6 +112,47 @@ Application::Application(const bool isStandalone)
#else
fail_to_link_is_mismatch_dgl_use_file_browser_off.ok = true;
#endif
#ifdef DGL_USE_WEB_VIEW
fail_to_link_is_mismatch_dgl_use_web_view_on.ok = true;
#else
fail_to_link_is_mismatch_dgl_use_web_view_off.ok = true;
#endif
#ifdef DGL_NO_SHARED_RESOURCES
fail_to_link_is_mismatch_dgl_no_shared_resources_on.ok = true;
#else
fail_to_link_is_mismatch_dgl_no_shared_resources_off.ok = true;
#endif
DISTRHO_SAFE_ASSERT(dpf_check_build_status());
}

Application::Application(int argc, char* argv[])
: pData(new PrivateData(true, kTypeAuto))
{
#if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW)
if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0)
std::exit(dpf_webview_start(argc, argv));
#else
// unused
(void)argc;
(void)argv;
#endif

// build config sentinels
#ifdef DPF_DEBUG
fail_to_link_is_mismatch_dpf_debug_on.ok = true;
#else
fail_to_link_is_mismatch_dpf_debug_off.ok = true;
#endif
#ifdef DGL_USE_FILE_BROWSER
fail_to_link_is_mismatch_dgl_use_file_browser_on.ok = true;
#else
fail_to_link_is_mismatch_dgl_use_file_browser_off.ok = true;
#endif
#ifdef DGL_USE_WEB_VIEW
fail_to_link_is_mismatch_dgl_use_web_view_on.ok = true;
#else
fail_to_link_is_mismatch_dgl_use_web_view_off.ok = true;
#endif
#ifdef DGL_NO_SHARED_RESOURCES
fail_to_link_is_mismatch_dgl_no_shared_resources_on.ok = true;
#else
@@ -156,6 +213,11 @@ double Application::getTime() const
return pData->getTime();
}

Application::Type Application::getType() const noexcept
{
return pData->isModern ? kTypeModern : kTypeClassic;
}

void Application::addIdleCallback(IdleCallback* const callback)
{
DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)


+ 5
- 13
dgl/src/ApplicationPrivateData.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -53,13 +53,14 @@ const char* Application::getClassName() const noexcept

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

Application::PrivateData::PrivateData(const bool standalone)
Application::PrivateData::PrivateData(const bool standalone, const Type type)
: world(puglNewWorld(standalone ? PUGL_PROGRAM : PUGL_MODULE,
standalone ? PUGL_WORLD_THREADS : 0x0)),
(standalone ? PUGL_WORLD_THREADS : 0))),
isModern(false),
isStandalone(standalone),
isStarting(true),
isQuitting(false),
isQuittingInNextCycle(false),
isStarting(true),
needsRepaint(false),
visibleWindows(0),
mainThreadHandle(getCurrentThreadHandle()),
@@ -68,16 +69,11 @@ Application::PrivateData::PrivateData(const bool standalone)
{
DISTRHO_SAFE_ASSERT_RETURN(world != nullptr,);

#ifdef DGL_USING_SDL
SDL_Init(SDL_INIT_EVENTS|SDL_INIT_TIMER|SDL_INIT_VIDEO);
#else
puglSetWorldHandle(world, this);
#ifdef __EMSCRIPTEN__
puglSetWorldString(world, PUGL_CLASS_NAME, "canvas");
#else
puglSetWorldString(world, PUGL_CLASS_NAME, DISTRHO_MACRO_AS_STRING(DGL_NAMESPACE));
#endif
#endif
}

Application::PrivateData::~PrivateData()
@@ -88,12 +84,8 @@ Application::PrivateData::~PrivateData()
windows.clear();
idleCallbacks.clear();

#ifdef DGL_USING_SDL
SDL_Quit();
#else
if (world != nullptr)
puglFreeWorld(world);
#endif
}

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


+ 8
- 5
dgl/src/ApplicationPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -51,18 +51,21 @@ struct Application::PrivateData {
/** Pugl world instance. */
PuglWorld* const world;

/** Whether the applicating uses modern backend, otherwise classic. */
const bool isModern;

/** Whether the application is running as standalone, otherwise it is part of a plugin. */
const bool isStandalone;

/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */
bool isStarting;

/** Whether the applicating is about to quit, or already stopped. Defaults to false. */
bool isQuitting;

/** Helper for safely close everything from main thread. */
bool isQuittingInNextCycle;

/** Whether the applicating is starting up, that is, no windows have been made visible yet. Defaults to true. */
bool isStarting;

/** When true force all windows to be repainted on next idle. */
bool needsRepaint;

@@ -80,7 +83,7 @@ struct Application::PrivateData {
std::list<DGL_NAMESPACE::IdleCallback*> idleCallbacks;

/** Constructor and destructor */
explicit PrivateData(bool standalone);
explicit PrivateData(bool standalone, Type type);
~PrivateData();

/** Flag one window as shown, which increments @a visibleWindows.


+ 64
- 24
dgl/src/Cairo.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose with
@@ -32,21 +32,43 @@
// templated classes
#include "ImageBaseWidgets.cpp"

// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifndef DGL_CAIRO
# error Build config error, Cairo was NOT requested while building Cairo code
#endif
#ifdef DGL_OPENGL
# error Build config error, OpenGL requested while building Cairo code
#endif
#ifdef DGL_VULKAN
# error Build config error, Vulkan requested while building Cairo code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building Cairo code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building Cairo code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building Cairo code
#endif

START_NAMESPACE_DGL

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

static void notImplemented(const char* const name)
{
d_stderr2("cairo function not implemented: %s", name);
d_stderr2("Cairo function not implemented: %s", name);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

if (includeAlpha)
cairo_set_source_rgba(handle, red, green, blue, alpha);
@@ -54,7 +76,7 @@ void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
cairo_set_source_rgb(handle, red, green, blue);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Line

template<typename T>
@@ -63,7 +85,7 @@ void Line<T>::draw(const GraphicsContext& context, const T width)
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_line_width(handle, width);
cairo_move_to(handle, posStart.getX(), posStart.getY());
@@ -71,11 +93,13 @@ void Line<T>::draw(const GraphicsContext& context, const T width)
cairo_stroke(handle);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}
#endif

template class Line<double>;
template class Line<float>;
@@ -129,7 +153,7 @@ static void drawCircle(cairo_t* const handle,
template<typename T>
void Circle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
}
@@ -139,12 +163,13 @@ void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_line_width(handle, lineWidth);
drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Circle<T>::draw()
{
@@ -156,6 +181,7 @@ void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}
#endif

template class Circle<double>;
template class Circle<float>;
@@ -190,7 +216,7 @@ static void drawTriangle(cairo_t* const handle,
template<typename T>
void Triangle<T>::draw(const GraphicsContext& context)
{
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

drawTriangle<T>(handle, pos1, pos2, pos3, false);
}
@@ -200,12 +226,13 @@ void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_line_width(handle, lineWidth);
drawTriangle<T>(handle, pos1, pos2, pos3, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Triangle<T>::draw()
{
@@ -217,6 +244,7 @@ void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}
#endif

template class Triangle<double>;
template class Triangle<float>;
@@ -244,7 +272,7 @@ void Rectangle<T>::draw(const GraphicsContext& context)
{
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

drawRectangle(handle, *this, false);
}
@@ -255,12 +283,13 @@ void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth
DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_line_width(handle, lineWidth);
drawRectangle(handle, *this, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
@@ -272,6 +301,7 @@ void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}
#endif

template class Rectangle<double>;
template class Rectangle<float>;
@@ -516,7 +546,7 @@ void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noex
if (datarefcount != nullptr && --(*datarefcount) == 0)
std::free(surfacedata);
else
datarefcount = (int*)malloc(sizeof(*datarefcount));
datarefcount = static_cast<int*>(malloc(sizeof(*datarefcount)));

surface = newsurface;
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
@@ -531,7 +561,7 @@ void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
{
DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,);

cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;

cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
cairo_paint(handle);
@@ -623,7 +653,7 @@ void ImageBaseKnob<CairoImage>::PrivateData::init()
template <>
void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
{
cairo_surface_destroy((cairo_surface_t*)cairoSurface);
cairo_surface_destroy(static_cast<cairo_surface_t*>(cairoSurface));
cairoSurface = nullptr;
}

@@ -672,10 +702,10 @@ template <>
void ImageBaseKnob<CairoImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(context).handle;
const double normValue = getNormalizedValue();

cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;
cairo_surface_t* surface = static_cast<cairo_surface_t*>(pData->cairoSurface);

if (! pData->isReady)
{
@@ -826,15 +856,25 @@ void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, u
notImplemented("Window::PrivateData::renderToPicture");
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
void Window::PrivateData::createContextIfNeeded()
{
GraphicsContext& context((GraphicsContext&)graphicsContext);
((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
return context;
}

// -----------------------------------------------------------------------
void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
reinterpret_cast<CairoGraphicsContext&>(graphicsContext).handle = static_cast<cairo_t*>(puglGetContext(view));
}

void Window::PrivateData::endContext()
{
}

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

END_NAMESPACE_DGL

+ 17
- 1
dgl/src/Color.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -172,6 +172,14 @@ Color Color::invert() const noexcept
return color;
}

Color Color::asGrayscale() const noexcept
{
Color color(*this);
// values taken from https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/
color.red = color.green = color.blue = 0.299f * color.red + 0.587f * color.green + 0.114f * color.blue;
return color;
}

Color Color::fromHSL(float hue, float saturation, float lightness, float alpha)
{
float m1, m2;
@@ -234,6 +242,14 @@ Color Color::fromHTML(const char* rgb, const float alpha) noexcept
return Color(r, g, b, alpha);
}

Color Color::fromRGB(const uint color, const float alpha) noexcept
{
return Color(static_cast<int>(color >> 24) & 0xff,
static_cast<int>(color >> 16) & 0xff,
static_cast<int>(color >> 8) & 0xff,
alpha);
}

void Color::interpolate(const Color& other, float u) noexcept
{
fixRange(u);


+ 101
- 8
dgl/src/EventHandlers.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -31,6 +31,8 @@ struct ButtonEventHandler::PrivateData {
int state;
bool checkable;
bool checked;
bool enabled;
bool enabledInput;

Point<double> lastClickPos;
Point<double> lastMotionPos;
@@ -44,11 +46,16 @@ struct ButtonEventHandler::PrivateData {
state(kButtonStateDefault),
checkable(false),
checked(false),
enabled(true),
enabledInput(true),
lastClickPos(0, 0),
lastMotionPos(0, 0) {}

bool mouseEvent(const Widget::MouseEvent& ev)
{
if (! enabledInput)
return false;

lastClickPos = ev.pos;

// button was released, handle it now
@@ -98,12 +105,8 @@ struct ButtonEventHandler::PrivateData {

bool motionEvent(const Widget::MotionEvent& ev)
{
// keep pressed
if (button != -1)
{
lastMotionPos = ev.pos;
return true;
}
if (! enabledInput)
return false;

bool ret = false;

@@ -133,7 +136,7 @@ struct ButtonEventHandler::PrivateData {
}

lastMotionPos = ev.pos;
return ret;
return ret || button != -1;
}

void setActive(const bool active2, const bool sendCallback) noexcept
@@ -171,6 +174,27 @@ struct ButtonEventHandler::PrivateData {
}
}

void setEnabled(const bool enabled2, const bool appliesToEventInput) noexcept
{
if (appliesToEventInput)
enabledInput = enabled2;

if (enabled == enabled2)
return;

// reset temp vars if disabling
if (! enabled2)
{
button = -1;
state = kButtonStateDefault;
lastClickPos = Point<double>();
lastMotionPos = Point<double>();
}

enabled = enabled2;
widget->repaint();
}

DISTRHO_DECLARE_NON_COPYABLE(PrivateData)
};

@@ -217,6 +241,16 @@ void ButtonEventHandler::setCheckable(const bool checkable) noexcept
pData->checkable = checkable;
}

bool ButtonEventHandler::isEnabled() const noexcept
{
return pData->enabled;
}

void ButtonEventHandler::setEnabled(const bool enabled, const bool appliesToEventInput) noexcept
{
pData->setEnabled(enabled, appliesToEventInput);
}

Point<double> ButtonEventHandler::getLastClickPosition() const noexcept
{
return pData->lastClickPos;
@@ -281,6 +315,8 @@ struct KnobEventHandler::PrivateData {
float value;
float valueDef;
float valueTmp;
bool enabled;
bool enabledInput;
bool usingDefault;
bool usingLog;
Orientation orientation;
@@ -301,6 +337,8 @@ struct KnobEventHandler::PrivateData {
value(0.5f),
valueDef(value),
valueTmp(value),
enabled(true),
enabledInput(true),
usingDefault(false),
usingLog(false),
orientation(Vertical),
@@ -320,6 +358,8 @@ struct KnobEventHandler::PrivateData {
value(other->value),
valueDef(other->valueDef),
valueTmp(value),
enabled(other->enabled),
enabledInput(other->enabledInput),
usingDefault(other->usingDefault),
usingLog(other->usingLog),
orientation(other->orientation),
@@ -338,6 +378,8 @@ struct KnobEventHandler::PrivateData {
value = other->value;
valueDef = other->valueDef;
valueTmp = value;
enabled = other->enabled;
enabledInput = other->enabledInput;
usingDefault = other->usingDefault;
usingLog = other->usingLog;
orientation = other->orientation;
@@ -363,6 +405,9 @@ struct KnobEventHandler::PrivateData {

bool mouseEvent(const Widget::MouseEvent& ev, const double scaleFactor)
{
if (! enabledInput)
return false;

if (ev.button != 1)
return false;

@@ -416,6 +461,9 @@ struct KnobEventHandler::PrivateData {

bool motionEvent(const Widget::MotionEvent& ev, const double scaleFactor)
{
if (! enabledInput)
return false;

if ((state & kKnobStateDragging) == 0x0)
return false;

@@ -501,6 +549,9 @@ struct KnobEventHandler::PrivateData {

bool scrollEvent(const Widget::ScrollEvent& ev)
{
if (! enabledInput)
return false;

if (! widget->contains(ev.pos))
return false;

@@ -541,6 +592,28 @@ struct KnobEventHandler::PrivateData {
return ((usingLog ? invlogscale(value) : value) - minimum) / diff;
}

void setEnabled(const bool enabled2, const bool appliesToEventInput) noexcept
{
if (appliesToEventInput)
enabledInput = enabled2;

if (enabled == enabled2)
return;

// reset temp vars if disabling
if (! enabled2)
{
state = kKnobStateDefault;
lastX = 0.0;
lastY = 0.0;
lastClickTime = 0;
valueTmp = value;
}

enabled = enabled2;
widget->repaint();
}

void setRange(const float min, const float max) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(max > min,);
@@ -598,6 +671,16 @@ KnobEventHandler::~KnobEventHandler()
delete pData;
}

bool KnobEventHandler::isEnabled() const noexcept
{
return pData->enabled;
}

void KnobEventHandler::setEnabled(const bool enabled, const bool appliesToEventInput) noexcept
{
pData->setEnabled(enabled, appliesToEventInput);
}

bool KnobEventHandler::isInteger() const noexcept
{
return d_isEqual(pData->step, 1.f);
@@ -629,6 +712,16 @@ void KnobEventHandler::setDefault(const float def) noexcept
pData->usingDefault = true;
}

float KnobEventHandler::getMinimum() const noexcept
{
return pData->minimum;
}

float KnobEventHandler::getMaximum() const noexcept
{
return pData->maximum;
}

void KnobEventHandler::setRange(const float min, const float max) noexcept
{
pData->setRange(min, max);


+ 10
- 6
dgl/src/Layout.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -26,9 +26,11 @@ typedef std::list<VerticalLayout*>::iterator VerticalLayoutIterator;
// --------------------------------------------------------------------------------------------------------------------

template<> // horizontal
uint Layout<true>::setAbsolutePos(int x, const int y, const uint padding)
uint Layout<true>::setAbsolutePos(int x, int y, const uint padding)
{
uint maxHeight = 0;
y += padding;
x += padding;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
@@ -43,9 +45,11 @@ uint Layout<true>::setAbsolutePos(int x, const int y, const uint padding)
}

template<> // vertical
uint Layout<false>::setAbsolutePos(const int x, int y, const uint padding)
uint Layout<false>::setAbsolutePos(int x, int y, const uint padding)
{
uint maxWidth = 0;
y += padding;
x += padding;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
{
@@ -63,10 +67,10 @@ template<> // horizontal
void Layout<true>::setSize(const uint width, const uint padding)
{
uint maxHeight = 0;
uint nonFixedWidth = width;
uint nonFixedWidth = width - padding * 2;
uint numDynamiclySizedWidgets = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)
for (SubWidgetWithSizeHintIterator it = widgets.begin(), end = widgets.end(); it != end; ++it)
{
SubWidgetWithSizeHint& s(*it);
maxHeight = std::max(maxHeight, s.widget->getHeight());
@@ -96,7 +100,7 @@ template<> // vertical
void Layout<false>::setSize(const uint height, const uint padding)
{
uint biggestWidth = 0;
uint nonFixedHeight = height;
uint nonFixedHeight = height - padding * 2;
uint numDynamiclySizedWidgets = 0;

for (SubWidgetWithSizeHintIterator it=widgets.begin(), end=widgets.end(); it != end; ++it)


+ 10
- 0
dgl/src/NanoVG.cpp View File

@@ -89,6 +89,8 @@ DGL_EXT(PFNGLUNIFORMBLOCKBINDINGPROC, glUniformBlockBinding)
//#define STB_IMAGE_STATIC
#if defined(DGL_USE_GLES2)
# define NANOVG_GLES2_IMPLEMENTATION
#elif defined(DGL_USE_GLES3)
# define NANOVG_GLES3_IMPLEMENTATION
#elif defined(DGL_USE_OPENGL3)
# define NANOVG_GL3_IMPLEMENTATION
#else
@@ -279,6 +281,14 @@ GLuint NanoImage::getTextureHandle() const
return nvglImageHandle(fHandle.context, fHandle.imageId);
}

void NanoImage::update(const uchar* const data)
{
DISTRHO_SAFE_ASSERT_RETURN(fHandle.context != nullptr && fHandle.imageId != 0,);
DISTRHO_SAFE_ASSERT_RETURN(data != nullptr,);

nvgUpdateImage(fHandle.context, fHandle.imageId, data);
}

void NanoImage::_updateSize()
{
int w=0, h=0;


+ 35
- 562
dgl/src/OpenGL.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -20,7 +20,6 @@
#endif

#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

#include "SubWidgetPrivateData.hpp"
@@ -28,416 +27,20 @@
#include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

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

#ifdef DGL_USE_OPENGL3
static void notImplemented(const char* const name)
{
d_stderr2("OpenGL3 function not implemented: %s", name);
}
#else
# define DGL_USE_COMPAT_OPENGL
#endif

// -----------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
#ifdef DGL_USE_COMPAT_OPENGL
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
#else
notImplemented("Color::setFor");
// unused
(void)includeAlpha;
#endif
}

// -----------------------------------------------------------------------
// Line

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}
#endif

template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
#ifdef DGL_USE_COMPAT_OPENGL
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

// deprecated calls
template<typename T>
void Line<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawLine<T>(posStart, posEnd);
#else
notImplemented("Line::draw");
#endif
}

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// -----------------------------------------------------------------------
// Circle

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

const T origx = pos.getX();
const T origy = pos.getY();
double t, x = size, y = 0.0;

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i=0; i<numSegments; ++i)
{
glVertex2d(x + origx, y + origy);

t = x;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

glEnd();
}
#endif

template<typename T>
void Circle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Circle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
#else
notImplemented("Circle::draw");
#endif
}

template<typename T>
void Circle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
#else
notImplemented("Circle::drawOutline");
#endif
}

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// -----------------------------------------------------------------------
// Triangle

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawTriangle(const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}
#endif

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Triangle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, false);
#else
notImplemented("Triangle::draw");
#endif
}

template<typename T>
void Triangle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawTriangle<T>(pos1, pos2, pos3, true);
#else
notImplemented("Triangle::drawOutline");
#endif
}

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// -----------------------------------------------------------------------
// Rectangle

#ifdef DGL_USE_COMPAT_OPENGL
template<typename T>
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
}
#endif

template<typename T>
void Rectangle<T>::draw(const GraphicsContext&)
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

// deprecated calls
template<typename T>
void Rectangle<T>::draw()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, false);
#else
notImplemented("Rectangle::draw");
#endif
}

template<typename T>
void Rectangle<T>::drawOutline()
{
#ifdef DGL_USE_COMPAT_OPENGL
drawRectangle<T>(*this, true);
#else
notImplemented("Rectangle::drawOutline");
#endif
}

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()), GL_UNSIGNED_BYTE, image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

#ifdef DGL_USE_COMPAT_OPENGL
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

#ifdef DGL_USE_COMPAT_OPENGL
glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
#endif

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

OpenGLImage::OpenGLImage()
: ImageBase(),
setupCalled(false),
textureInit(false),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
}

@@ -446,6 +49,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, co
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -456,6 +63,10 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const Ima
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -466,6 +77,10 @@ OpenGLImage::OpenGLImage(const OpenGLImage& image)
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -475,6 +90,10 @@ OpenGLImage::~OpenGLImage()
{
if (textureId != 0)
glDeleteTextures(1, &textureId);

#ifdef DGL_USE_GLES
std::free(convertedData);
#endif
}

void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
@@ -489,11 +108,6 @@ void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, c
ImageBase::loadFromMemory(rdata, s, fmt);
}

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
{
rawData = image.rawData;
@@ -511,12 +125,16 @@ OpenGLImage& OpenGLImage::operator=(const OpenGLImage& image) noexcept
return *this;
}

// deprecated calls
#ifdef DGL_ALLOW_DEPRECATED_METHODS
OpenGLImage::OpenGLImage(const char* const rdata, const uint w, const uint h, const GLenum fmt)
: ImageBase(rdata, w, h, asDISTRHOImageFormat(fmt)),
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
@@ -527,155 +145,17 @@ OpenGLImage::OpenGLImage(const char* const rdata, const Size<uint>& s, const GLe
setupCalled(false),
textureInit(true),
textureId(0)
#ifdef DGL_USE_GLES
, convertedData(nullptr)
, rawDataLast(nullptr)
#endif
{
glGenTextures(1, &textureId);
DISTRHO_SAFE_ASSERT(textureId != 0);
}

void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

// -----------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
#ifdef DGL_USE_COMPAT_OPENGL
glPushMatrix();
#endif

const int w2 = w/2;
const int h2 = h/2;

#ifdef DGL_USE_COMPAT_OPENGL
glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);
#endif

Rectangle<int>(-w2, -h2, w, h).draw(context);

#ifdef DGL_USE_COMPAT_OPENGL
glPopMatrix();
#endif
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<OpenGLImage>;

// -----------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<OpenGLImage>;

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

void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
{
@@ -736,7 +216,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}

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

void TopLevelWidget::PrivateData::display()
{
@@ -757,7 +237,7 @@ void TopLevelWidget::PrivateData::display()
selfw->pData->displaySubWidgets(width, height, window.pData->autoScaleFactor);
}

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

void Window::PrivateData::renderToPicture(const char* const filename,
const GraphicsContext&,
@@ -787,13 +267,6 @@ void Window::PrivateData::renderToPicture(const char* const filename,
fclose(f);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return (const GraphicsContext&)graphicsContext;
}

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

END_NAMESPACE_DGL

+ 570
- 0
dgl/src/OpenGL2.cpp View File

@@ -0,0 +1,570 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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.
*/

#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../OpenGL.hpp"
#include "../Color.hpp"
#include "../ImageWidgets.hpp"

// #include "SubWidgetPrivateData.hpp"
// #include "TopLevelWidgetPrivateData.hpp"
// #include "WidgetPrivateData.hpp"
#include "WindowPrivateData.hpp"

// templated classes
#include "ImageBaseWidgets.cpp"

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifndef DGL_OPENGL
# error Build config error, OpenGL was NOT requested while building OpenGL2 code
#endif
#ifdef DGL_CAIRO
# error Build config error, Cairo requested while building OpenGL2 code
#endif
#ifdef DGL_VULKAN
# error Build config error, Vulkan requested while building OpenGL2 code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building OpenGL2 code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building OpenGL2 code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building OpenGL2 code
#endif

// --------------------------------------------------------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, const bool includeAlpha)
{
if (includeAlpha)
glColor4f(red, green, blue, alpha);
else
glColor3f(red, green, blue);
}

// --------------------------------------------------------------------------------------------------------------------
// Line

template<typename T>
static void drawLine(const Point<T>& posStart, const Point<T>& posEnd)
{
DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);

glBegin(GL_LINES);

{
glVertex2d(posStart.getX(), posStart.getY());
glVertex2d(posEnd.getX(), posEnd.getY());
}

glEnd();
}

template<typename T>
void Line<T>::draw(const GraphicsContext&, const T width)
{
DISTRHO_SAFE_ASSERT_RETURN(width != 0,);

glLineWidth(static_cast<GLfloat>(width));
drawLine<T>(posStart, posEnd);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Line<T>::draw()
{
drawLine<T>(posStart, posEnd);
}
#endif

template class Line<double>;
template class Line<float>;
template class Line<int>;
template class Line<uint>;
template class Line<short>;
template class Line<ushort>;

// --------------------------------------------------------------------------------------------------------------------
// Circle

template<typename T>
static void drawCircle(const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);

const double origx = static_cast<double>(pos.getX());
const double origy = static_cast<double>(pos.getY());
double t;
double x = size;
double y = 0.0;

glBegin(outline ? GL_LINE_LOOP : GL_POLYGON);

for (uint i = 0; i < numSegments; ++i)
{
glVertex2d(x + origx, y + origy);

t = x;
x = cos * x - sin * y;
y = sin * t + cos * y;
}

glEnd();
}

template<typename T>
static void drawCircle(const GraphicsContext&,
const Point<T>& pos,
const uint numSegments,
const float size,
const float sin,
const float cos,
const bool outline)
{
drawCircle<T>(pos, numSegments, size, sin, cos, outline);
}

template<typename T>
void Circle<T>::draw(const GraphicsContext& context)
{
drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawCircle<T>(context, fPos, fNumSegments, fSize, fSin, fCos, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Circle<T>::draw()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, false);
}

template<typename T>
void Circle<T>::drawOutline()
{
drawCircle<T>(fPos, fNumSegments, fSize, fSin, fCos, true);
}
#endif

template class Circle<double>;
template class Circle<float>;
template class Circle<int>;
template class Circle<uint>;
template class Circle<short>;
template class Circle<ushort>;

// --------------------------------------------------------------------------------------------------------------------
// Triangle

template<typename T>
static void drawTriangle(const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);

glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLES);

{
glVertex2d(pos1.getX(), pos1.getY());
glVertex2d(pos2.getX(), pos2.getY());
glVertex2d(pos3.getX(), pos3.getY());
}

glEnd();
}

template<typename T>
static void drawTriangle(const GraphicsContext&,
const Point<T>& pos1,
const Point<T>& pos2,
const Point<T>& pos3,
const bool outline)
{
drawTriangle<T>(pos1, pos2, pos3, outline);
}

template<typename T>
void Triangle<T>::draw(const GraphicsContext&)
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline(const GraphicsContext&, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawTriangle<T>(pos1, pos2, pos3, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Triangle<T>::draw()
{
drawTriangle<T>(pos1, pos2, pos3, false);
}

template<typename T>
void Triangle<T>::drawOutline()
{
drawTriangle<T>(pos1, pos2, pos3, true);
}
#endif

template class Triangle<double>;
template class Triangle<float>;
template class Triangle<int>;
template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;

// --------------------------------------------------------------------------------------------------------------------
// Rectangle

template<typename T>
static void drawRectangle(const Rectangle<T>& rect, const bool outline)
{
DISTRHO_SAFE_ASSERT_RETURN(rect.isValid(),);

glBegin(outline ? GL_LINE_LOOP : GL_QUADS);

{
const T x = rect.getX();
const T y = rect.getY();
const T w = rect.getWidth();
const T h = rect.getHeight();

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();
}

template<typename T>
void Rectangle<T>::draw(const GraphicsContext& context)
{
drawRectangle<T>(*this, false);
}

template<typename T>
void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
{
DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);

glLineWidth(static_cast<GLfloat>(lineWidth));
drawRectangle<T>(*this, true);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
drawRectangle<T>(*this, false);
}

template<typename T>
void Rectangle<T>::drawOutline()
{
drawRectangle<T>(*this, true);
}
#endif

template class Rectangle<double>;
template class Rectangle<float>;
template class Rectangle<int>;
template class Rectangle<uint>;
template class Rectangle<short>;
template class Rectangle<ushort>;

// --------------------------------------------------------------------------------------------------------------------
// OpenGLImage

static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId)
{
DISTRHO_SAFE_ASSERT_RETURN(image.isValid(),);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
static_cast<GLsizei>(image.getWidth()),
static_cast<GLsizei>(image.getHeight()),
0,
asOpenGLImageFormat(image.getFormat()),
GL_UNSIGNED_BYTE,
image.getRawData());

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

static void drawOpenGLImage(const OpenGLImage& image, const Point<int>& pos, const GLuint textureId, bool& setupCalled)
{
if (textureId == 0 || image.isInvalid())
return;

if (! setupCalled)
{
setupOpenGLImage(image, textureId);
setupCalled = true;
}

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);

glBegin(GL_QUADS);

{
const int x = pos.getX();
const int y = pos.getY();
const int w = static_cast<int>(image.getWidth());
const int h = static_cast<int>(image.getHeight());

glTexCoord2f(0.0f, 0.0f);
glVertex2d(x, y);

glTexCoord2f(1.0f, 0.0f);
glVertex2d(x+w, y);

glTexCoord2f(1.0f, 1.0f);
glVertex2d(x+w, y+h);

glTexCoord2f(0.0f, 1.0f);
glVertex2d(x, y+h);
}

glEnd();

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
void OpenGLImage::draw()
{
drawOpenGLImage(*this, Point<int>(0, 0), textureId, setupCalled);
}

void OpenGLImage::drawAt(const int x, const int y)
{
drawOpenGLImage(*this, Point<int>(x, y), textureId, setupCalled);
}

void OpenGLImage::drawAt(const Point<int>& pos)
{
drawOpenGLImage(*this, pos, textureId, setupCalled);
}
#endif

// --------------------------------------------------------------------------------------------------------------------
// ImageBaseAboutWindow

#if 0
template <>
void ImageBaseAboutWindow<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
img.draw(context);
}
#endif

template class ImageBaseAboutWindow<OpenGLImage>;

// --------------------------------------------------------------------------------------------------------------------
// ImageBaseButton

template class ImageBaseButton<OpenGLImage>;

// --------------------------------------------------------------------------------------------------------------------
// ImageBaseKnob

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::init()
{
glTextureId = 0;
glGenTextures(1, &glTextureId);
}

template <>
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup()
{
if (glTextureId == 0)
return;

glDeleteTextures(1, &glTextureId);
glTextureId = 0;
}

template <>
void ImageBaseKnob<OpenGLImage>::onDisplay()
{
const GraphicsContext& context(getGraphicsContext());
const float normValue = getNormalizedValue();

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, pData->glTextureId);

if (! pData->isReady)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

static const float trans[] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, trans);

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

uint imageDataOffset = 0;

if (pData->rotationAngle == 0)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->imgLayerCount > 0,);
DISTRHO_SAFE_ASSERT_RETURN(normValue >= 0.0f,);

const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight);
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth);

// TODO kImageFormatGreyscale
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA ||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3);
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1));
}

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
static_cast<GLsizei>(getWidth()), static_cast<GLsizei>(getHeight()), 0,
asOpenGLImageFormat(pData->image.getFormat()), GL_UNSIGNED_BYTE, pData->image.getRawData() + imageDataOffset);

pData->isReady = true;
}

const int w = static_cast<int>(getWidth());
const int h = static_cast<int>(getHeight());

if (pData->rotationAngle != 0)
{
glPushMatrix();

const int w2 = w/2;
const int h2 = h/2;

glTranslatef(static_cast<float>(w2), static_cast<float>(h2), 0.0f);
glRotatef(normValue*static_cast<float>(pData->rotationAngle), 0.0f, 0.0f, 1.0f);

Rectangle<int>(-w2, -h2, w, h).draw(context);

glPopMatrix();
}
else
{
Rectangle<int>(0, 0, w, h).draw(context);
}

glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}

template class ImageBaseKnob<OpenGLImage>;

// --------------------------------------------------------------------------------------------------------------------
// ImageBaseSlider

template class ImageBaseSlider<OpenGLImage>;

// --------------------------------------------------------------------------------------------------------------------
// ImageBaseSwitch

template class ImageBaseSwitch<OpenGLImage>;

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

void Window::PrivateData::createContextIfNeeded()
{
}

void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
}

void Window::PrivateData::endContext()
{
}

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

END_NAMESPACE_DGL

+ 1025
- 0
dgl/src/OpenGL3.cpp
File diff suppressed because it is too large
View File


+ 53
- 6
dgl/src/Stub.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,7 +14,14 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include "Color.hpp"
#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../Color.hpp"
#include "../ImageBaseWidgets.hpp"

#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
#include "WidgetPrivateData.hpp"
@@ -22,11 +29,33 @@

START_NAMESPACE_DGL

// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifdef DGL_CAIRO
# error Build config error, Cairo requested while building Stub code
#endif
#ifdef DGL_OPENGL
# error Build config error, OpenGL requested while building Stub code
#endif
#ifdef DGL_VULKAN
# error Build config error, Vulkan requested while building Stub code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building Stub code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building Stub code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building Stub code
#endif

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

static void notImplemented(const char* const name)
{
d_stderr2("stub function not implemented: %s", name);
d_stderr2("Stub function not implemented: %s", name);
}

// --------------------------------------------------------------------------------------------------------------------
@@ -46,11 +75,13 @@ void Line<T>::draw(const GraphicsContext& context, T)
notImplemented("Line::draw");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}
#endif

template class Line<double>;
template class Line<float>;
@@ -74,6 +105,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Circle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Circle<T>::draw()
{
@@ -85,6 +117,7 @@ void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}
#endif

template class Circle<double>;
template class Circle<float>;
@@ -108,6 +141,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Triangle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Triangle<T>::draw()
{
@@ -119,6 +153,7 @@ void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}
#endif

template class Triangle<double>;
template class Triangle<float>;
@@ -142,6 +177,7 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Rectangle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
@@ -153,6 +189,7 @@ void Rectangle<T>::drawOutline()
{
notImplemented("Rectangle::drawOutline");
}
#endif

template class Rectangle<double>;
template class Rectangle<float>;
@@ -182,10 +219,20 @@ void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, u

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
void Window::PrivateData::createContextIfNeeded()
{
}

void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
}

void Window::PrivateData::endContext()
{
GraphicsContext& context((GraphicsContext&)graphicsContext);
return context;
}

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


+ 53
- 7
dgl/src/Vulkan.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,8 +14,14 @@
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#ifdef _MSC_VER
// instantiated template classes whose methods are defined elsewhere
# pragma warning(disable:4661)
#endif

#include "../Vulkan.hpp"
#include "../Color.hpp"
#include "../ImageBaseWidgets.hpp"

#include "SubWidgetPrivateData.hpp"
#include "TopLevelWidgetPrivateData.hpp"
@@ -24,14 +30,36 @@

START_NAMESPACE_DGL

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Check for correct build config

#ifndef DGL_VULKAN
# error Build config error, Vulkan was NOT requested while building Vulkan code
#endif
#ifdef DGL_CAIRO
# error Build config error, Cairo requested while building Vulkan code
#endif
#ifdef DGL_OPENGL
# error Build config error, OpenGL requested while building Vulkan code
#endif
#ifdef DGL_USE_GLES2
# error Build config error, GLESv2 requested while building Vulkan code
#endif
#ifdef DGL_USE_GLES3
# error Build config error, GLESv3 requested while building Vulkan code
#endif
#ifdef DGL_USE_OPENGL3
# error Build config error, OpenGL3 requested while building Vulkan code
#endif

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

static void notImplemented(const char* const name)
{
d_stderr2("vulkan function not implemented: %s", name);
d_stderr2("Vulkan function not implemented: %s", name);
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Color

void Color::setFor(const GraphicsContext&, bool)
@@ -48,11 +76,13 @@ void Line<T>::draw(const GraphicsContext&, T)
notImplemented("Line::draw");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Line<T>::draw()
{
notImplemented("Line::draw");
}
#endif

template class Line<double>;
template class Line<float>;
@@ -76,6 +106,7 @@ void Circle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Circle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Circle<T>::draw()
{
@@ -87,6 +118,7 @@ void Circle<T>::drawOutline()
{
notImplemented("Circle::drawOutline");
}
#endif

template class Circle<double>;
template class Circle<float>;
@@ -110,6 +142,7 @@ void Triangle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Triangle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Triangle<T>::draw()
{
@@ -121,6 +154,7 @@ void Triangle<T>::drawOutline()
{
notImplemented("Triangle::drawOutline");
}
#endif

template class Triangle<double>;
template class Triangle<float>;
@@ -129,7 +163,6 @@ template class Triangle<uint>;
template class Triangle<short>;
template class Triangle<ushort>;


// -----------------------------------------------------------------------
// Rectangle

@@ -145,11 +178,13 @@ void Rectangle<T>::drawOutline(const GraphicsContext&, T)
notImplemented("Rectangle::drawOutline");
}

#ifdef DGL_ALLOW_DEPRECATED_METHODS
template<typename T>
void Rectangle<T>::draw()
{
notImplemented("Rectangle::draw");
}
#endif

template<typename T>
void Rectangle<T>::drawOutline()
@@ -238,9 +273,20 @@ void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, u

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
void Window::PrivateData::createContextIfNeeded()
{
}

void Window::PrivateData::destroyContext()
{
}

void Window::PrivateData::startContext()
{
}

void Window::PrivateData::endContext()
{
return (const GraphicsContext&)graphicsContext;
}

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


+ 23
- 0
dgl/src/WebViewWin32.cpp View File

@@ -0,0 +1,23 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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.
*/

// Include CHOC separately because it requires C++17

#define DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION
#define WEB_VIEW_NAMESPACE DGL_NAMESPACE
#define WEB_VIEW_DGL_NAMESPACE
#include "../WebView.hpp"
#include "../../distrho/extra/WebViewWin32.hpp"

+ 51
- 32
dgl/src/Window.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -28,7 +28,11 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win)
: window(win),
ppData(nullptr),
active(window.pData->view != nullptr && puglBackendEnter(window.pData->view)),
reenter(false) {}
reenter(false)
{
if (active)
window.pData->createContextIfNeeded();
}

Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transientWin)
: window(win),
@@ -40,6 +44,8 @@ Window::ScopedGraphicsContext::ScopedGraphicsContext(Window& win, Window& transi
{
puglBackendLeave(ppData->view);
active = puglBackendEnter(window.pData->view);
if (active)
window.pData->createContextIfNeeded();
}
}

@@ -180,22 +186,22 @@ int Window::getOffsetX() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

return puglGetFrame(pData->view).x;
return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).x;
}

int Window::getOffsetY() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

return puglGetFrame(pData->view).y;
return puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION).y;
}

Point<int> Window::getOffset() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Point<int>());

const PuglRect rect = puglGetFrame(pData->view);
return Point<int>(rect.x, rect.y);
const PuglPoint pos = puglGetPositionHint(pData->view, PUGL_CURRENT_POSITION);
return Point<int>(pos.x, pos.y);
}

void Window::setOffsetX(const int x)
@@ -214,7 +220,7 @@ void Window::setOffset(const int x, const int y)
DISTRHO_SAFE_ASSERT_RETURN(!pData->isEmbed,);

if (pData->view != nullptr)
puglSetPosition(pData->view, x, y);
puglSetPositionHint(pData->view, PUGL_CURRENT_POSITION, x, y);
}

void Window::setOffset(const Point<int>& offset)
@@ -226,29 +232,28 @@ uint Window::getWidth() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

const double width = puglGetFrame(pData->view).width;
DISTRHO_SAFE_ASSERT_RETURN(width > 0.0, 0);
return static_cast<uint>(width + 0.5);
const PuglSpan width = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).width;
DISTRHO_SAFE_ASSERT(width > 0);
return width;
}

uint Window::getHeight() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, 0);

const double height = puglGetFrame(pData->view).height;
DISTRHO_SAFE_ASSERT_RETURN(height > 0.0, 0);
return static_cast<uint>(height + 0.5);
const PuglSpan height = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE).height;
DISTRHO_SAFE_ASSERT(height > 0);
return height;
}

Size<uint> Window::getSize() const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pData->view != nullptr, Size<uint>());

const PuglRect rect = puglGetFrame(pData->view);
DISTRHO_SAFE_ASSERT_RETURN(rect.width > 0.0, Size<uint>());
DISTRHO_SAFE_ASSERT_RETURN(rect.height > 0.0, Size<uint>());
return Size<uint>(static_cast<uint>(rect.width + 0.5),
static_cast<uint>(rect.height + 0.5));
const PuglArea size = puglGetSizeHint(pData->view, PUGL_CURRENT_SIZE);
DISTRHO_SAFE_ASSERT(size.width > 0);
DISTRHO_SAFE_ASSERT(size.height > 0);
return Size<uint>(size.width, size.height);
}

void Window::setWidth(const uint width)
@@ -421,6 +426,20 @@ bool Window::openFileBrowser(const FileBrowserOptions& options)
}
#endif

#ifdef DGL_USE_WEB_VIEW
bool Window::createWebView(const char* const url, const DGL_NAMESPACE::WebViewOptions& options)
{
return pData->createWebView(url, options);
}

void Window::evaluateJS(const char* const js)
{
DISTRHO_SAFE_ASSERT_RETURN(pData->webViewHandle != nullptr,);

webViewEvaluateJS(pData->webViewHandle, js);
}
#endif

void Window::repaint() noexcept
{
if (pData->view == nullptr)
@@ -429,7 +448,7 @@ void Window::repaint() noexcept
if (pData->usesScheduledRepaints)
pData->appData->needsRepaint = true;

puglPostRedisplay(pData->view);
puglObscureView(pData->view);
}

void Window::repaint(const Rectangle<uint>& rect) noexcept
@@ -440,22 +459,22 @@ void Window::repaint(const Rectangle<uint>& rect) noexcept
if (pData->usesScheduledRepaints)
pData->appData->needsRepaint = true;

PuglRect prect = {
static_cast<PuglCoord>(rect.getX()),
static_cast<PuglCoord>(rect.getY()),
static_cast<PuglSpan>(rect.getWidth()),
static_cast<PuglSpan>(rect.getHeight()),
};
int x = static_cast<int>(rect.getX());
int y = static_cast<int>(rect.getY());
uint width = rect.getWidth();
uint height = rect.getHeight();

if (pData->autoScaling)
{
const double autoScaleFactor = pData->autoScaleFactor;

prect.x = static_cast<PuglCoord>(prect.x * autoScaleFactor);
prect.y = static_cast<PuglCoord>(prect.y * autoScaleFactor);
prect.width = static_cast<PuglSpan>(prect.width * autoScaleFactor + 0.5);
prect.height = static_cast<PuglSpan>(prect.height * autoScaleFactor + 0.5);
x = d_roundToIntPositive(x * autoScaleFactor);
y = d_roundToIntPositive(y * autoScaleFactor);
width = d_roundToUnsignedInt(width * autoScaleFactor);
height = d_roundToUnsignedInt(height * autoScaleFactor);
}
puglPostRedisplayRect(pData->view, prect);

puglObscureRegion(pData->view, x, y, width, height);
}

void Window::renderToPicture(const char* const filename)
@@ -509,8 +528,8 @@ void Window::setGeometryConstraints(uint minimumWidth,
{
const Size<uint> size(getSize());

setSize(static_cast<uint>(size.getWidth() * scaleFactor + 0.5),
static_cast<uint>(size.getHeight() * scaleFactor + 0.5));
setSize(d_roundToUnsignedInt(size.getWidth() * scaleFactor),
d_roundToUnsignedInt(size.getHeight() * scaleFactor));
}
}



+ 118
- 45
dgl/src/WindowPrivateData.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -40,13 +40,11 @@ START_NAMESPACE_DGL
#endif

#ifdef DGL_DEBUG_EVENTS
# define DGL_DBG(msg) std::fprintf(stderr, "%s", msg);
# define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__);
# define DGL_DBGF std::fflush(stderr);
# define DGL_DBG(msg) d_stdout("%s", msg);
# define DGL_DBGp(...) d_stdout(__VA_ARGS__);
#else
# define DGL_DBG(msg)
# define DGL_DBGp(...)
# define DGL_DBGF
#endif

#define DEFAULT_WIDTH 640
@@ -93,10 +91,10 @@ static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintp

if (PuglView* const view = puglNewView(world))
{
puglSetParentWindow(view, parentWindowHandle);
puglSetParent(view, parentWindowHandle);

if (parentWindowHandle != 0)
puglSetPosition(view, 0, 0);
puglSetPositionHint(view, PUGL_DEFAULT_POSITION, 0, 0);

return view;
}
@@ -130,6 +128,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s)
filenameToRenderInto(nullptr),
#ifdef DGL_USE_FILE_BROWSER
fileBrowserHandle(nullptr),
#endif
#ifdef DGL_USE_WEB_VIEW
webViewHandle(nullptr),
#endif
modal()
{
@@ -160,6 +161,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* c
filenameToRenderInto(nullptr),
#ifdef DGL_USE_FILE_BROWSER
fileBrowserHandle(nullptr),
#endif
#ifdef DGL_USE_WEB_VIEW
webViewHandle(nullptr),
#endif
modal(ppData)
{
@@ -192,6 +196,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
filenameToRenderInto(nullptr),
#ifdef DGL_USE_FILE_BROWSER
fileBrowserHandle(nullptr),
#endif
#ifdef DGL_USE_WEB_VIEW
webViewHandle(nullptr),
#endif
modal()
{
@@ -227,6 +234,9 @@ Window::PrivateData::PrivateData(Application& a, Window* const s,
filenameToRenderInto(nullptr),
#ifdef DGL_USE_FILE_BROWSER
fileBrowserHandle(nullptr),
#endif
#ifdef DGL_USE_WEB_VIEW
webViewHandle(nullptr),
#endif
modal()
{
@@ -247,6 +257,10 @@ Window::PrivateData::~PrivateData()
#ifdef DGL_USE_FILE_BROWSER
if (fileBrowserHandle != nullptr)
fileBrowserClose(fileBrowserHandle);
#endif
#ifdef DGL_USE_WEB_VIEW
if (webViewHandle != nullptr)
webViewDestroy(webViewHandle);
#endif
puglHide(view);
appData->oneWindowClosed();
@@ -254,6 +268,9 @@ Window::PrivateData::~PrivateData()
isVisible = false;
}

#ifndef DPF_TEST_WINDOW_CPP
destroyContext();
#endif
puglFreeView(view);
}

@@ -286,8 +303,8 @@ void Window::PrivateData::initPre(const uint width, const uint height, const boo
// PUGL_SAMPLES ??
puglSetEventFunc(view, puglEventCallback);

// setting default size triggers system-level calls, do it last
puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height));
// setting size triggers system-level calls, do it last
puglSetSizeAndDefault(view, width, height);
}

bool Window::PrivateData::initPost()
@@ -352,10 +369,6 @@ void Window::PrivateData::show()
isClosed = false;
appData->oneWindowShown();

// FIXME
// PuglRect rect = puglGetFrame(view);
// puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height));

#if defined(DISTRHO_OS_WINDOWS)
puglWin32ShowCentered(view);
#elif defined(DISTRHO_OS_MAC)
@@ -394,13 +407,21 @@ void Window::PrivateData::hide()
if (modal.enabled)
stopModal();

#ifdef DGL_USE_FILE_BROWSER
#ifdef DGL_USE_FILE_BROWSER
if (fileBrowserHandle != nullptr)
{
fileBrowserClose(fileBrowserHandle);
fileBrowserHandle = nullptr;
}
#endif
#endif

#ifdef DGL_USE_WEB_VIEW
if (webViewHandle != nullptr)
{
webViewDestroy(webViewHandle);
webViewHandle = nullptr;
}
#endif

puglHide(view);

@@ -431,6 +452,13 @@ void Window::PrivateData::setResizable(const bool resizable)
puglSetResizable(view, resizable);
}

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

const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
{
return reinterpret_cast<const GraphicsContext&>(graphicsContext);
}

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

void Window::PrivateData::idleCallback()
@@ -443,6 +471,11 @@ void Window::PrivateData::idleCallback()
fileBrowserHandle = nullptr;
}
#endif

#ifdef DGL_USE_WEB_VIEW
if (webViewHandle != nullptr)
webViewIdle(webViewHandle);
#endif
}

// -----------------------------------------------------------------------
@@ -479,7 +512,7 @@ bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback)

#ifdef DGL_USE_FILE_BROWSER
// -----------------------------------------------------------------------
// file handling
// file browser dialog

bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
{
@@ -491,6 +524,8 @@ bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
if (options2.title == nullptr)
options2.title = puglGetViewString(view, PUGL_WINDOW_TITLE);

options2.className = puglGetViewString(view, PUGL_CLASS_NAME);

fileBrowserHandle = fileBrowserCreate(isEmbed,
puglGetNativeView(view),
autoScaling ? autoScaleFactor : scaleFactor,
@@ -500,12 +535,38 @@ bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options)
}
#endif // DGL_USE_FILE_BROWSER

#ifdef DGL_USE_WEB_VIEW
// -----------------------------------------------------------------------
// file browser dialog

bool Window::PrivateData::createWebView(const char* const url, const DGL_NAMESPACE::WebViewOptions& options)
{
if (webViewHandle != nullptr)
webViewDestroy(webViewHandle);

const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
uint initialWidth = size.width - options.offset.x;
uint initialHeight = size.height - options.offset.y;

webViewOffset = Point<int>(options.offset.x, options.offset.y);

webViewHandle = webViewCreate(url,
puglGetNativeView(view),
initialWidth,
initialHeight,
autoScaling ? autoScaleFactor : scaleFactor,
options);

return webViewHandle != nullptr;
}
#endif // DGL_USE_WEB_VIEW

// -----------------------------------------------------------------------
// modal handling

void Window::PrivateData::startModal()
{
DGL_DBG("Window modal loop starting..."); DGL_DBGF;
DGL_DBG("Window modal loop starting...");
DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show());

// activate modal mode for this window
@@ -527,7 +588,7 @@ void Window::PrivateData::startModal()

void Window::PrivateData::stopModal()
{
DGL_DBG("Window modal loop stopping..."); DGL_DBGF;
DGL_DBG("Window modal loop stopping...");

// deactivate modal mode
modal.enabled = false;
@@ -579,11 +640,15 @@ void Window::PrivateData::runAsModal(const bool blockWait)
// -----------------------------------------------------------------------
// pugl events

void Window::PrivateData::onPuglConfigure(const double width, const double height)
void Window::PrivateData::onPuglConfigure(const uint width, const uint height)
{
DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,);

DGL_DBGp("PUGL: onReshape : %f %f\n", width, height);
DGL_DBGp("PUGL: onReshape : %d %d\n", width, height);

#ifndef DPF_TEST_WINDOW_CPP
createContextIfNeeded();
#endif

if (autoScaling)
{
@@ -596,8 +661,16 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh
autoScaleFactor = 1.0;
}

const uint uwidth = static_cast<uint>(width / autoScaleFactor + 0.5);
const uint uheight = static_cast<uint>(height / autoScaleFactor + 0.5);
const uint uwidth = d_roundToUnsignedInt(width / autoScaleFactor);
const uint uheight = d_roundToUnsignedInt(height / autoScaleFactor);

#ifdef DGL_USE_WEB_VIEW
if (webViewHandle != nullptr)
webViewResize(webViewHandle,
uwidth - webViewOffset.getX(),
uheight - webViewOffset.getY(),
autoScaling ? autoScaleFactor : scaleFactor);
#endif

self->onReshape(uwidth, uheight);

@@ -619,7 +692,7 @@ void Window::PrivateData::onPuglConfigure(const double width, const double heigh
#endif

// always repaint after a resize
puglPostRedisplay(view);
puglObscureView(view);
}

void Window::PrivateData::onPuglExpose()
@@ -629,6 +702,8 @@ void Window::PrivateData::onPuglExpose()
puglOnDisplayPrepare(view);

#ifndef DPF_TEST_WINDOW_CPP
startContext();

FOR_EACH_TOP_LEVEL_WIDGET(it)
{
TopLevelWidget* const widget(*it);
@@ -639,11 +714,13 @@ void Window::PrivateData::onPuglExpose()

if (char* const filename = filenameToRenderInto)
{
const PuglRect rect = puglGetFrame(view);
const PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
filenameToRenderInto = nullptr;
renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height));
renderToPicture(filename, getGraphicsContext(), size.width, size.height);
std::free(filename);
}

endContext();
#endif
}

@@ -860,7 +937,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
{
Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view);
#if defined(DEBUG) && defined(DGL_DEBUG_EVENTS)
if (event->type != PUGL_TIMER) {
if (event->type != PUGL_TIMER && event->type != PUGL_EXPOSE && event->type != PUGL_MOTION) {
printEvent(event, "pugl event: ", true);
}
#endif
@@ -915,7 +992,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)));
#endif
#ifdef DGL_USING_X11
puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone);
puglX11SetWindowType(view, pData->appData->isStandalone);
#endif
}
break;
@@ -962,7 +1039,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
Widget::KeyboardEvent ev;
ev.mod = event->key.state;
ev.flags = event->key.flags;
ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5);
ev.time = d_roundToUnsignedInt(event->key.time * 1000.0);
ev.press = event->type == PUGL_KEY_PRESS;
ev.key = event->key.key;
ev.keycode = event->key.keycode;
@@ -985,7 +1062,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
Widget::CharacterInputEvent ev;
ev.mod = event->text.state;
ev.flags = event->text.flags;
ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5);
ev.time = d_roundToUnsignedInt(event->text.time * 1000.0);
ev.keycode = event->text.keycode;
ev.character = event->text.character;
std::strncpy(ev.string, event->text.string, sizeof(ev.string));
@@ -1008,7 +1085,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
Widget::MouseEvent ev;
ev.mod = event->button.state;
ev.flags = event->button.flags;
ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5);
ev.time = d_roundToUnsignedInt(event->button.time * 1000.0);
ev.button = event->button.button + 1;
ev.press = event->type == PUGL_BUTTON_PRESS;
if (pData->autoScaling && 0)
@@ -1031,7 +1108,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
Widget::MotionEvent ev;
ev.mod = event->motion.state;
ev.flags = event->motion.flags;
ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5);
ev.time = d_roundToUnsignedInt(event->motion.time * 1000.0);
if (pData->autoScaling && 0)
{
const double scaleFactor = pData->autoScaleFactor;
@@ -1052,7 +1129,7 @@ PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const Pu
Widget::ScrollEvent ev;
ev.mod = event->scroll.state;
ev.flags = event->scroll.flags;
ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5);
ev.time = d_roundToUnsignedInt(event->scroll.time * 1000.0);
if (pData->autoScaling && 0)
{
const double scaleFactor = pData->autoScaleFactor;
@@ -1119,7 +1196,7 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver
{
#define FFMT "%6.1f"
#define PFMT FFMT " " FFMT
#define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#define PRINT(fmt, ...) d_stdout(fmt, __VA_ARGS__), 1

switch (event->type) {
case PUGL_NOTHING:
@@ -1188,25 +1265,21 @@ static int printEvent(const PuglEvent* event, const char* prefix, const bool ver

if (verbose) {
switch (event->type) {
case PUGL_CREATE:
return fprintf(stderr, "%sCreate\n", prefix);
case PUGL_DESTROY:
return fprintf(stderr, "%sDestroy\n", prefix);
case PUGL_MAP:
return fprintf(stderr, "%sMap\n", prefix);
case PUGL_UNMAP:
return fprintf(stderr, "%sUnmap\n", prefix);
case PUGL_UPDATE:
return 0; // fprintf(stderr, "%sUpdate\n", prefix);
case PUGL_REALIZE:
return PRINT("%sRealize\n", prefix);
case PUGL_UNREALIZE:
return PRINT("%sUnrealize\n", prefix);
case PUGL_CONFIGURE:
return PRINT("%sConfigure " PFMT " " PFMT "\n",
return PRINT("%sConfigure %d %d %d %d\n",
prefix,
event->configure.x,
event->configure.y,
event->configure.width,
event->configure.height);
case PUGL_UPDATE:
return 0; // fprintf(stderr, "%sUpdate\n", prefix);
case PUGL_EXPOSE:
return PRINT("%sExpose " PFMT " " PFMT "\n",
return PRINT("%sExpose %d %d %d %d\n",
prefix,
event->expose.x,
event->expose.y,


+ 19
- 4
dgl/src/WindowPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -45,7 +45,11 @@ struct Window::PrivateData : IdleCallback {
PuglView* view;

/** Reserved space for graphics context. */
mutable uint8_t graphicsContext[sizeof(void*)];
mutable uint8_t graphicsContext[sizeof(int) * 9];
void createContextIfNeeded();
void destroyContext();
void startContext();
void endContext();

/** The top-level widgets associated with this Window. */
std::list<TopLevelWidget*> topLevelWidgets;
@@ -95,6 +99,12 @@ struct Window::PrivateData : IdleCallback {
DGL_NAMESPACE::FileBrowserHandle fileBrowserHandle;
#endif

#ifdef DGL_USE_WEB_VIEW
/** Handle for web view operations. */
DGL_NAMESPACE::WebViewHandle webViewHandle;
DGL_NAMESPACE::Point<int> webViewOffset;
#endif

/** Modal window setup. */
struct Modal {
PrivateData* parent; // parent of this window (so we can become modal)
@@ -169,10 +179,15 @@ struct Window::PrivateData : IdleCallback {
bool removeIdleCallback(IdleCallback* callback);

#ifdef DGL_USE_FILE_BROWSER
// file handling
// file browser dialog
bool openFileBrowser(const DGL_NAMESPACE::FileBrowserOptions& options);
#endif

#ifdef DGL_USE_WEB_VIEW
// web view
bool createWebView(const char* url, const DGL_NAMESPACE::WebViewOptions& options);
#endif

static void renderToPicture(const char* filename, const GraphicsContext& context, uint width, uint height);

// modal handling
@@ -181,7 +196,7 @@ struct Window::PrivateData : IdleCallback {
void runAsModal(bool blockWait);

// pugl events
void onPuglConfigure(double width, double height);
void onPuglConfigure(uint width, uint height);
void onPuglExpose();
void onPuglClose();
void onPuglFocus(bool focus, CrossingMode mode);


+ 24
- 20
dgl/src/nanovg/nanovg_gl.h View File

@@ -173,7 +173,7 @@ struct GLNVGtexture {
int width, height;
int type;
int flags;
#if defined NANOVG_GLES2
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
unsigned char* data;
#endif
};
@@ -813,10 +813,10 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
switch (type)
{
case NVG_TEXTURE_BGR:
#if NANOVG_GLES2
// GLES2 cannot handle GL_BGR, do local conversion to GL_RGB
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// GLES cannot handle GL_BGR, do local conversion to GL_RGB
tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 3 * w * h);
for (uint32_t i=0; i<w*h; ++i)
for (int i = 0; i < w * h; ++i)
{
tex->data[i*3+0] = data[i*3+2];
tex->data[i*3+1] = data[i*3+1];
@@ -829,34 +829,34 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int im
#endif
break;
case NVG_TEXTURE_BGRA:
#if NANOVG_GLES2
// GLES2 cannot handle GL_BGRA, do local conversion to GL_RGBA
#if defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
// GLES cannot handle GL_BGRA, do local conversion to GL_RGBA
tex->data = (uint8_t*)malloc(sizeof(uint8_t) * 4 * w * h);
for (uint32_t i=0; i<w*h; ++i)
for (int i = 0; i < w * h; ++i)
{
tex->data[i*3+0] = data[i*3+3];
tex->data[i*3+1] = data[i*3+2];
tex->data[i*3+2] = data[i*3+1];
tex->data[i*3+3] = data[i*3+0];
tex->data[i*4+0] = data[i*4+2];
tex->data[i*4+1] = data[i*4+1];
tex->data[i*4+2] = data[i*4+0];
tex->data[i*4+3] = data[i*4+3];
}
data = tex->data;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
#else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
#endif
break;
case NVG_TEXTURE_RGB:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_RGBA:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
break;
default:
#if defined(NANOVG_GLES2) || defined (NANOVG_GL2)
#if defined(NANOVG_GL2) || defined(NANOVG_GLES2)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#elif defined(NANOVG_GLES3)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);
#endif
break;
@@ -960,19 +960,23 @@ static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w
switch (tex->type)
{
case NVG_TEXTURE_BGR:
#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3))
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGR, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
#endif
case NVG_TEXTURE_RGB:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGB, GL_UNSIGNED_BYTE, data);
break;
case NVG_TEXTURE_BGRA:
#if !(defined(NANOVG_GLES2) || defined(NANOVG_GLES3))
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_BGRA, GL_UNSIGNED_BYTE, data);
break;
#endif
case NVG_TEXTURE_RGBA:
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);
break;
default:
#if defined(NANOVG_GLES2) || defined(NANOVG_GL2)
#if defined(NANOVG_GL2) || defined(NANOVG_GLES2)
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);


+ 92
- 34
dgl/src/pugl-extra/wasm.c View File

@@ -1,10 +1,11 @@
// Copyright 2012-2022 David Robillard <d@drobilla.net>
// Copyright 2021-2022 Filipe Coelho <falktx@falktx.com>
// Copyright 2021-2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

#include "wasm.h"

#include "../pugl-upstream/src/internal.h"
#include "../pugl-upstream/src/platform.h"

#include <stdio.h>

@@ -82,6 +83,73 @@ puglInitViewInternals(PuglWorld* const world)
return impl;
}

PuglStatus
puglApplySizeHint(PuglView* const view, const PuglSizeHint PUGL_UNUSED(hint))
{
// No fine-grained updates, hints are always recalculated together
return puglUpdateSizeHints(view);
}

PuglStatus
puglUpdateSizeHints(PuglView* const view)
{
const char* const className = view->world->strings[PUGL_CLASS_NAME];

if (!view->hints[PUGL_RESIZABLE]) {
PuglArea size = puglGetSizeHint(view, PUGL_CURRENT_SIZE);
if (!puglIsValidSize(size.width, size.height)) {
size = puglGetSizeHint(view, PUGL_DEFAULT_SIZE);
}
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
var width = parseInt($1 / window.devicePixelRatio);
var height = parseInt($2 / window.devicePixelRatio);
canvasWrapper.style.setProperty("min-width", width + 'px');
canvasWrapper.style.setProperty("max-width", width + 'px');
canvasWrapper.style.setProperty("min-height", height + 'px');
canvasWrapper.style.setProperty("max-height", height + 'px');
}, className, size.width, size.height);
} else {
const PuglArea minSize = view->sizeHints[PUGL_MIN_SIZE];
if (puglIsValidArea(minSize)) {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, minSize.width, minSize.height);
} else {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.removeProperty("min-width");
canvasWrapper.style.removeProperty("min-height");
}, className);
}

const PuglArea maxSize = view->sizeHints[PUGL_MAX_SIZE];
if (puglIsValidArea(maxSize)) {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("max-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("max-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, maxSize.width, maxSize.height);
} else {
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.removeProperty("max-width");
canvasWrapper.style.removeProperty("max-height");
}, className);
}

/* TODO
const PuglArea minAspect = view->sizeHints[PUGL_MIN_ASPECT];
const PuglArea maxAspect = view->sizeHints[PUGL_MAX_ASPECT];
const PuglArea fixedAspect = view->sizeHints[PUGL_FIXED_ASPECT];
*/
}

return PUGL_SUCCESS;
}

static PuglStatus
puglDispatchEventWithContext(PuglView* const view, const PuglEvent* event)
{
@@ -729,7 +797,8 @@ puglRealize(PuglView* const view)
const char* const className = view->world->strings[PUGL_CLASS_NAME];
d_stdout("className is %s", className);

PuglViewSize defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
const PuglPoint defaultPos = view->positionHints[PUGL_DEFAULT_POSITION];
const PuglArea defaultSize = view->sizeHints[PUGL_DEFAULT_SIZE];
if (!defaultSize.width || !defaultSize.height) {
return PUGL_BAD_CONFIGURATION;
}
@@ -747,8 +816,8 @@ puglRealize(PuglView* const view)
puglDispatchSimpleEvent(view, PUGL_REALIZE);

PuglEvent event = {{PUGL_CONFIGURE, 0}};
event.configure.x = view->defaultX;
event.configure.y = view->defaultY;
event.configure.x = defaultPos.x;
event.configure.y = defaultPos.y;
event.configure.width = defaultSize.width;
event.configure.height = defaultSize.height;
puglDispatchEvent(view, &event);
@@ -760,6 +829,8 @@ puglRealize(PuglView* const view)
canvasWrapper.style.setProperty("--device-pixel-ratio", window.devicePixelRatio);
}, className);

puglUpdateSizeHints(view);

emscripten_set_canvas_element_size(className, defaultSize.width, defaultSize.height);
#ifndef PUGL_WASM_NO_KEYBOARD_INPUT
// emscripten_set_keypress_callback(className, view, false, puglKeyCallback);
@@ -794,7 +865,7 @@ puglShow(PuglView* const view, PuglShowCommand)
{
view->impl->visible = true;
view->impl->needsRepaint = true;
return puglPostRedisplay(view);
return puglObscureView(view);
}

PuglStatus
@@ -884,15 +955,23 @@ puglUpdate(PuglWorld* const world, const double timeout)
}

PuglStatus
puglPostRedisplay(PuglView* const view)
puglObscureView(PuglView* const view)
{
view->impl->needsRepaint = true;
return PUGL_SUCCESS;
}

PuglStatus
puglPostRedisplayRect(PuglView* const view, const PuglRect rect)
puglObscureRegion(PuglView* view,
const int x,
const int y,
const unsigned width,
const unsigned height)
{
if (!puglIsValidPosition(x, y) || !puglIsValidSize(width, height)) {
return PUGL_BAD_PARAMETER;
}

view->impl->needsRepaint = true;
return PUGL_FAILURE;
}
@@ -918,13 +997,13 @@ puglViewStringChanged(PuglView*, const PuglStringHint key, const char* const val
}

PuglStatus
puglSetSizeHint(PuglView* const view,
const PuglSizeHint hint,
const PuglSpan width,
const PuglSpan height)
puglSetWindowSize(PuglView* const view, const unsigned width, const unsigned height)
{
view->sizeHints[hint].width = width;
view->sizeHints[hint].height = height;
if (view->impl->created) {
const char* const className = view->world->strings[PUGL_CLASS_NAME];
emscripten_set_canvas_element_size(className, width, height);
}

return PUGL_SUCCESS;
}

@@ -1152,24 +1231,3 @@ puglSetTransientParent(PuglView* const view, const PuglNativeView parent)
view->transientParent = parent;
return PUGL_FAILURE;
}

PuglStatus
puglSetPosition(PuglView* const view, const int x, const int y)
{
printf("TODO: %s %d\n", __func__, __LINE__);

if (x > INT16_MAX || y > INT16_MAX) {
return PUGL_BAD_PARAMETER;
}

if (!view->impl->created) {
// Set defaults to be used when realized
view->defaultX = x;
view->defaultY = y;
return PUGL_SUCCESS;
}

view->lastConfigure.x = (PuglCoord)x;
view->lastConfigure.y = (PuglCoord)y;
return puglPostRedisplay(view);
}

+ 1
- 1
dgl/src/pugl-upstream

@@ -1 +1 @@
Subproject commit 311dd39b15a1603da65769f5eb9cdea5d7e97e19
Subproject commit 5e2621d714ddf1cb0f86e852f8ba5dffe04aa3a3

+ 109
- 102
dgl/src/pugl.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -110,16 +110,26 @@
# endif
#endif

#ifndef DGL_FILE_BROWSER_DISABLED
#ifdef DGL_USE_FILE_BROWSER
# define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED
# define FILE_BROWSER_DIALOG_DGL_NAMESPACE
# define FILE_BROWSER_DIALOG_NAMESPACE DGL_NAMESPACE
# define DGL_FILE_BROWSER_DIALOG_HPP_INCLUDED
START_NAMESPACE_DGL
# include "../../distrho/extra/FileBrowserDialogImpl.hpp"
END_NAMESPACE_DGL
# include "../../distrho/extra/FileBrowserDialogImpl.cpp"
#endif

#ifdef DGL_USE_WEB_VIEW
# define DGL_WEB_VIEW_HPP_INCLUDED
# define WEB_VIEW_NAMESPACE DGL_NAMESPACE
# define WEB_VIEW_DGL_NAMESPACE
START_NAMESPACE_DGL
# include "../../distrho/extra/WebViewImpl.hpp"
END_NAMESPACE_DGL
# include "../../distrho/extra/WebViewImpl.cpp"
#endif

#if defined(DGL_USING_X11) && defined(DGL_X11_WINDOW_ICON_NAME)
extern const ulong* DGL_X11_WINDOW_ICON_NAME;
#endif
@@ -240,21 +250,23 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view)

if (view->backend != nullptr)
{
#ifdef DGL_OPENGL
#if defined(DGL_USE_GLES2)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2);
#elif defined(DGL_USE_GLES3)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_ES_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
#elif defined(DGL_USE_OPENGL3)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_CORE_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 3);
#else
#elif defined(DGL_OPENGL)
puglSetViewHint(view, PUGL_CONTEXT_API, PUGL_OPENGL_API);
puglSetViewHint(view, PUGL_CONTEXT_PROFILE, PUGL_OPENGL_COMPATIBILITY_PROFILE);
puglSetViewHint(view, PUGL_CONTEXT_VERSION_MAJOR, 2);
#endif
#endif
}
else
{
@@ -267,19 +279,20 @@ void puglSetMatchingBackendForCurrentBuild(PuglView* const view)

void puglRaiseWindow(PuglView* const view)
{
#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];
#elif defined(DISTRHO_OS_WASM)
// this does the same as puglShow(view, PUGL_SHOW_FORCE_RAISE) + puglShow(view, PUGL_SHOW_RAISE)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
NSWindow* const window = [view->impl->wrapperView window];
[window orderFrontRegardless];
[window orderFront:view->impl->wrapperView];
#elif defined(DISTRHO_OS_WASM)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WINDOWS)
SetForegroundWindow(view->impl->hwnd);
SetActiveWindow(view->impl->hwnd);
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
XRaiseWindow(view->world->impl->display, view->impl->win);
#endif
#endif
}

// --------------------------------------------------------------------------------------------------------------------
@@ -296,29 +309,31 @@ PuglStatus puglSetGeometryConstraints(PuglView* const view, const uint width, co
view->sizeHints[PUGL_FIXED_ASPECT].height = static_cast<PuglSpan>(height);
}

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
if (view->impl->window)
{
if (const PuglStatus status = updateSizeHint(view, PUGL_MIN_SIZE))
return status;

if (const PuglStatus status = updateSizeHint(view, PUGL_FIXED_ASPECT))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;
}
#elif defined(DISTRHO_OS_WASM)
#elif defined(DISTRHO_OS_WASM)
const char* const className = view->world->strings[PUGL_CLASS_NAME];
EM_ASM({
var canvasWrapper = document.getElementById(UTF8ToString($0)).parentElement;
canvasWrapper.style.setProperty("min-width", parseInt($1 / window.devicePixelRatio) + 'px');
canvasWrapper.style.setProperty("min-height", parseInt($2 / window.devicePixelRatio) + 'px');
}, className, width, height);
#elif defined(DISTRHO_OS_WINDOWS)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
// nothing
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
if (view->impl->win)
{
if (const PuglStatus status = updateSizeHints(view))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

XFlush(view->world->impl->display);
}
#endif
#endif

return PUGL_SUCCESS;
}
@@ -330,98 +345,79 @@ void puglSetResizable(PuglView* const view, const bool resizable)
{
puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE);

#if defined(DISTRHO_OS_HAIKU)
#elif 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)
| (resizable ? NSResizableWindowMask : 0x0);
| (resizable ? NSResizableWindowMask : 0);
[window setStyleMask:style];
}
// FIXME use [view setAutoresizingMask:NSViewNotSizable] ?
#elif defined(DISTRHO_OS_WASM)
// nothing
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WASM)
puglUpdateSizeHints(view);
#elif defined(DISTRHO_OS_WINDOWS)
if (const HWND hwnd = view->impl->hwnd)
{
const uint winFlags = resizable ? GetWindowLong(hwnd, GWL_STYLE) | (WS_SIZEBOX | WS_MAXIMIZEBOX)
: GetWindowLong(hwnd, GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX);
SetWindowLong(hwnd, GWL_STYLE, winFlags);
}
#elif defined(HAVE_X11)
updateSizeHints(view);
#endif
#elif defined(HAVE_X11)
puglUpdateSizeHints(view);
#endif
}

// --------------------------------------------------------------------------------------------------------------------
// set window size while also changing default

PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height)
PuglStatus puglSetSizeAndDefault(PuglView* const view, const uint width, const uint height)
{
if (width > INT16_MAX || height > INT16_MAX)
return PUGL_BAD_PARAMETER;

#ifdef DGL_USING_X11
// workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118
if (view->impl->win && !view->parent && !view->transientParent)
{
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0;
}
else
#endif
// set default size first
{
view->sizeHints[PUGL_DEFAULT_SIZE].width = static_cast<PuglSpan>(width);
view->sizeHints[PUGL_DEFAULT_SIZE].height = static_cast<PuglSpan>(height);
}
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_CURRENT_SIZE].width = width;
view->sizeHints[PUGL_DEFAULT_SIZE].height = view->sizeHints[PUGL_CURRENT_SIZE].height = height;

#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
#if defined(DISTRHO_OS_HAIKU)
#elif defined(DISTRHO_OS_MAC)
// matches upstream pugl
if (view->impl->wrapperView)
{
if (const PuglStatus status = puglSetSize(view, width, height))
return status;

// nothing to do for PUGL_DEFAULT_SIZE hint

if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;
}
#elif defined(DISTRHO_OS_WASM)
d_stdout("className is %s", view->world->strings[PUGL_CLASS_NAME]);
#elif defined(DISTRHO_OS_WASM)
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

emscripten_set_canvas_element_size(view->world->strings[PUGL_CLASS_NAME], width, height);
#elif defined(DISTRHO_OS_WINDOWS)
#elif defined(DISTRHO_OS_WINDOWS)
// matches upstream pugl, except we re-enter context after resize
if (view->impl->hwnd)
{
if (const PuglStatus status = puglSetSize(view, width, height))
return status;

// nothing to do for PUGL_DEFAULT_SIZE hint

if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;

// make sure to return context back to ourselves
puglBackendEnter(view);
}
#elif defined(HAVE_X11)
#elif defined(HAVE_X11)
// matches upstream pugl, adds flush at the end
if (view->impl->win)
{
if (const PuglStatus status = puglSetSize(view, width, height))
if (const PuglStatus status = puglUpdateSizeHints(view))
return status;

// updateSizeHints will use last known size, which is not yet updated
const PuglSpan lastWidth = view->lastConfigure.width;
const PuglSpan lastHeight = view->lastConfigure.height;
view->lastConfigure.width = static_cast<PuglSpan>(width);
view->lastConfigure.height = static_cast<PuglSpan>(height);

updateSizeHints(view);

view->lastConfigure.width = lastWidth;
view->lastConfigure.height = lastHeight;
if (const PuglStatus status = puglSetWindowSize(view, width, height))
return status;

// flush size changes
XFlush(view->world->impl->display);
}
#endif
#endif

return PUGL_SUCCESS;
}
@@ -445,11 +441,12 @@ void puglOnDisplayPrepare(PuglView*)
void puglFallbackOnResize(PuglView* const view, const uint width, const uint height)
{
#ifdef DGL_OPENGL
#if defined(DGL_USE_OPENGL3)
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
#else
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#ifdef DGL_USE_OPENGL3
glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
#else
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
@@ -458,9 +455,10 @@ void puglFallbackOnResize(PuglView* const view, const uint width, const uint hei
glLoadIdentity();
#endif
#else
return;
// unused
(void)view;
(void)width;
(void)height;
#endif
}

@@ -607,55 +605,64 @@ void puglWin32ShowCentered(PuglView* const view)

PuglStatus puglX11UpdateWithoutExposures(PuglWorld* const world)
{
const bool wasDispatchingEvents = world->impl->dispatchingEvents;
world->impl->dispatchingEvents = true;
const PuglWorldState startState = world->state;
world->state = PUGL_WORLD_UPDATING;
PuglStatus st = PUGL_SUCCESS;

const double startTime = puglGetTime(world);
const double endTime = startTime + 0.03;
const double endTime = startTime + 0.03;

for (double t = startTime; !st && t < endTime; t = puglGetTime(world))
{
pollX11Socket(world, endTime - t);
st = dispatchX11Events(world);
if (!(st = pollX11Socket(world, endTime - t)))
st = dispatchX11Events(world);
}

world->impl->dispatchingEvents = wasDispatchingEvents;
world->state = startState;
return st;
}

// --------------------------------------------------------------------------------------------------------------------
// X11 specific, set dialog window type and pid hints
// X11 specific, set dialog window type

void puglX11SetWindowTypeAndPID(const PuglView* const view, const bool isStandalone)
void puglX11SetWindowType(const PuglView* const view, const bool isStandalone)
{
const PuglInternals* const impl = view->impl;
Display* const display = view->world->impl->display;

const pid_t pid = getpid();
const Atom _nwp = XInternAtom(display, "_NET_WM_PID", False);
XChangeProperty(display, impl->win, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);

#if defined(DGL_X11_WINDOW_ICON_NAME) && defined(DGL_X11_WINDOW_ICON_SIZE)
if (isStandalone)
{
const Atom _nwi = XInternAtom(display, "_NET_WM_ICON", False);
XChangeProperty(display, impl->win, _nwi, XA_CARDINAL, 32, PropModeReplace,
(const uchar*)DGL_X11_WINDOW_ICON_NAME, DGL_X11_WINDOW_ICON_SIZE);
const Atom NET_WM_ICON = XInternAtom(display, "_NET_WM_ICON", False);
XChangeProperty(display,
impl->win,
NET_WM_ICON,
XA_CARDINAL,
32,
PropModeReplace,
reinterpret_cast<const uchar*>(DGL_X11_WINDOW_ICON_NAME),
DGL_X11_WINDOW_ICON_SIZE);
}
#endif

const Atom _wt = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
const Atom NET_WM_WINDOW_TYPE = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);

Atom _wts[2];
int numAtoms = 0;
Atom windowTypes[2];
int numWindowTypes = 0;

if (! isStandalone)
_wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);

_wts[numAtoms++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);

XChangeProperty(display, impl->win, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, numAtoms);
windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);

windowTypes[numWindowTypes++] = XInternAtom(display, "_NET_WM_WINDOW_TYPE_NORMAL", False);

XChangeProperty(display,
impl->win,
NET_WM_WINDOW_TYPE,
XA_ATOM,
32,
PropModeReplace,
reinterpret_cast<const uchar*>(&windowTypes),
numWindowTypes);
}

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


+ 3
- 3
dgl/src/pugl.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -121,8 +121,8 @@ void puglWin32ShowCentered(PuglView* view);
// X11 specific, update world without triggering exposure events
PuglStatus puglX11UpdateWithoutExposures(PuglWorld* world);

// X11 specific, set dialog window type and pid hints
void puglX11SetWindowTypeAndPID(const PuglView* view, bool isStandalone);
// X11 specific, set dialog window type
void puglX11SetWindowType(const PuglView* view, bool isStandalone);

#endif



+ 28
- 4
distrho/DistrhoDetails.hpp View File

@@ -202,7 +202,7 @@ static constexpr const uint32_t kStateIsOnlyForUI = 0x20;

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

Each designation is unique, there must be only one parameter that uses it.@n
The use of designated parameters is completely optional.
@@ -214,13 +214,20 @@ enum ParameterDesignation {
/**
Null or unset designation.
*/
kParameterDesignationNull = 0,
kParameterDesignationNull,

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

/**
Reset designation.@n
When on (> 0.5f), it means the plugin should reset its internal processing state
(like filters, oscillators, envelopes, lfos, etc) and kill all voices.
*/
kParameterDesignationReset,
};

/**
@@ -234,7 +241,12 @@ namespace ParameterDesignationSymbols {
static constexpr const char bypass[] = "dpf_bypass";

/**
Bypass designation symbol, inverted for LV2 so it becomes "enabled".
Reset designation symbol.
*/
static constexpr const char reset[] = "dpf_reset";

/**
LV2 bypass designation symbol, inverted for LV2 so it becomes "enabled".
*/
static constexpr const char bypass_lv2[] = "lv2_enabled";
};
@@ -728,6 +740,18 @@ struct Parameter {
ranges.min = 0.0f;
ranges.max = 1.0f;
break;
case kParameterDesignationReset:
hints = kParameterIsAutomatable|kParameterIsBoolean|kParameterIsInteger|kParameterIsTrigger;
name = "Reset";
shortName = "Reset";
symbol = ParameterDesignationSymbols::reset;
unit = "";
midiCC = 0;
groupId = kPortGroupNone;
ranges.def = 0.0f;
ranges.min = 0.0f;
ranges.max = 1.0f;
break;
}
}



+ 24
- 0
distrho/DistrhoInfo.hpp View File

@@ -553,6 +553,12 @@ START_NAMESPACE_DISTRHO
*/
#define DISTRHO_PLUGIN_WANT_LATENCY 1

/**
Whether the plugin wants MPE for MIDI input and/or output.
@note Only AU and CLAP formats implement this at the moment
*/
#define DISTRHO_PLUGIN_WANT_MIDI_AS_MPE 0

/**
Whether the plugin wants MIDI input.@n
This is automatically enabled if @ref DISTRHO_PLUGIN_IS_SYNTH is true.
@@ -863,6 +869,24 @@ START_NAMESPACE_DISTRHO
*/
#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.effect"

/**
Plugin name abbreviation consisting of 2 or 3 characters in uppercase.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ABBREVIATION "DFX"

/**
Path to a in-bundle/local 200x200 PNG image file to be used as the plugin's block image asset when OFF.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF "anagram-block-off.png"

/**
Path to a in-bundle/local 200x200 PNG image file to be used as the plugin's block image asset when ON.
@note This macro is required when building plugins for the Darkglass Anagram unit.
*/
#define DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON "anagram-block-on.png"

/** @} */

/* ------------------------------------------------------------------------------------------------------------


+ 3
- 4
distrho/DistrhoPluginMain.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -29,15 +29,14 @@
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# include "src/DistrhoPluginLV2.cpp"
# include "src/DistrhoPluginLV2export.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_MAPI)
# include "src/DistrhoPluginMAPI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
# include "src/DistrhoPluginVST2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST3)
# include "src/DistrhoPluginVST3.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_EXPORT)
# include "src/DistrhoPluginExport.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_SHARED)
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin();
DISTRHO_PLUGIN_EXPORT DISTRHO_NAMESPACE::Plugin* createSharedPlugin() { return DISTRHO_NAMESPACE::createPlugin(); }
#elif defined(DISTRHO_PLUGIN_TARGET_STATIC)
START_NAMESPACE_DISTRHO
Plugin* createStaticPlugin() { return createPlugin(); }


+ 32
- 3
distrho/DistrhoPluginUtils.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -39,13 +39,36 @@ const char* getBinaryFilename();

/**
Get a string representation of the current plugin format we are building against.@n
This can be "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2" or "VST3".@n
This can be "AudioUnit", "JACK/Standalone", "LADSPA", "DSSI", "LV2", "VST2", "VST3" or "CLAP".@n
This string is purely informational and must not be used to tweak plugin behaviour.

@note DO NOT CHANGE PLUGIN BEHAVIOUR BASED ON PLUGIN FORMAT.
*/
const char* getPluginFormatName() noexcept;

/**
List of supported OS-specific directories to be used for getSpecialDir.
*/
enum SpecialDir {
/** The user "home" directory */
kSpecialDirHome,
/** Directory intended to store persistent configuration data (in general) */
kSpecialDirConfig,
/** Directory intended to store persistent configuration data for the current plugin */
kSpecialDirConfigForPlugin,
/** Directory intended to store "documents" (in general) */
kSpecialDirDocuments,
/** Directory intended to store "documents" for the current plugin */
kSpecialDirDocumentsForPlugin,
};

/**
Get an OS-specific directory.@n
Calling this function will ensure the dictory exists on the filesystem.@n
The returned path always includes a final OS separator.
*/
const char* getSpecialDir(SpecialDir dir);

/**
Get the path to where resources are stored within the plugin bundle.@n
Requires a valid plugin bundle path.
@@ -62,7 +85,13 @@ const char* getPluginFormatName() noexcept;
The other non-mentioned formats do not support bundles.@n

@note For CLAP and VST2 on non-macOS systems, this assumes you have your plugin inside a dedicated directory
rather than only shipping with the binary (e.g. <myplugin.vst>/myplugin.dll)
rather than only shipping with the binary, like so:
@code
+ myplugin.vst/
- myplugin.dll
+ resources/
- image1.png
@endcode
*/
const char* getResourcePath(const char* bundlePath) noexcept;



+ 10
- 7
distrho/DistrhoUI.hpp View File

@@ -106,10 +106,11 @@ public:

The following example code can be use to extract individual colors:
```
const int red = (bgColor >> 24) & 0xff;
const int green = (bgColor >> 16) & 0xff;
const int blue = (bgColor >> 8) & 0xff;
int red = (bgColor >> 24) & 0xff;
int green = (bgColor >> 16) & 0xff;
int blue = (bgColor >> 8) & 0xff;
```
@see Color::fromRGB
*/
uint getBackgroundColor() const noexcept;

@@ -119,10 +120,11 @@ public:

The following example code can be use to extract individual colors:
```
const int red = (fgColor >> 24) & 0xff;
const int green = (fgColor >> 16) & 0xff;
const int blue = (fgColor >> 8) & 0xff;
int red = (fgColor >> 24) & 0xff;
int green = (fgColor >> 16) & 0xff;
int blue = (fgColor >> 8) & 0xff;
```
@see Color::fromRGB
*/
uint getForegroundColor() const noexcept;

@@ -185,7 +187,8 @@ public:
#if DISTRHO_UI_FILE_BROWSER
/**
Open a file browser dialog with this window as transient parent.@n
A few options can be specified to setup the dialog.
A few options can be specified to setup the dialog.@n
The @a DISTRHO_NAMESPACE::FileBrowserOptions::className variable is automatically set in this call.

If a path is selected, onFileSelected() will be called with the user chosen path.
If the user cancels or does not pick a file, onFileSelected() will be called with nullptr as filename.


+ 14
- 3
distrho/DistrhoUIMain.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -36,7 +36,13 @@
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 0
# include "src/DistrhoUIDSSI.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_LV2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# if defined(DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT)
# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# warning Using single/monolithic LV2 target while DISTRHO_PLUGIN_WANT_DIRECT_ACCESS is 0
# endif
# else
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# endif
# include "src/DistrhoUILV2.cpp"
#elif defined(DISTRHO_PLUGIN_TARGET_VST2)
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT 1
@@ -66,11 +72,16 @@
# endif
#endif

#if defined(DPF_USING_LD_LINUX_WEBVIEW) && !DISTRHO_IS_STANDALONE
#if defined(DISTRHO_UI_LINUX_WEBVIEW_START) && !DISTRHO_IS_STANDALONE
int main(int argc, char* argv[])
{
return DISTRHO_NAMESPACE::dpf_webview_start(argc, argv);
}
#elif defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW) && !DISTRHO_IS_STANDALONE
int main()
{
return 0;
}
#endif

#endif

+ 1
- 0
distrho/DistrhoUI_win32.cpp View File

@@ -22,6 +22,7 @@

#if DISTRHO_UI_WEB_VIEW
# define DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION
# define WEB_VIEW_NAMESPACE DISTRHO_NAMESPACE
# include "extra/WebView.hpp"
# include "extra/WebViewWin32.hpp"
#endif

+ 8
- 1
distrho/DistrhoUtils.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -23,6 +23,13 @@
# define __STDC_LIMIT_MACROS
#endif

#ifdef __WINE__
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <winsock2.h>
#endif

#include <cstdarg>
#include <cstdio>
#include <cstdlib>


+ 12
- 6
distrho/extra/Base64.hpp View File

@@ -86,15 +86,15 @@ bool isBase64Char(const char c)
// --------------------------------------------------------------------------------------------------------------------

static inline
std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string)
void d_getChunkFromBase64String_impl(std::vector<uint8_t>& vector, const char* const base64string)
{
DISTRHO_SAFE_ASSERT_RETURN(base64string != nullptr, std::vector<uint8_t>());
vector.clear();
DISTRHO_SAFE_ASSERT_RETURN(base64string != nullptr,);

uint i=0, j=0;
uint charArray3[3], charArray4[4];

std::vector<uint8_t> ret;
ret.reserve(std::strlen(base64string)*3/4 + 4);
vector.reserve(std::strlen(base64string)*3/4 + 4);

for (std::size_t l=0, len=std::strlen(base64string); l<len; ++l)
{
@@ -119,7 +119,7 @@ std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string)
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];

for (i=0; i<3; ++i)
ret.push_back(static_cast<uint8_t>(charArray3[i]));
vector.push_back(static_cast<uint8_t>(charArray3[i]));

i = 0;
}
@@ -138,9 +138,15 @@ std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string)
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3];

for (j=0; i>0 && j<i-1; j++)
ret.push_back(static_cast<uint8_t>(charArray3[j]));
vector.push_back(static_cast<uint8_t>(charArray3[j]));
}
}

static inline
std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string)
{
std::vector<uint8_t> ret;
d_getChunkFromBase64String_impl(ret, base64string);
return ret;
}



+ 33
- 10
distrho/extra/ChildProcess.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -30,21 +30,31 @@
# include <sys/wait.h>
#endif

#if defined(DISTRHO_OS_LINUX)
# include <sys/prctl.h>
#elif defined(DISTRHO_OS_MAC)
# include <dispatch/dispatch.h>
#endif

START_NAMESPACE_DISTRHO

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

class ChildProcess
{
#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
PROCESS_INFORMATION pinfo;
#else
pid_t pid;
#endif

#ifdef DISTRHO_OS_MAC
static void _proc_exit_handler(void*) { ::kill(::getpid(), SIGTERM); }
#endif

public:
ChildProcess()
#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
: pinfo(CPP_AGGREGATE_INIT(PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 })
#else
: pid(-1)
@@ -57,13 +67,13 @@ public:
stop();
}

#ifdef _WIN32
bool start(const char* const args[], const WCHAR* const envp)
#ifdef DISTRHO_OS_WINDOWS
bool start(const char* const args[], const WCHAR* const envp = nullptr)
#else
bool start(const char* const args[], char* const* const envp = nullptr)
#endif
{
#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
std::string cmd;

for (uint i = 0; args[i] != nullptr; ++i)
@@ -109,6 +119,19 @@ public:
{
// child process
case 0:
#if defined(DISTRHO_OS_LINUX)
::prctl(PR_SET_PDEATHSIG, SIGTERM);
#elif defined(DISTRHO_OS_MAC)
if (const dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
::getppid(),
DISPATCH_PROC_EXIT,
nullptr))
{
dispatch_source_set_event_handler_f(source, _proc_exit_handler);
dispatch_resume(source);
}
#endif

if (envp != nullptr)
execve(args[0], const_cast<char* const*>(args), envp);
else
@@ -133,7 +156,7 @@ public:
const uint32_t timeout = d_gettime_ms() + timeoutInMilliseconds;
bool sendTerminate = true;

#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
if (pinfo.hProcess == INVALID_HANDLE_VALUE)
return;

@@ -234,7 +257,7 @@ public:

bool isRunning()
{
#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
if (pinfo.hProcess == INVALID_HANDLE_VALUE)
return false;

@@ -267,7 +290,7 @@ public:
#endif
}

#ifndef _WIN32
#ifndef DISTRHO_OS_WINDOWS
void signal(const int sig)
{
if (pid > 0)
@@ -277,7 +300,7 @@ public:

void terminate()
{
#ifdef _WIN32
#ifdef DISTRHO_OS_WINDOWS
if (pinfo.hProcess != INVALID_HANDLE_VALUE)
TerminateProcess(pinfo.hProcess, 15);
#else


+ 114
- 0
distrho/extra/Filesystem.hpp View File

@@ -0,0 +1,114 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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_FILESYSTEM_UTILS_HPP_INCLUDED
#define DISTRHO_FILESYSTEM_UTILS_HPP_INCLUDED

#include "String.hpp"

#include <cstdio>

#ifdef DISTRHO_OS_WINDOWS
# include <stringapiset.h>
#else
# include <cerrno>
#endif

START_NAMESPACE_DISTRHO

// --------------------------------------------------------------------------------------------------------------------
// filesystem related calls

/*
* Wrapper around `fopen` call, needed on Windows because its C standard functions use ASCII instead of UTF-8.
*/
static inline
FILE* d_fopen(const char* const pathname, const char* const mode)
{
#ifdef DISTRHO_OS_WINDOWS
WCHAR lpathname[MAX_PATH];
WCHAR lmode[4];
if (MultiByteToWideChar(CP_UTF8, 0, pathname, -1, lpathname, ARRAY_SIZE(lpathname)) != 0 &&
MultiByteToWideChar(CP_UTF8, 0, mode, -1, lmode, ARRAY_SIZE(lmode)) != 0)
return _wfopen(lpathname, lmode);
#endif

return fopen(pathname, mode);
}

// --------------------------------------------------------------------------------------------------------------------
// filesystem related classes

/**
Handy class to help write files in a safe way, which does:
- open pathname + ".tmp" instead of opening a file directly (so partial writes are safe)
- on close, flush data to disk and rename file to remove ".tmp"

To use it, create a local variable (on the stack) and call ok() or manually check @a fd variable.
@code
if (const SafeFileWriter file("/path/to/file.txt"); file.ok())
file.write("Success!");
@endcode
*/
struct SafeFileWriter
{
String filename;
FILE* const fd;

/**
Constructor, opening @a pathname + ".tmp" for writing.
*/
SafeFileWriter(const char* const pathname, const char* const mode = "w")
: filename(pathname),
fd(d_fopen(filename + ".tmp", mode))
{
#ifndef DISTRHO_OS_WINDOWS
if (fd == nullptr)
d_stderr2("failed to open '%s' for writing: %s", pathname, std::strerror(errno));
#endif
}

/**
Destructor, will flush file data contents, close and rename file.
*/
~SafeFileWriter()
{
if (fd == nullptr)
return;

std::fflush(fd);
std::fclose(fd);
std::rename(filename + ".tmp", filename);
}

/** Check if the file was opened successfully. */
inline bool ok() const noexcept
{
return fd != nullptr;
}

/** Wrapper around `fwrite`, purely for convenience. */
inline size_t write(const void* const ptr, const size_t size, const size_t nmemb = 1) const
{
return std::fwrite(ptr, size, nmemb, fd);
}
};

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

END_NAMESPACE_DISTRHO

#endif // DISTRHO_FILESYSTEM_UTILS_HPP_INCLUDED

+ 6
- 2
distrho/extra/RingBuffer.hpp View File

@@ -537,7 +537,7 @@ public:
* Commit all previous write operations to the ringbuffer.
* If a write operation has previously failed, this will reset/invalidate the previous write attempts.
*/
bool commitWrite() noexcept
bool commitWrite(const char* const debugMsg = nullptr) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(buffer != nullptr, false);

@@ -549,7 +549,11 @@ public:
}

// nothing to commit?
DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
if (debugMsg != nullptr) {
DISTRHO_CUSTOM_SAFE_ASSERT_RETURN(debugMsg, buffer->head != buffer->wrtn, false);
} else {
DISTRHO_SAFE_ASSERT_RETURN(buffer->head != buffer->wrtn, false);
}

// all ok
buffer->head = buffer->wrtn;


+ 5
- 0
distrho/extra/ScopedPointer.hpp View File

@@ -172,6 +172,11 @@ public:
/** Lets you access methods and properties of the object that this ScopedPointer refers to. */
ObjectType* operator->() const noexcept { return object; }

//==============================================================================
/** Removes the current object from this ScopedPointer and deletes it.
*/
void reset() noexcept { ObjectType* const o = object; object = nullptr; delete o; }

//==============================================================================
/** Removes the current object from this ScopedPointer without deleting it.
This will return the current object, and set the ScopedPointer to a null pointer.


+ 72
- 62
distrho/extra/String.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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

// -----------------------------------------------------------------------
@@ -49,10 +53,7 @@ public:
fBufferLen(0),
fBufferAlloc(false)
{
char ch[2];
ch[0] = c;
ch[1] = '\0';

const char ch[2] = { c, '\0' };
_dup(ch);
}

@@ -87,6 +88,19 @@ public:
_dup(strBuf);
}

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

/*
* Integer.
*/
@@ -255,7 +269,7 @@ public:
/*
* Get length of the string.
*/
std::size_t length() const noexcept
size_t length() const noexcept
{
return fBufferLen;
}
@@ -281,7 +295,7 @@ public:
*/
bool contains(const char c) const noexcept
{
for (std::size_t i=0; i<fBufferLen; ++i)
for (size_t i=0; i<fBufferLen; ++i)
{
if (fBuffer[i] == c)
return true;
@@ -320,7 +334,7 @@ public:
/*
* Check if character at 'pos' is a digit.
*/
bool isDigit(const std::size_t pos) const noexcept
bool isDigit(const size_t pos) const noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(pos < fBufferLen, false);

@@ -344,7 +358,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(prefix != nullptr, false);

const std::size_t prefixLen(std::strlen(prefix));
const size_t prefixLen(std::strlen(prefix));

if (fBufferLen < prefixLen)
return false;
@@ -369,7 +383,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(suffix != nullptr, false);

const std::size_t suffixLen(std::strlen(suffix));
const size_t suffixLen(std::strlen(suffix));

if (fBufferLen < suffixLen)
return false;
@@ -381,7 +395,7 @@ public:
* Find the first occurrence of character 'c' in the string.
* Returns "length()" if the character is not found.
*/
std::size_t find(const char c, bool* const found = nullptr) const noexcept
size_t find(const char c, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || c == '\0')
{
@@ -390,7 +404,7 @@ public:
return fBufferLen;
}

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
@@ -409,7 +423,7 @@ public:
* Find the first occurrence of string 'strBuf' in the string.
* Returns "length()" if the string is not found.
*/
std::size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
size_t find(const char* const strBuf, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
{
@@ -434,7 +448,7 @@ public:

if (found != nullptr)
*found = true;
return static_cast<std::size_t>(ret);
return static_cast<size_t>(ret);
}

if (found != nullptr)
@@ -446,7 +460,7 @@ public:
* Find the last occurrence of character 'c' in the string.
* Returns "length()" if the character is not found.
*/
std::size_t rfind(const char c, bool* const found = nullptr) const noexcept
size_t rfind(const char c, bool* const found = nullptr) const noexcept
{
if (fBufferLen == 0 || c == '\0')
{
@@ -455,7 +469,7 @@ public:
return fBufferLen;
}

for (std::size_t i=fBufferLen; i > 0; --i)
for (size_t i=fBufferLen; i > 0; --i)
{
if (fBuffer[i-1] == c)
{
@@ -474,7 +488,7 @@ public:
* Find the last occurrence of string 'strBuf' in the string.
* Returns "length()" if the string is not found.
*/
std::size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
size_t rfind(const char* const strBuf, bool* const found = nullptr) const noexcept
{
if (found != nullptr)
*found = false;
@@ -482,12 +496,12 @@ public:
if (fBufferLen == 0 || strBuf == nullptr || strBuf[0] == '\0')
return fBufferLen;

const std::size_t strBufLen(std::strlen(strBuf));
const size_t strBufLen(std::strlen(strBuf));

std::size_t ret = fBufferLen;
size_t ret = fBufferLen;
const char* tmpBuf = fBuffer;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (std::strstr(tmpBuf+1, strBuf) == nullptr && std::strncmp(tmpBuf, strBuf, strBufLen) == 0)
{
@@ -518,7 +532,7 @@ public:
{
DISTRHO_SAFE_ASSERT_RETURN(before != '\0' /* && after != '\0' */, *this);

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == before)
fBuffer[i] = after;
@@ -537,7 +551,7 @@ public:
if (fBufferLen == 0)
return *this;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] == c)
{
@@ -553,7 +567,7 @@ public:
/*
* Truncate the string to size 'n'.
*/
String& truncate(const std::size_t n) noexcept
String& truncate(const size_t n) noexcept
{
if (n >= fBufferLen)
return *this;
@@ -569,7 +583,7 @@ public:
*/
String& toBasic() noexcept
{
for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= '0' && fBuffer[i] <= '9')
continue;
@@ -593,7 +607,7 @@ public:
{
static constexpr const char kCharDiff = 'a' - 'A';

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= 'A' && fBuffer[i] <= 'Z')
fBuffer[i] = static_cast<char>(fBuffer[i] + kCharDiff);
@@ -609,7 +623,7 @@ public:
{
static constexpr const char kCharDiff = 'a' - 'A';

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
if (fBuffer[i] >= 'a' && fBuffer[i] <= 'z')
fBuffer[i] = static_cast<char>(fBuffer[i] - kCharDiff);
@@ -674,31 +688,28 @@ public:
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html
// Copyright (C) 2004-2008 René Nyffenegger

static String asBase64(const void* const data, const std::size_t dataSize)
static String asBase64(const void* const data, const size_t dataSize)
{
static constexpr const char* const kBase64Chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

#ifndef _MSC_VER
const std::size_t kTmpBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
#else
constexpr std::size_t kTmpBufSize = 65536U;
#endif
const size_t strBufSize = std::min(d_nextPowerOf2(static_cast<uint32_t>(dataSize/3)), 65536U);
char* strBuf = static_cast<char*>(std::malloc(strBufSize));
DISTRHO_SAFE_ASSERT_RETURN(strBuf != nullptr, String());

strBuf[strBufSize] = '\0';
size_t strBufIndex = 0;

const uchar* bytesToEncode((const uchar*)data);
const uchar* bytesToEncode = static_cast<const uchar*>(data);

uint i=0, j=0;
uint charArray3[3], charArray4[4];

char strBuf[kTmpBufSize + 1];
strBuf[kTmpBufSize] = '\0';
std::size_t strBufIndex = 0;

String ret;

for (std::size_t s=0; s<dataSize; ++s)
for (size_t s = 0; s < dataSize; ++s)
{
charArray3[i++] = *(bytesToEncode++);

@@ -709,10 +720,10 @@ public:
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;

for (i=0; i<4; ++i)
for (i = 0; i < 4; ++i)
strBuf[strBufIndex++] = kBase64Chars[charArray4[i]];

if (strBufIndex >= kTmpBufSize-7)
if (strBufIndex >= strBufSize - 7)
{
strBuf[strBufIndex] = '\0';
strBufIndex = 0;
@@ -725,7 +736,7 @@ public:

if (i != 0)
{
for (j=i; j<3; ++j)
for (j = i; j < 3; ++j)
charArray3[j] = '\0';

charArray4[0] = (charArray3[0] & 0xfc) >> 2;
@@ -733,7 +744,7 @@ public:
charArray4[2] = ((charArray3[1] & 0x0f) << 2) + ((charArray3[2] & 0xc0) >> 6);
charArray4[3] = charArray3[2] & 0x3f;

for (j=0; j<4 && i<3 && j<i+1; ++j)
for (j = 0; j < 4 && i < 3 && j < i + 1; ++j)
strBuf[strBufIndex++] = kBase64Chars[charArray4[j]];

for (; i++ < 3;)
@@ -746,6 +757,7 @@ public:
ret += strBuf;
}

std::free(strBuf);
return ret;
}

@@ -764,7 +776,7 @@ public:

char* newbufptr = newbuf;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
const char c = fBuffer[i];

@@ -887,7 +899,7 @@ public:

char* newbufptr = newbuf;

for (std::size_t i=0; i < fBufferLen; ++i)
for (size_t i=0; i < fBufferLen; ++i)
{
const char c = fBuffer[i];

@@ -943,19 +955,16 @@ public:
return fBuffer;
}

char operator[](const std::size_t pos) const noexcept
char operator[](const size_t pos) const noexcept
{
if (pos < fBufferLen)
return fBuffer[pos];

d_safe_assert("pos < fBufferLen", __FILE__, __LINE__);

static char fallback;
fallback = '\0';
return fallback;
return '\0';
}

char& operator[](const std::size_t pos) noexcept
char& operator[](const size_t pos) noexcept
{
if (pos < fBufferLen)
return fBuffer[pos];
@@ -1006,7 +1015,7 @@ public:
if (strBuf == nullptr || strBuf[0] == '\0')
return *this;

const std::size_t strBufLen = std::strlen(strBuf);
const size_t strBufLen = std::strlen(strBuf);

// for empty strings, we can just take the appended string as our entire data
if (isEmpty())
@@ -1016,13 +1025,14 @@ public:
}

// we have some data ourselves, reallocate to add the new stuff
char* const newBuf = static_cast<char*>(std::realloc(fBuffer, fBufferLen + strBufLen + 1));
char* const newBuf = static_cast<char*>(std::realloc(fBufferAlloc ? fBuffer : nullptr, fBufferLen + strBufLen + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this);

std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1);

fBuffer = newBuf;
fBufferLen += strBufLen;
fBufferAlloc = true;

return *this;
}
@@ -1039,8 +1049,8 @@ public:
if (isEmpty())
return String(strBuf);

const std::size_t strBufLen = std::strlen(strBuf);
const std::size_t newBufSize = fBufferLen + strBufLen;
const size_t strBufLen = std::strlen(strBuf);
const size_t newBufSize = fBufferLen + strBufLen;
char* const newBuf = static_cast<char*>(std::malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

@@ -1065,7 +1075,7 @@ public:

private:
char* fBuffer; // the actual string buffer
std::size_t fBufferLen; // string length
size_t fBufferLen; // string length
bool fBufferAlloc; // wherever the buffer is allocated, not using _null()

/*
@@ -1086,7 +1096,7 @@ private:
* - Allocates string only if 'strBuf' is not null and new string contents are different
* - If 'strBuf' is null, 'size' must be 0
*/
void _dup(const char* const strBuf, const std::size_t size = 0) noexcept
void _dup(const char* const strBuf, const size_t size = 0) noexcept
{
if (strBuf != nullptr)
{
@@ -1143,9 +1153,9 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep
if (strBefore.isEmpty())
return String(strBufAfter);

const std::size_t strBeforeLen = strBefore.length();
const std::size_t strBufAfterLen = std::strlen(strBufAfter);
const std::size_t newBufSize = strBeforeLen + strBufAfterLen;
const size_t strBeforeLen = strBefore.length();
const size_t strBufAfterLen = std::strlen(strBufAfter);
const size_t newBufSize = strBeforeLen + strBufAfterLen;
char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());

@@ -1163,9 +1173,9 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep
if (strBufBefore == nullptr || strBufBefore[0] == '\0')
return strAfter;

const std::size_t strBufBeforeLen = std::strlen(strBufBefore);
const std::size_t strAfterLen = strAfter.length();
const std::size_t newBufSize = strBufBeforeLen + strAfterLen;
const size_t strBufBeforeLen = std::strlen(strBufBefore);
const size_t strAfterLen = strAfter.length();
const size_t newBufSize = strBufBeforeLen + strAfterLen;
char* const newBuf = static_cast<char*>(malloc(newBufSize + 1));
DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String());



+ 20
- 11
distrho/extra/WebViewImpl.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -99,14 +99,14 @@
#define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c)

#define WEB_VIEW_DELEGATE_CLASS_NAME \
MACRO_NAME(WebViewDelegate_, _, DISTRHO_NAMESPACE)
MACRO_NAME(WebViewDelegate_, _, WEB_VIEW_NAMESPACE)

@interface WEB_VIEW_DELEGATE_CLASS_NAME : NSObject<WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate>
@end

@implementation WEB_VIEW_DELEGATE_CLASS_NAME {
@public
WebViewMessageCallback callback;
WEB_VIEW_NAMESPACE::WebViewMessageCallback callback;
void* callbackPtr;
bool loaded;
}
@@ -242,6 +242,11 @@ START_NAMESPACE_DISTRHO

#if WEB_VIEW_USING_X11_IPC

#ifdef WEB_VIEW_DGL_NAMESPACE
using DISTRHO_NAMESPACE::ChildProcess;
using DISTRHO_NAMESPACE::RingBufferControl;
#endif

#ifdef __linux__
typedef int32_t ipc_sem_t;
#else
@@ -773,8 +778,10 @@ void webViewIdle(const WebViewHandle handle)

d_stderr("server ringbuffer data race, abort!");
handle->rbctrl2.flush();
return;
break;
}

std::free(buffer);
#else
// unused
(void)handle;
@@ -941,7 +948,7 @@ struct QSize {
S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \
DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false);

static void web_wake_idle(void* const ptr)
static int web_wake_idle(void* const ptr)
{
WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);

@@ -989,6 +996,7 @@ static void web_wake_idle(void* const ptr)
}

free(buffer);
return 0;
}

// -----------------------------------------------------------------------------------------------------------
@@ -1039,7 +1047,9 @@ static bool gtk3(Display* const display,
{
void* lib;
if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
(lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
(lib = dlopen("libwebkit2gtk-4.1.so.0", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
(lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr &&
(lib = dlopen("libwebkit2gtk-4.1.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr)
{
d_stdout("WebView gtk3 platform not available: %s", dlerror());
return false;
@@ -1135,10 +1145,8 @@ static bool gtk3(Display* const display,
GtkWidget* const window = gtk_plug_new(winId);
DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false);

gtk_window_set_default_size(GTK_WINDOW(window),
(width - x) * scaleFactor,
(height - y) * scaleFactor);
gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor);
gtk_window_set_default_size(GTK_WINDOW(window), width, height);
gtk_window_move(GTK_WINDOW(window), x, y);

WebKitSettings* const settings = webkit_settings_new();
DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false);
@@ -1774,7 +1782,7 @@ static bool qtwebengine(const int qtVersion,
QWebEnginePage_setWebChannel(&page, &channel, 0);

QWebEngineView_move(&webview, QPoint(x, y));
QWebEngineView_resize(&webview, QSize(static_cast<int>(width), static_cast<int>(height)));
QWebEngineView_resize(&webview, QSize(static_cast<int>(width / scaleFactor), static_cast<int>(height / scaleFactor)));
QWebEngineView_winId(&webview);
QWindow_setParent(QWebEngineView_windowHandle(&webview), QWindow_fromWinId(winId));

@@ -2061,3 +2069,4 @@ END_NAMESPACE_DISTRHO

#undef WEB_VIEW_DISTRHO_NAMESPACE
#undef WEB_VIEW_DGL_NAMESPACE
#undef WEB_VIEW_NAMESPACE

+ 11
- 2
distrho/extra/WebViewImpl.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,7 +18,7 @@
# error bad include
#endif

#if !defined(DGL_UI_USE_WEB_VIEW) && defined(DISTRHO_UI_WEB_VIEW) && DISTRHO_UI_WEB_VIEW == 0
#if !defined(DGL_USE_WEB_VIEW) && defined(DISTRHO_UI_WEB_VIEW) && DISTRHO_UI_WEB_VIEW == 0
# error To use WebViews in DPF plugins please set DISTRHO_UI_WEB_VIEW to 1
#endif

@@ -125,3 +125,12 @@ void webViewReload(WebViewHandle webview);
void webViewResize(WebViewHandle webview, uint width, uint height, double scaleFactor);

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

/**
Helper class for usage in std::shared_ptr and std::unique_ptr.
*/
struct WebViewDestroy {
void operator()(WebViewHandle handle) { webViewDestroy(handle); }
};

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

+ 26
- 5
distrho/extra/WebViewWin32.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -26,24 +26,41 @@ START_NAMESPACE_DISTRHO

class WebView;

WebView* webview_choc_create(const WebViewOptions& opts);
END_NAMESPACE_DISTRHO

#ifdef WEB_VIEW_DGL_NAMESPACE
START_NAMESPACE_DGL
using DISTRHO_NAMESPACE::WebView;
#else
START_NAMESPACE_DISTRHO
#endif

WebView* webview_choc_create(const WEB_VIEW_NAMESPACE::WebViewOptions& opts);
void webview_choc_destroy(WebView*);
void* webview_choc_handle(WebView*);
void webview_choc_eval(WebView*, const char* js);
void webview_choc_navigate(WebView*, const char* url);

#ifdef WEB_VIEW_DGL_NAMESPACE
END_NAMESPACE_DGL
#else
END_NAMESPACE_DISTRHO
#endif

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

#ifdef DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION

# define WC_ERR_INVALID_CHARS 0
# define WC_ERR_INVALID_CHARS 0x80
# include "choc/choc_WebView.h"

#ifdef WEB_VIEW_DGL_NAMESPACE
START_NAMESPACE_DGL
#else
START_NAMESPACE_DISTRHO
#endif

WebView* webview_choc_create(const WebViewOptions& opts)
WebView* webview_choc_create(const WEB_VIEW_NAMESPACE::WebViewOptions& opts)
{
WebView::Options wopts;
wopts.acceptsFirstMouseClick = true;
@@ -52,7 +69,7 @@ WebView* webview_choc_create(const WebViewOptions& opts)
std::unique_ptr<WebView> webview = std::make_unique<WebView>(wopts);
DISTRHO_SAFE_ASSERT_RETURN(webview->loadedOK(), nullptr);

if (const WebViewMessageCallback callback = opts.callback)
if (const WEB_VIEW_NAMESPACE::WebViewMessageCallback callback = opts.callback)
{
webview->addInitScript("function postMessage(m){window.chrome.webview.postMessage(m);}");

@@ -94,7 +111,11 @@ void webview_choc_navigate(WebView* const webview, const char* const url)
webview->navigate(url);
}

#ifdef WEB_VIEW_DGL_NAMESPACE
END_NAMESPACE_DGL
#else
END_NAMESPACE_DISTRHO
#endif

#endif // DISTRHO_WEB_VIEW_INCLUDE_IMPLEMENTATION



+ 184
- 86
distrho/src/DistrhoPluginAU.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -202,7 +202,8 @@ static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYP
static constexpr const uint32_t kSubType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID));
static constexpr const uint32_t kManufacturer = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID));

static constexpr const uint32_t kWantedAudioFormat = kAudioFormatFlagsNativeFloatPacked
static constexpr const uint32_t kWantedAudioFormat = 0
| kAudioFormatFlagsNativeFloatPacked
| kAudioFormatFlagIsNonInterleaved;


@@ -263,7 +264,7 @@ bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs
// --------------------------------------------------------------------------------------------------------------------

struct PropertyListener {
AudioUnitPropertyID prop;
AudioUnitPropertyID prop;
AudioUnitPropertyListenerProc proc;
void* userData;
};
@@ -276,12 +277,28 @@ struct RenderListener {
typedef std::vector<PropertyListener> PropertyListeners;
typedef std::vector<RenderListener> RenderListeners;

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

typedef struct {
UInt32 numPackets;
MIDIPacket packets[kMaxMidiEvents];
} d_MIDIPacketList;
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
// useful definitions
static constexpr const uint32_t kMIDIPacketNonDataSize = sizeof(MIDIPacket)
#if __cplusplus >= 201103L
- sizeof(MIDIPacket::data);
#else
- sizeof(static_cast<MIDIPacket*>(0)->data);
#endif
static constexpr const uint32_t kMIDIPacketListNonDataSize = sizeof(MIDIPacketList)
#if __cplusplus >= 201103L
- sizeof(MIDIPacketList::packet);
#else
- sizeof(static_cast<MIDIPacketList*>(0)->packet);
#endif

// size of data used for midi events
static constexpr const uint32_t kMIDIPacketListMaxDataSize = kMIDIPacketNonDataSize * kMaxMidiEvents
+ sizeof(Byte) * MidiEvent::kDataSize * kMaxMidiEvents;

// size of midi list + data
static constexpr const uint32_t kMIDIPacketListSize = kMIDIPacketListNonDataSize + kMIDIPacketListMaxDataSize;
#endif

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

@@ -331,10 +348,14 @@ public:
fUsingRenderListeners(false),
fParameterCount(fPlugin.getParameterCount()),
fLastParameterValues(nullptr),
fBypassParameterIndex(UINT32_MAX)
fBypassParameterIndex(UINT32_MAX),
fResetParameterIndex(UINT32_MAX)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
, fMidiEventCount(0)
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
, fMidiOutputDataOffset(0)
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
, fCurrentProgram(-1)
, fLastFactoryProgram(0)
@@ -345,7 +366,7 @@ public:
, fStateCount(fPlugin.getStateCount())
#endif
{
if (fParameterCount != 0)
if (fParameterCount != 0)
{
fLastParameterValues = new float[fParameterCount];
std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount);
@@ -354,8 +375,17 @@ public:
{
fLastParameterValues[i] = fPlugin.getParameterValue(i);

if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass)
switch (fPlugin.getParameterDesignation(i))
{
case kParameterDesignationNull:
break;
case kParameterDesignationBypass:
fBypassParameterIndex = i;
break;
case kParameterDesignationReset:
fResetParameterIndex = i;
break;
}
}
}

@@ -370,8 +400,10 @@ public:
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if ((fMidiOutputPackets = static_cast<MIDIPacketList*>(std::malloc(kMIDIPacketListSize))) != nullptr)
std::memset(fMidiOutputPackets, 0, kMIDIPacketListSize);

std::memset(&fMidiOutput, 0, sizeof(fMidiOutput));
std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets));
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -428,6 +460,10 @@ public:
reallocAudioBufferList(false);
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
std::free(fMidiOutputPackets);
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
for (uint32_t i=0; i<fProgramCount; ++i)
CFRelease(fFactoryPresetsData[i].presetName);
@@ -451,7 +487,8 @@ public:
fMidiEventCount = 0;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
fMidiOutputPackets.numPackets = 0;
fMidiOutputDataOffset = 0;
fMidiOutputPackets->numPackets = 0;
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
fTimePosition.clear();
@@ -635,7 +672,7 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
#if DISTRHO_PLUGIN_WANT_TIMEPOS
outDataSize = sizeof(HostCallbackInfo);
outWritable = false;
outWritable = true;
return noErr;
#else
return kAudioUnitErr_InvalidProperty;
@@ -659,6 +696,17 @@ public:
outWritable = true;
return noErr;

case kAudioUnitProperty_SupportsMPE:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
#if DISTRHO_PLUGIN_WANT_MIDI_AS_MPE
outDataSize = sizeof(UInt32);
outWritable = false;
return noErr;
#else
return kAudioUnitErr_InvalidProperty;
#endif

case kAudioUnitProperty_CocoaUI:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
@@ -708,7 +756,7 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(uint16_t);
outWritable = false;
outWritable = true;
return noErr;

case 'DPFe':
@@ -739,7 +787,7 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(uint32_t);
outWritable = false;
outWritable = true;
return noErr;
#endif

@@ -748,7 +796,7 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(CFArrayRef);
outWritable = false;
outWritable = true;
return noErr;

case 'DPFs':
@@ -764,7 +812,7 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
outDataSize = sizeof(void*);
outWritable = false;
outWritable = true;
return noErr;
#endif

@@ -885,13 +933,13 @@ public:
case kAudioUnitProperty_FastDispatch:
switch (inElement)
{
case kAudioUnitGetParameterSelect:
case kAudioUnitGetParameterSelect:
*static_cast<AudioUnitGetParameterProc*>(outData) = FastDispatchGetParameter;
return noErr;
case kAudioUnitSetParameterSelect:
case kAudioUnitSetParameterSelect:
*static_cast<AudioUnitSetParameterProc*>(outData) = FastDispatchSetParameter;
return noErr;
case kAudioUnitRenderSelect:
case kAudioUnitRenderSelect:
*static_cast<AudioUnitRenderProc*>(outData) = FastDispatchRender;
return noErr;
}
@@ -1028,6 +1076,12 @@ public:
}
return noErr;

#if DISTRHO_PLUGIN_WANT_MIDI_AS_MPE
case kAudioUnitProperty_SupportsMPE:
*static_cast<UInt32*>(outData) = 1;
return noErr;
#endif

#if DISTRHO_PLUGIN_HAS_UI
case kAudioUnitProperty_CocoaUI:
{
@@ -1099,6 +1153,8 @@ public:
const CFStringRef keyRef = CFStringCreateWithCString(nullptr,
fPlugin.getStateKey(i),
kCFStringEncodingASCII);
DISTRHO_SAFE_ASSERT_CONTINUE(keyRef != nullptr);

CFArrayAppendValue(keysRef, keyRef);
CFRelease(keyRef);
}
@@ -1414,7 +1470,7 @@ public:
const float value = bypass ? 1.f : 0.f;
fLastParameterValues[fBypassParameterIndex] = value;
fPlugin.setParameterValue(fBypassParameterIndex, value);
notifyPropertyListeners(inProp, inScope, inElement);
notifyPropertyListeners(inProp, inScope, inElement);
}
}
return noErr;
@@ -1436,12 +1492,12 @@ public:
#if DISTRHO_PLUGIN_WANT_TIMEPOS
{
const UInt32 usableDataSize = std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo)));
const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0;
const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0;

std::memcpy(&fHostCallbackInfo, inData, usableDataSize);
std::memcpy(&fHostCallbackInfo, inData, usableDataSize);

if (sizeof(HostCallbackInfo) > usableDataSize)
std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize);
std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize);

if (changed)
notifyPropertyListeners(inProp, inScope, inElement);
@@ -1568,7 +1624,7 @@ public:
AUEventListenerNotify(NULL, NULL, &event);

if (fBypassParameterIndex == inElement)
notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
}
return noErr;

@@ -1593,28 +1649,44 @@ public:

case 'DPFs':
DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFDictionaryRef), inDataSize, kAudioUnitErr_InvalidPropertyValue);
#if DISTRHO_PLUGIN_WANT_STATE
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement);
DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement);
{
const CFStringRef valueRef = *static_cast<const CFStringRef*>(inData);
const CFDictionaryRef dictRef = *static_cast<const CFDictionaryRef*>(inData);
DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(dictRef) == CFDictionaryGetTypeID(),
kAudioUnitErr_InvalidPropertyValue);
DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetCount(dictRef) == 1, kAudioUnitErr_InvalidPropertyValue);

CFStringRef keyRef = nullptr;
CFStringRef valueRef = nullptr;
CFDictionaryGetKeysAndValues(dictRef,
reinterpret_cast<const void**>(&keyRef),
reinterpret_cast<const void**>(&valueRef));
DISTRHO_SAFE_ASSERT_RETURN(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID(),
kAudioUnitErr_InvalidPropertyValue);
DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(),
kAudioUnitErr_InvalidPropertyValue);

const CFIndex valueLen = CFStringGetLength(valueRef);
char* const value = static_cast<char*>(std::malloc(valueLen + 1));
DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError);
DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8),
const CFIndex keyRefLen = CFStringGetLength(keyRef);
char* key = static_cast<char*>(std::malloc(keyRefLen + 1));
DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(keyRef, key, keyRefLen + 1, kCFStringEncodingASCII),
kAudioUnitErr_InvalidPropertyValue);

const String& key(fPlugin.getStateKey(inElement));
const CFIndex valueRefLen = CFStringGetLength(valueRef);
char* value = static_cast<char*>(std::malloc(valueRefLen + 1));
DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueRefLen + 1, kCFStringEncodingUTF8),
kAudioUnitErr_InvalidPropertyValue);

const String dkey(key);

// save this key as needed
if (fPlugin.wantStateKey(key))
fStateMap[key] = value;
if (fPlugin.wantStateKey(dkey))
fStateMap[dkey] = value;

fPlugin.setState(key, value);
fPlugin.setState(dkey, value);

std::free(key);
std::free(value);
}
return noErr;
@@ -1777,7 +1849,12 @@ public:
DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope);
DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement);

if (fPlugin.isActive())
if (fResetParameterIndex != UINT32_MAX)
{
fPlugin.setParameterValue(fResetParameterIndex, 1.f);
fPlugin.setParameterValue(fResetParameterIndex, 0.f);
}
else if (fPlugin.isActive())
{
fPlugin.deactivate();
fPlugin.activate();
@@ -1787,7 +1864,8 @@ public:
fMidiEventCount = 0;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
fMidiOutputPackets.numPackets = 0;
fMidiOutputDataOffset = 0;
fMidiOutputPackets->numPackets = 0;
#endif
#if DISTRHO_PLUGIN_WANT_TIMEPOS
fTimePosition.clear();
@@ -2015,7 +2093,7 @@ public:
midiEvent.data[1] = inData1;
midiEvent.data[2] = inData2;

switch (inStatus)
switch (inStatus & 0xF0)
{
case 0x80:
case 0x90:
@@ -2057,6 +2135,8 @@ public:
break;
default:
// invalid
d_debug("auMIDIEvent received invalid event %u %u %u %u @ %u",
inStatus, inData1, inData2, inOffsetSampleFrame, fMidiEventCount);
return kAudioUnitErr_InvalidPropertyValue;
}

@@ -2133,6 +2213,7 @@ private:
const uint32_t fParameterCount;
float* fLastParameterValues;
uint32_t fBypassParameterIndex;
uint32_t fResetParameterIndex;

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
uint32_t fMidiEventCount;
@@ -2141,8 +2222,9 @@ private:
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
uint32_t fMidiOutputDataOffset;
MIDIPacketList* fMidiOutputPackets;
AUMIDIOutputCallbackStruct fMidiOutput;
d_MIDIPacketList fMidiOutputPackets;
#endif

#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -2172,7 +2254,7 @@ private:
const PropertyListener& pl(*it);

if (pl.prop == prop)
pl.proc(pl.userData, fComponent, prop, scope, elem);
pl.proc(pl.userData, fComponent, prop, scope, elem);
}
}

@@ -2217,7 +2299,8 @@ private:
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
fMidiOutputPackets.numPackets = 0;
fMidiOutputDataOffset = 0;
fMidiOutputPackets->numPackets = 0;
#endif

#if DISTRHO_PLUGIN_WANT_TIMEPOS
@@ -2294,12 +2377,11 @@ private:
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr)
if (fMidiOutputPackets != nullptr &&
fMidiOutputPackets->numPackets != 0 &&
fMidiOutput.midiOutputCallback != nullptr)
{
fMidiOutput.midiOutputCallback(fMidiOutput.userData,
inTimeStamp,
0,
reinterpret_cast<const ::MIDIPacketList*>(&fMidiOutputPackets));
fMidiOutput.midiOutputCallback(fMidiOutput.userData, inTimeStamp, 0, fMidiOutputPackets);
}
#else
// unused
@@ -2497,12 +2579,12 @@ private:
CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII);
CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8);

if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
reinterpret_cast<const void**>(&keyRef),
reinterpret_cast<const void**>(&valueRef),
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks))
if (const CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
reinterpret_cast<const void**>(&keyRef),
reinterpret_cast<const void**>(&valueRef),
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks))
{
CFArrayAppendValue(statesRef, dictRef);
CFRelease(dictRef);
@@ -2716,17 +2798,21 @@ private:
bool writeMidi(const MidiEvent& midiEvent)
{
DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fMidiOutput.midiOutputCallback != nullptr, false);
DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("Out of memory", fMidiOutputPackets != nullptr, false);

if (midiEvent.size > sizeof(MIDIPacket::data))
return true;
if (fMidiOutputPackets.numPackets == kMaxMidiEvents)
if (fMidiOutputDataOffset + kMIDIPacketNonDataSize + midiEvent.size >= kMIDIPacketListMaxDataSize)
return false;

const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
MIDIPacket& packet(fMidiOutputPackets.packets[fMidiOutputPackets.numPackets++]);
packet.timeStamp = midiEvent.frame;
packet.length = midiEvent.size;
std::memcpy(packet.data, midiData, midiEvent.size);
MIDIPacket* const packet = reinterpret_cast<MIDIPacket*>(
reinterpret_cast<uint8_t*>(fMidiOutputPackets->packet) + fMidiOutputDataOffset);

packet->timeStamp = midiEvent.frame;
packet->length = midiEvent.size;
std::memcpy(packet->data, midiData, midiEvent.size);

++fMidiOutputPackets->numPackets;
fMidiOutputDataOffset += kMIDIPacketNonDataSize + midiEvent.size;
return true;
}

@@ -2793,7 +2879,7 @@ private:
// --------------------------------------------------------------------------------------------------------------------

struct AudioComponentPlugInInstance {
AudioComponentPlugInInterface acpi;
AudioComponentPlugInInterface acpi;
PluginAU* plugin;

AudioComponentPlugInInstance() noexcept
@@ -2802,9 +2888,9 @@ struct AudioComponentPlugInInstance {
{
std::memset(&acpi, 0, sizeof(acpi));
acpi.Open = Open;
acpi.Close = Close;
acpi.Lookup = Lookup;
acpi.reserved = nullptr;
acpi.Close = Close;
acpi.Lookup = Lookup;
acpi.reserved = nullptr;
}

~AudioComponentPlugInInstance()
@@ -2812,14 +2898,18 @@ struct AudioComponentPlugInInstance {
delete plugin;
}

static OSStatus Open(void* const self, const AudioUnit component)
static OSStatus Open(void* const self, const AudioUnit component)
{
d_debug("AudioComponentPlugInInstance::Open(%p)", self);

static_cast<AudioComponentPlugInInstance*>(self)->plugin = new PluginAU(component);
return noErr;
}

static OSStatus Close(void* const self)
static OSStatus Close(void* const self)
{
d_debug("AudioComponentPlugInInstance::Close(%p)", self);

delete static_cast<AudioComponentPlugInInstance*>(self);
return noErr;
}
@@ -2908,15 +2998,15 @@ struct AudioComponentPlugInInstance {
d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %d:%x:%s, %d:%s, %d, ...)",
self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);

UInt32 dataSize = 0;
Boolean writable = false;
UInt32 dataSize = 0;
Boolean writable = false;
const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable);

if (outDataSize != nullptr)
*outDataSize = dataSize;
if (outDataSize != nullptr)
*outDataSize = dataSize;

if (outWritable != nullptr)
*outWritable = writable;
if (outWritable != nullptr)
*outWritable = writable;

return res;
}
@@ -2928,8 +3018,16 @@ struct AudioComponentPlugInInstance {
void* const outData,
UInt32* const ioDataSize)
{
d_debug("AudioComponentPlugInInstance::GetProperty(%p, %d:%x:%s, %d:%s, %d, ...)",
self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
#ifdef DEBUG
switch (inProp) {
case kAudioUnitProperty_PresentPreset:
break;
default:
d_debug("AudioComponentPlugInInstance::GetProperty(%p, %d:%x:%s, %d:%s, %d, ...)",
self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement);
break;
}
#endif
DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError);

Boolean writable;
@@ -2952,24 +3050,24 @@ struct AudioComponentPlugInInstance {
if (res != noErr)
return res;

void* outBuffer;
void* outBuffer;
uint8_t* tmpBuffer;
if (inDataSize < outDataSize)
{
tmpBuffer = new uint8_t[outDataSize];
outBuffer = tmpBuffer;
}
{
tmpBuffer = new uint8_t[outDataSize];
outBuffer = tmpBuffer;
}
else
{
tmpBuffer = nullptr;
outBuffer = outData;
}
tmpBuffer = nullptr;
outBuffer = outData;
}

res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer);

if (res != noErr)
if (res != noErr)
{
*ioDataSize = 0;
*ioDataSize = 0;
return res;
}



+ 67
- 17
distrho/src/DistrhoPluginCLAP.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -286,7 +286,7 @@ public:
#else
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
fPlugin.getInstancePointer(), scaleFactor);
fPlugin.getInstancePointer(), scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
*width = tmpUI.getWidth();
*height = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();
@@ -306,13 +306,15 @@ public:
#if DISTRHO_UI_USER_RESIZABLE
if (UIExporter* const ui = fUI.get())
return ui->isResizable();
#endif
return true;
#else
return false;
#endif
}

bool getResizeHints(clap_gui_resize_hints_t* const hints) const
{
if (canResize())
if (fUI != nullptr && fUI->isResizable())
{
uint minimumWidth, minimumHeight;
bool keepAspectRatio;
@@ -344,7 +346,7 @@ public:

bool adjustSize(uint32_t* const width, uint32_t* const height) const
{
if (canResize())
if (fUI != nullptr && fUI->isResizable())
{
uint minimumWidth, minimumHeight;
bool keepAspectRatio;
@@ -582,7 +584,8 @@ private:
nullptr, // TODO fileRequestCallback,
d_nextBundlePath,
fPlugin.getInstancePointer(),
fScaleFactor);
fScaleFactor,
DGL_NAMESPACE::Application::kTypeClassic);

#if DISTRHO_PLUGIN_WANT_PROGRAMS
fUI->programLoaded(fCurrentProgram);
@@ -751,6 +754,7 @@ public:
updateStateValueCallback),
fHost(host),
fOutputEvents(nullptr),
fResetParameterIndex(UINT32_MAX),
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS != 0
fUsingCV(false),
#endif
@@ -763,7 +767,19 @@ public:
#endif
fHostExtensions(host)
{
fCachedParameters.setup(fPlugin.getParameterCount());
if (const uint32_t paramCount = fPlugin.getParameterCount())
{
fCachedParameters.setup(paramCount);

for (uint32_t i=0; i<paramCount; ++i)
{
if (fPlugin.getParameterDesignation(i) == kParameterDesignationReset)
{
fResetParameterIndex = i;
break;
}
}
}

#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT
fNotesRingBuffer.setRingBuffer(&fNotesBuffer, true);
@@ -821,6 +837,22 @@ public:
#endif
}

void reset()
{
if (fResetParameterIndex != UINT32_MAX)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fMidiEventCount = 0;
#endif
fPlugin.setParameterValue(fResetParameterIndex, 1.f);
fPlugin.setParameterValue(fResetParameterIndex, 0.f);
}
else
{
fHost->request_restart(fHost);
}
}

bool process(const clap_process_t* const process)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -1114,14 +1146,19 @@ public:
{
const ParameterRanges& ranges(fPlugin.getParameterRanges(index));

if (fPlugin.getParameterDesignation(index) == kParameterDesignationBypass)
switch (fPlugin.getParameterDesignation(index))
{
case kParameterDesignationBypass:
info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_BYPASS|CLAP_PARAM_IS_AUTOMATABLE;
std::strcpy(info->name, "Bypass");
std::strcpy(info->module, "dpf_bypass");
}
else
{
break;
case kParameterDesignationReset:
info->flags = CLAP_PARAM_IS_STEPPED|CLAP_PARAM_IS_READONLY;
std::strcpy(info->name, "Reset");
std::strcpy(info->module, "dpf_reset");
break;
default:
const uint32_t hints = fPlugin.getParameterHints(index);
const uint32_t groupId = fPlugin.getParameterGroupId(index);

@@ -1151,6 +1188,7 @@ public:
}

d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn);
break;
}

info->id = index;
@@ -1786,6 +1824,7 @@ private:
const clap_host_t* const fHost;
const clap_output_events_t* fOutputEvents;

uint32_t fResetParameterIndex;
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
const float* fAudioInputs[DISTRHO_PLUGIN_NUM_INPUTS];
#endif
@@ -2284,23 +2323,33 @@ static bool CLAP_ABI clap_plugin_note_ports_get(const clap_plugin_t*, uint32_t,
{
if (is_input)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
info->id = 0;
#if DISTRHO_PLUGIN_WANT_MIDI_AS_MPE
info->supported_dialects = CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_MIDI_MPE;
info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI_MPE;
#else
info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
#endif
std::strcpy(info->name, "Event/MIDI Input");
return true;
#endif
#endif
}
else
{
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
info->id = 0;
#if DISTRHO_PLUGIN_WANT_MIDI_AS_MPE
info->supported_dialects = CLAP_NOTE_DIALECT_MIDI | CLAP_NOTE_DIALECT_MIDI_MPE;
info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI_MPE;
#else
info->supported_dialects = CLAP_NOTE_DIALECT_MIDI;
info->preferred_dialect = CLAP_NOTE_DIALECT_MIDI;
#endif
std::strcpy(info->name, "Event/MIDI Output");
return true;
#endif
#endif
}

return false;
@@ -2440,9 +2489,10 @@ static void CLAP_ABI clap_plugin_stop_processing(const clap_plugin_t*)
// nothing to do
}

static void CLAP_ABI clap_plugin_reset(const clap_plugin_t*)
static void CLAP_ABI clap_plugin_reset(const clap_plugin_t* const plugin)
{
// nothing to do
PluginCLAP* const instance = static_cast<PluginCLAP*>(plugin->plugin_data);
instance->reset();
}

static clap_process_status CLAP_ABI clap_plugin_process(const clap_plugin_t* const plugin, const clap_process_t* const process)


+ 14
- 10
distrho/src/DistrhoPluginChecks.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -65,6 +65,10 @@
# define DISTRHO_PLUGIN_WANT_LATENCY 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_MIDI_AS_MPE
# define DISTRHO_PLUGIN_WANT_MIDI_AS_MPE 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0
#endif
@@ -178,6 +182,13 @@
# error Synths need audio output to work!
#endif

// --------------------------------------------------------------------------------------------------------------------
// Test if MIDI as MPE enabled where it doesn't make sense

#if DISTRHO_PLUGIN_WANT_MIDI_AS_MPE && ! (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT)
# error MIDI as MPE needs MIDI input or output to work!
#endif

// --------------------------------------------------------------------------------------------------------------------
// Enable MIDI input if synth, test if midi-input disabled when synth

@@ -275,15 +286,8 @@ static_assert(sizeof(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID)) == 5, "The macro DISTR
# error DISTRHO_UI_IS_STANDALONE must not be defined
#endif

#ifdef DPF_USING_LD_LINUX_WEBVIEW
# error DPF_USING_LD_LINUX_WEBVIEW must not be defined
#endif

// --------------------------------------------------------------------------------------------------------------------
// Set DPF_USING_LD_LINUX_WEBVIEW for internal use

#if DISTRHO_UI_WEB_VIEW && defined(DISTRHO_OS_LINUX)
# define DPF_USING_LD_LINUX_WEBVIEW
#ifdef DISTRHO_UI_LINUX_WEBVIEW_START
# error DISTRHO_UI_LINUX_WEBVIEW_START must not be defined
#endif

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


+ 78
- 65
distrho/src/DistrhoPluginInternal.hpp View File

@@ -338,6 +338,64 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
{
uint32_t j=0;
# if DISTRHO_PLUGIN_NUM_INPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++j)
fPlugin->initAudioPort(true, i, fData->audioPorts[j]);
# endif
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++j)
fPlugin->initAudioPort(false, i, fData->audioPorts[j]);
# endif
}
#endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0

for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
fPlugin->initParameter(i, fData->parameters[i]);

{
std::set<uint32_t> portGroupIndices;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
portGroupIndices.insert(fData->audioPorts[i].groupId);
#endif
for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
portGroupIndices.insert(fData->parameters[i].groupId);

portGroupIndices.erase(kPortGroupNone);

if (const uint32_t portGroupSize = static_cast<uint32_t>(portGroupIndices.size()))
{
fData->portGroups = new PortGroupWithId[portGroupSize];
fData->portGroupCount = portGroupSize;

uint32_t index = 0;
for (std::set<uint32_t>::iterator it = portGroupIndices.begin(); it != portGroupIndices.end(); ++it, ++index)
{
PortGroupWithId& portGroup(fData->portGroups[index]);
portGroup.groupId = *it;

if (portGroup.groupId < portGroupSize)
fPlugin->initPortGroup(portGroup.groupId, portGroup);
else
fillInPredefinedPortGroupData(portGroup.groupId, portGroup);
}
}
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
for (uint32_t i=0; i < fData->programCount; ++i)
fPlugin->initProgramName(i, fData->programNames[i]);
#endif

#if DISTRHO_PLUGIN_WANT_STATE
for (uint32_t i=0; i < fData->stateCount; ++i)
fPlugin->initState(i, fData->states[i]);
#endif

#if defined(DPF_RUNTIME_TESTING) && defined(__GNUC__) && !defined(__clang__)
/* Run-time testing build.
* Verify that virtual functions are overriden if parameters, programs or states are in use.
@@ -380,17 +438,30 @@ public:
# if DISTRHO_PLUGIN_WANT_STATE
if (fData->stateCount != 0)
{
if ((void*)(fPlugin->*(static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))) ==
(void*)static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))
bool hasNonUiState = false;
for (uint32_t i=0; i < fData->stateCount; ++i)
{
d_stderr2("DPF warning: Plugins with state must implement `initState`");
abort();
if ((fData->states[i].hints & kStateIsOnlyForUI) == 0)
{
hasNonUiState = true;
break;
}
}

if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState)
if (hasNonUiState)
{
d_stderr2("DPF warning: Plugins with state must implement `setState`");
abort();
if ((void*)(fPlugin->*(static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))) ==
(void*)static_cast<void(Plugin::*)(uint32_t,State&)>(&Plugin::initState))
{
d_stderr2("DPF warning: Plugins with state must implement `initState`");
abort();
}

if ((void*)(fPlugin->*(&Plugin::setState)) == (void*)&Plugin::setState)
{
d_stderr2("DPF warning: Plugins with state must implement `setState`");
abort();
}
}
}
# endif
@@ -412,64 +483,6 @@ public:
# endif
#endif

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
{
uint32_t j=0;
# if DISTRHO_PLUGIN_NUM_INPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++j)
fPlugin->initAudioPort(true, i, fData->audioPorts[j]);
# endif
# if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++j)
fPlugin->initAudioPort(false, i, fData->audioPorts[j]);
# endif
}
#endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0

for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
fPlugin->initParameter(i, fData->parameters[i]);

{
std::set<uint32_t> portGroupIndices;

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
portGroupIndices.insert(fData->audioPorts[i].groupId);
#endif
for (uint32_t i=0, count=fData->parameterCount; i < count; ++i)
portGroupIndices.insert(fData->parameters[i].groupId);

portGroupIndices.erase(kPortGroupNone);

if (const uint32_t portGroupSize = static_cast<uint32_t>(portGroupIndices.size()))
{
fData->portGroups = new PortGroupWithId[portGroupSize];
fData->portGroupCount = portGroupSize;

uint32_t index = 0;
for (std::set<uint32_t>::iterator it = portGroupIndices.begin(); it != portGroupIndices.end(); ++it, ++index)
{
PortGroupWithId& portGroup(fData->portGroups[index]);
portGroup.groupId = *it;

if (portGroup.groupId < portGroupSize)
fPlugin->initPortGroup(portGroup.groupId, portGroup);
else
fillInPredefinedPortGroupData(portGroup.groupId, portGroup);
}
}
}

#if DISTRHO_PLUGIN_WANT_PROGRAMS
for (uint32_t i=0, count=fData->programCount; i < count; ++i)
fPlugin->initProgramName(i, fData->programNames[i]);
#endif

#if DISTRHO_PLUGIN_WANT_STATE
for (uint32_t i=0, count=fData->stateCount; i < count; ++i)
fPlugin->initState(i, fData->states[i]);
#endif

fData->callbacksPtr = callbacksPtr;
fData->writeMidiCallbackFunc = writeMidiCall;
fData->requestParameterValueChangeCallbackFunc = requestParameterValueChangeCall;


+ 4
- 8
distrho/src/DistrhoPluginJACK.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -88,10 +88,6 @@ static const writeMidiFunc writeMidiCallback = nullptr;
static const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
#endif

#ifdef DPF_USING_LD_LINUX_WEBVIEW
int dpf_webview_start(int argc, char* argv[]);
#endif

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

static volatile bool gCloseSignalReceived = false;
@@ -556,7 +552,7 @@ protected:
midiData[1] = note;
midiData[2] = velocity;
fNotesRingBuffer.writeCustomData(midiData, 3);
fNotesRingBuffer.commitWrite();
fNotesRingBuffer.commitWrite("PluginJack::sendNote");
}
# endif

@@ -1003,9 +999,9 @@ int main(int argc, char* argv[])
}
#endif

#ifdef DPF_USING_LD_LINUX_WEBVIEW
#if defined(DISTRHO_UI_LINUX_WEBVIEW_START)
if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0)
return dpf_webview_start(argc, argv);
return DISTRHO_NAMESPACE::dpf_webview_start(argc, argv);
#endif

if (argc == 2 && std::strcmp(argv[1], "selftest") == 0)


+ 40
- 9
distrho/src/DistrhoPluginLV2export.cpp View File

@@ -66,6 +66,10 @@
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048
#endif

#ifndef DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
# define DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#endif

#ifndef DISTRHO_PLUGIN_USES_MODGUI
# define DISTRHO_PLUGIN_USES_MODGUI 0
#endif
@@ -260,14 +264,14 @@ void lv2_generate_ttl(const char* const basename)
const String pluginDLL(basename);
const String pluginTTL(pluginDLL + ".ttl");

#if DISTRHO_PLUGIN_HAS_UI
#if DISTRHO_PLUGIN_HAS_UI
String pluginUI(pluginDLL);
# if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if ! DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
pluginUI.truncate(pluginDLL.rfind("_dsp"));
pluginUI += "_ui";
const String uiTTL(pluginUI + ".ttl");
# endif
#endif
#endif
#endif

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

@@ -278,7 +282,7 @@ void lv2_generate_ttl(const char* const basename)
String manifestString;
manifestString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
manifestString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n";
#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
manifestString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n";
#endif
#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -304,14 +308,14 @@ void lv2_generate_ttl(const char* const basename)
manifestString += "<" DISTRHO_UI_URI ">\n";
manifestString += " a ui:" DISTRHO_LV2_UI_TYPE " ;\n";
manifestString += " ui:binary <" + pluginUI + "." DISTRHO_DLL_EXTENSION "> ;\n";
# if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# if DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
addAttribute(manifestString, "lv2:extensionData", lv2ManifestUiExtensionData, 4);
addAttribute(manifestString, "lv2:optionalFeature", lv2ManifestUiOptionalFeatures, 4);
addAttribute(manifestString, "lv2:requiredFeature", lv2ManifestUiRequiredFeatures, 4);
addAttribute(manifestString, "opts:supportedOption", lv2ManifestUiSupportedOptions, 4, true);
# else // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# else // DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
manifestString += " rdfs:seeAlso <" + uiTTL + "> .\n";
# endif // DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# endif // DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
manifestString += "\n";
#endif

@@ -363,6 +367,7 @@ void lv2_generate_ttl(const char* const basename)
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n";
#endif
pluginString += "@prefix dg: <http://www.darkglass.com/lv2/ns#> .\n";
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n";
pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n";
pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n";
@@ -758,6 +763,16 @@ void lv2_generate_ttl(const char* const basename)
pluginString += " lv2:portProperty lv2:toggled , lv2:integer ;\n";
pluginString += " lv2:designation lv2:enabled ;\n";
break;
case kParameterDesignationReset:
designated = true;
pluginString += " lv2:name \"Reset\" ;\n";
pluginString += " lv2:symbol \"" + String(ParameterDesignationSymbols::reset) + "\" ;\n";
pluginString += " lv2:default 0 ;\n";
pluginString += " lv2:minimum 0 ;\n";
pluginString += " lv2:maximum 1 ;\n";
pluginString += " lv2:portProperty lv2:toggled , lv2:integer , <" LV2_PORT_PROPS__trigger "> ;\n";
pluginString += " lv2:designation <" LV2_KXSTUDIO_PROPERTIES__Reset "> ;\n";
break;
}
}

@@ -983,6 +998,22 @@ void lv2_generate_ttl(const char* const basename)
}
}

// Darkglass Anagram
#ifdef DISTRHO_PLUGIN_ABBREVIATION
pluginString += " dg:abbreviation \"" DISTRHO_PLUGIN_ABBREVIATION "\" ;\n";
#endif
#ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF
pluginString += " dg:blockImageOff <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF "> ;\n";
#endif
#ifdef DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON
pluginString += " dg:blockImageOn <" DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON "> ;\n";
#endif
#if defined(DISTRHO_PLUGIN_ABBREVIATION) || \
defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_OFF) || \
defined(DISTRHO_PLUGIN_ANAGRAM_BLOCK_IMAGE_ON)
pluginString += "\n";
#endif

#ifdef DISTRHO_PLUGIN_BRAND
// MOD
pluginString += " mod:brand \"" DISTRHO_PLUGIN_BRAND "\" ;\n";
@@ -1554,7 +1585,7 @@ void lv2_generate_ttl(const char* const basename)

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

#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_AND_UI_IN_SINGLE_OBJECT
{
std::cout << "Writing " << uiTTL << "..."; std::cout.flush();
std::fstream uiFile(uiTTL, std::ios::out);


+ 158
- 0
distrho/src/DistrhoPluginMAPI.cpp View File

@@ -0,0 +1,158 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2025 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.
*/

#include "DistrhoPluginInternal.hpp"

#ifndef DISTRHO_NO_WARNINGS
# if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
# error Cannot use parameter value change request with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
# error Cannot use MIDI with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_FULL_STATE
# error Cannot use full state with MAPI
# endif
# if DISTRHO_PLUGIN_WANT_TIMEPOS
# error Cannot use time position with MAPI
# endif
#endif

#include "mapi/mapi.h"

START_NAMESPACE_DISTRHO

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

class PluginMAPI
{
public:
PluginMAPI()
: fPlugin(nullptr, nullptr, nullptr, nullptr)
{
fPlugin.activate();
}

~PluginMAPI() noexcept
{
fPlugin.deactivate();
}

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

void process(const float* const* ins, float** outs, unsigned int frames)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
fPlugin.run(const_cast<const float**>(ins), outs, frames, nullptr, 0);
#else
fPlugin.run(const_cast<const float**>(ins), outs, frames);
#endif

updateParameterOutputsAndTriggers();
}

void setParameter(unsigned int index, float value)
{
fPlugin.setParameterValue(index, fPlugin.getParameterRanges(index).getFixedValue(value));
}

void setState(const char* key, const char* value)
{
#if DISTRHO_PLUGIN_WANT_STATE
fPlugin.setState(key, value);
#else
// unused
(void)key;
(void)value;
#endif
}

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

private:
PluginExporter fPlugin;

void updateParameterOutputsAndTriggers()
{
float value;

for (uint32_t i = 0, count = fPlugin.getParameterCount(); i < count; ++i)
{
if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
{
// NOTE: no trigger support in MAPI, simulate it here
value = fPlugin.getParameterRanges(i).def;

if (d_isEqual(value, fPlugin.getParameterValue(i)))
continue;

fPlugin.setParameterValue(i, value);
}
}
}
};

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

MAPI_EXPORT
mapi_handle_t mapi_create(unsigned int sample_rate)
{
if (d_nextBufferSize == 0)
{
#if defined(_DARKGLASS_DEVICE_PABLITO)
d_nextBufferSize = 16;
#elif defined(__MOD_DEVICES__)
d_nextBufferSize = 128;
#else
d_nextBufferSize = 2048;
#endif
}

d_nextSampleRate = sample_rate;

return new PluginMAPI();
}

MAPI_EXPORT
void mapi_process(mapi_handle_t handle,
const float* const* ins,
float** outs,
unsigned int frames)
{
static_cast<PluginMAPI*>(handle)->process(ins, outs, frames);
}

MAPI_EXPORT
void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value)
{
static_cast<PluginMAPI*>(handle)->setParameter(index, value);
}

MAPI_EXPORT
void mapi_set_state(mapi_handle_t handle, const char* key, const char* value)
{
static_cast<PluginMAPI*>(handle)->setState(key, value);
}

MAPI_EXPORT
void mapi_destroy(mapi_handle_t handle)
{
delete static_cast<PluginMAPI*>(handle);
}

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

END_NAMESPACE_DISTRHO

+ 6
- 5
distrho/src/DistrhoPluginVST2.cpp View File

@@ -175,7 +175,8 @@ public:
nullptr, // TODO file request
d_nextBundlePath,
plugin->getInstancePointer(),
scaleFactor),
scaleFactor,
DGL_NAMESPACE::Application::kTypeClassic),
fKeyboardModifiers(0)
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
, fNotesRingBuffer()
@@ -398,8 +399,8 @@ public:
{
parameterValues = new float[parameterCount];

for (uint32_t i=0; i < parameterCount; ++i)
parameterValues[i] = NAN;
for (uint32_t i = 0; i < parameterCount; ++i)
parameterValues[i] = fPlugin.getParameterDefault(i);
}

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -600,7 +601,7 @@ public:
#else
UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(),
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
fPlugin.getInstancePointer(), scaleFactor);
fPlugin.getInstancePointer(), scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
fVstRect.right = tmpUI.getWidth();
fVstRect.bottom = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();
@@ -1709,7 +1710,7 @@ const vst_effect* VSTPluginMain(const vst_host_callback audioMaster)
return effect;
}

#if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_UI_WEB_VIEW)
#if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || (defined(DISTRHO_OS_LINUX) && defined(DGL_USE_WEB_VIEW)))
DISTRHO_PLUGIN_EXPORT
const vst_effect* VSTPluginMainCompat(vst_host_callback) asm ("main");



+ 6
- 0
distrho/src/DistrhoPluginVST3.cpp View File

@@ -1747,6 +1747,12 @@ public:
case kParameterDesignationBypass:
flags |= V3_PARAM_IS_BYPASS;
break;
case kParameterDesignationReset:
info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
info->step_count = 1;
strncpy_utf16(info->title, "Reset", 128);
strncpy_utf16(info->short_title, "Reset", 128);
return V3_OK;
}

if (hints & kParameterIsOutput)


+ 19
- 8
distrho/src/DistrhoUIAU.mm View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -125,6 +125,7 @@ public:

CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes);

// setup property listeners
AudioUnitAddPropertyListener(fComponent, kAudioUnitProperty_SampleRate, auPropertyChangedCallback, this);
AudioUnitAddPropertyListener(fComponent, 'DPFp', auPropertyChangedCallback, this);
#if DISTRHO_PLUGIN_WANT_PROGRAMS
@@ -322,15 +323,22 @@ private:
#if DISTRHO_PLUGIN_WANT_STATE
void setState(const char* const key, const char* const value)
{
const std::vector<String>::iterator it = std::find(fStateKeys.begin(), fStateKeys.end(), key);
DISTRHO_SAFE_ASSERT_RETURN(it != fStateKeys.end(),);

if (const CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8))
CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII);
CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8);

if (const CFDictionaryRef dictRef = CFDictionaryCreate(nullptr,
reinterpret_cast<const void**>(&keyRef),
reinterpret_cast<const void**>(&valueRef),
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks))
{
const uint32_t index = it - fStateKeys.begin();
AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, index, &valueRef, sizeof(CFStringRef));
CFRelease(valueRef);
AudioUnitSetProperty(fComponent, 'DPFs', kAudioUnitScope_Global, 0, &dictRef, sizeof(dictRef));
CFRelease(dictRef);
}

CFRelease(keyRef);
CFRelease(valueRef);
}

static void setStateCallback(void* const ptr, const char* const key, const char* const value)
@@ -387,6 +395,9 @@ END_NAMESPACE_DISTRHO
#define COCOA_VIEW_CLASS_NAME \
MACRO_NAME(CocoaView_, DISTRHO_PLUGIN_AU_TYPE, _, DISTRHO_PLUGIN_UNIQUE_ID, _, DISTRHO_PLUGIN_BRAND_ID)

using DISTRHO_NAMESPACE::DPF_UI_AU;
using DISTRHO_NAMESPACE::d_nextSampleRate;

@interface COCOA_VIEW_CLASS_NAME : NSView
{
@public


+ 3
- 7
distrho/src/DistrhoUIDSSI.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -38,10 +38,6 @@ static constexpr const setSizeFunc setSizeCallback = nullptr;
// unsupported in DSSI
static constexpr const fileRequestFunc fileRequestCallback = nullptr;

#ifdef DPF_USING_LD_LINUX_WEBVIEW
int dpf_webview_start(int argc, char* argv[]);
#endif

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


@@ -391,9 +387,9 @@ int main(int argc, char* argv[])
{
USE_NAMESPACE_DISTRHO

#ifdef DPF_USING_LD_LINUX_WEBVIEW
#if defined(DISTRHO_UI_LINUX_WEBVIEW_START)
if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0)
return dpf_webview_start(argc - 1, argv + 1);
return DISTRHO_NAMESPACE::dpf_webview_start(argc, argv);
#endif

// dummy test mode


+ 8
- 7
distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,18 +21,18 @@

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI exporter class

class UIExporter
{
// -------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------------
// UI Widget and its private data

UI* ui;
UI::PrivateData* uiData;

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

public:
UIExporter(void* const callbacksPtr,
@@ -47,11 +47,12 @@ public:
const char* const bundlePath = nullptr,
void* const dspPtr = nullptr,
const double scaleFactor = 0.0,
const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeAuto,
const uint32_t bgColor = 0,
const uint32_t fgColor = 0xffffffff,
const char* const appClassName = nullptr)
: ui(nullptr),
uiData(new UI::PrivateData(appClassName))
uiData(new UI::PrivateData(appClassName, appType))
{
uiData->sampleRate = sampleRate;
uiData->bundlePath = bundlePath != nullptr ? strdup(bundlePath) : nullptr;
@@ -267,12 +268,12 @@ public:
uiData->app.repaintIfNeeeded();
}

void addIdleCallbackForNativeIdle(IdleCallback* const cb, const uint timerFrequencyInMs)
void addIdleCallbackForNativeIdle(DGL_NAMESPACE::IdleCallback* const cb, const uint timerFrequencyInMs)
{
uiData->window->addIdleCallback(cb, timerFrequencyInMs);
}

void removeIdleCallbackForNativeIdle(IdleCallback* const cb)
void removeIdleCallbackForNativeIdle(DGL_NAMESPACE::IdleCallback* const cb)
{
uiData->window->removeIdleCallback(cb);
}


+ 16
- 12
distrho/src/DistrhoUILV2.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -52,7 +52,7 @@ static constexpr const sendNoteFunc sendNoteCallback = nullptr;
// unwanted in LV2, resize extension is deprecated and hosts can do it without extensions
static constexpr const setSizeFunc setSizeCallback = nullptr;

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

template <class LV2F>
static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
@@ -80,6 +80,7 @@ public:
void* const dspPtr,
const float sampleRate,
const float scaleFactor,
const DGL_NAMESPACE::Application::Type appType,
const uint32_t bgColor,
const uint32_t fgColor,
const char* const appClassName)
@@ -101,7 +102,7 @@ public:
sendNoteCallback,
setSizeCallback,
fileRequestCallback,
bundlePath, dspPtr, scaleFactor, bgColor, fgColor, appClassName)
bundlePath, dspPtr, scaleFactor, appType, bgColor, fgColor, appClassName)
{
if (widget != nullptr)
*widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
@@ -483,7 +484,7 @@ private:
}
};

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

static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
const char* const uri,
@@ -499,6 +500,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
return nullptr;
}

// TODO allow classic vs modern ui type
static constexpr const DGL_NAMESPACE::Application::Type appType = DGL_NAMESPACE::Application::kTypeClassic;

const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;
void* parentId = nullptr;
@@ -633,7 +637,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, appClassName);
sampleRate, scaleFactor, appType, bgColor, fgColor, appClassName);
}

#define uiPtr ((UiLv2*)ui)
@@ -648,7 +652,7 @@ static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t buffe
uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
}

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

static int lv2ui_idle(LV2UI_Handle ui)
{
@@ -665,7 +669,7 @@ static int lv2ui_hide(LV2UI_Handle ui)
return uiPtr->lv2ui_hide();
}

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

static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
{
@@ -677,7 +681,7 @@ static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* optio
return uiPtr->lv2_set_options(options);
}

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

#if DISTRHO_PLUGIN_WANT_PROGRAMS
static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
@@ -686,7 +690,7 @@ static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t progra
}
#endif

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

static const void* lv2ui_extension_data(const char* uri)
{
@@ -713,7 +717,7 @@ static const void* lv2ui_extension_data(const char* uri)

#undef instancePtr

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

static const LV2UI_Descriptor sLv2UiDescriptor = {
DISTRHO_UI_URI,
@@ -723,7 +727,7 @@ static const LV2UI_Descriptor sLv2UiDescriptor = {
lv2ui_extension_data
};

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

END_NAMESPACE_DISTRHO

@@ -929,4 +933,4 @@ void modgui_cleanup(const LV2UI_Handle handle)
}
#endif

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

+ 21
- 12
distrho/src/DistrhoUIPrivateData.hpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -61,14 +61,20 @@

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
/* define webview start */
#if defined(HAVE_X11) && defined(DISTRHO_OS_LINUX) && DISTRHO_UI_WEB_VIEW
# define DISTRHO_UI_LINUX_WEBVIEW_START
int dpf_webview_start(int argc, char* argv[]);
#endif

// --------------------------------------------------------------------------------------------------------------------
// Plugin Application, will set class name based on plugin details

class PluginApplication : public DGL_NAMESPACE::Application
{
public:
explicit PluginApplication(const char* className)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE)
explicit PluginApplication(const char* className, const Application::Type type)
: DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE, type)
{
#if defined(__MOD_DEVICES__) || !defined(__EMSCRIPTEN__)
if (className == nullptr)
@@ -102,7 +108,7 @@ public:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// Plugin Window, will pass some Window events to UI

class PluginWindow : public DGL_NAMESPACE::Window
@@ -132,7 +138,10 @@ public:

// this is called just before creating UI, ensuring proper context to it
if (pData->initPost())
{
puglBackendEnter(pData->view);
pData->createContextIfNeeded();
}
}

~PluginWindow() override
@@ -234,7 +243,7 @@ protected:
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI callbacks

typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
@@ -244,7 +253,7 @@ typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8
typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
typedef bool (*fileRequestFunc) (void* ptr, const char* key);

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI private data

struct UI::PrivateData {
@@ -283,8 +292,8 @@ struct UI::PrivateData {
setSizeFunc setSizeCallbackFunc;
fileRequestFunc fileRequestCallbackFunc;

PrivateData(const char* const appClassName) noexcept
: app(appClassName),
PrivateData(const char* const appClassName, const DGL_NAMESPACE::Application::Type appType) noexcept
: app(appClassName, appType),
window(nullptr),
#if DISTRHO_UI_USE_WEB_VIEW
webview(nullptr),
@@ -383,7 +392,7 @@ struct UI::PrivateData {
#endif
};

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// UI private data fileRequestCallback, which requires PluginWindow definitions

inline bool UI::PrivateData::fileRequestCallback(const char* const key)
@@ -410,7 +419,7 @@ inline bool UI::PrivateData::fileRequestCallback(const char* const key)
return false;
}

// -----------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// PluginWindow onFileSelected that require UI::PrivateData definitions

#if DISTRHO_UI_FILE_BROWSER
@@ -448,7 +457,7 @@ inline void PluginWindow::onFileSelected(const char* const filename)
}
#endif

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

END_NAMESPACE_DISTRHO



+ 4
- 3
distrho/src/DistrhoUIVST3.cpp View File

@@ -129,7 +129,7 @@ static uint translateVST3Modifiers(const int64_t modifiers) noexcept
* Helper class for getting a native idle timer.
*/
#if !DPF_VST3_USING_HOST_RUN_LOOP
class NativeIdleCallback : public IdleCallback
class NativeIdleCallback : public DGL_NAMESPACE::IdleCallback
{
public:
NativeIdleCallback(UIExporter& ui)
@@ -205,7 +205,8 @@ public:
nullptr, // TODO file request
d_nextBundlePath,
instancePointer,
scaleFactor)
scaleFactor,
DGL_NAMESPACE::Application::kTypeClassic)
{
}

@@ -1374,7 +1375,7 @@ struct dpf_plugin_view : v3_plugin_view_cpp {
#else
UIExporter tmpUI(nullptr, 0, view->sampleRate,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
view->instancePointer, scaleFactor);
view->instancePointer, scaleFactor, DGL_NAMESPACE::Application::kTypeClassic);
rect->right = tmpUI.getWidth();
rect->bottom = tmpUI.getHeight();
scaleFactor = tmpUI.getScaleFactor();


+ 241
- 6
distrho/src/DistrhoUtils.cpp View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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
@@ -23,13 +23,19 @@
#include "../DistrhoStandaloneUtils.hpp"

#ifdef DISTRHO_OS_WINDOWS
# include <direct.h>
# include <shlobj.h>
# include <windows.h>
#else
# ifndef STATIC_BUILD
# include <dlfcn.h>
# endif
# include <fcntl.h>
# include <limits.h>
# include <pwd.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <unistd.h>
#endif

#ifdef DISTRHO_OS_WINDOWS
@@ -109,6 +115,240 @@ const char* getPluginFormatName() noexcept
#endif
}

#ifndef DISTRHO_OS_WINDOWS
static inline
void _createDirIfNeeded(const char* const dir)
{
try {
if (access(dir, F_OK) != 0)
mkdir(dir, 0755);
} DISTRHO_SAFE_EXCEPTION("createDirIfNeeded");
}
#endif

static const char* _getDocumentsDir();
static const char* _getDocumentsDirForPlugin();
static const char* _getHomeDir();

static const char* _getConfigDir()
{
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS)
return _getDocumentsDir();
#else
static String dir;

if (dir.isEmpty())
{
if (const char* const xdgEnv = getenv("XDG_CONFIG_HOME"))
{
dir = xdgEnv;

if (dir.isNotEmpty() && ! dir.endsWith('/'))
dir += "/";
}

if (dir.isEmpty())
{
dir = _getHomeDir();
dir += ".config/";
}

_createDirIfNeeded(dir);
}

return dir;
#endif
}

static const char* _getConfigDirForPlugin()
{
#if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS)
return _getDocumentsDirForPlugin();
#else
static String dir;

if (dir.isEmpty())
{
dir = _getConfigDir();
dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR;
_createDirIfNeeded(dir);
}

return dir;
#endif
}

static const char* _getDocumentsDir()
{
static String dir;

if (dir.isEmpty())
{
#if defined(DISTRHO_OS_MAC)
dir = _getHomeDir();
dir += "Documents/";
#elif defined(DISTRHO_OS_WASM)
dir = _getHomeDir();
#elif defined(DISTRHO_OS_WINDOWS)
WCHAR wpath[MAX_PATH];
if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK)
{
CHAR apath[MAX_PATH];
if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0)
dir = apath;
}
#else
String xdgDirsConfigPath(_getConfigDir());
xdgDirsConfigPath += "user-dirs.dirs";

if (FILE* const f = std::fopen(xdgDirsConfigPath, "r"))
{
std::fseek(f, 0, SEEK_END);
const long size = std::ftell(f);
std::fseek(f, 0, SEEK_SET);

// something is wrong if config dirs file is longer than 1MiB!
if (size > 0 && size < 1024 * 1024)
{
if (char* filedata = static_cast<char*>(std::malloc(size)))
{
for (long r = 0, total = 0; total < size;)
{
r = std::fread(filedata + total, 1, size - total, f);

if (r == 0)
{
std::free(filedata);
filedata = nullptr;
break;
}

total += r;
}

if (filedata != nullptr)
{
if (char* const xdgDocsDir = std::strstr(filedata, "XDG_DOCUMENTS_DIR=\""))
{
if (char* const xdgDocsDirNL = std::strstr(xdgDocsDir, "\"\n"))
{
*xdgDocsDirNL = '\0';
String sdir(xdgDocsDir + 19);

if (sdir.startsWith("$HOME"))
{
dir = _getHomeDir();
dir += sdir.buffer() + 6;
}
else
{
dir = sdir;
}
}
}

std::free(filedata);
}
}
}

std::fclose(f);
}

// ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data
if (dir.isEmpty())
{
dir = _getHomeDir();
dir += "Documents/";
}

_createDirIfNeeded(dir);
#endif

if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP))
dir += DISTRHO_OS_SEP_STR;
}

return dir;
}

static const char* _getDocumentsDirForPlugin()
{
static String dir;

if (dir.isEmpty())
{
#ifdef DISTRHO_OS_WINDOWS
WCHAR wpath[MAX_PATH];
if (SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK)
{
wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\");
_wmkdir(wpath);

CHAR apath[MAX_PATH];
if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0)
dir = apath;
}
#else
dir = _getDocumentsDir();
dir += DISTRHO_PLUGIN_NAME DISTRHO_OS_SEP_STR;
_createDirIfNeeded(dir);
#endif
}

return dir;
}

static const char* _getHomeDir()
{
static String dir;

if (dir.isEmpty())
{
#ifdef DISTRHO_OS_WINDOWS
WCHAR wpath[MAX_PATH];
if (SHGetFolderPathW(nullptr, CSIDL_PROFILE, nullptr, SHGFP_TYPE_CURRENT, wpath) == S_OK)
{
CHAR apath[MAX_PATH];
if (WideCharToMultiByte(CP_UTF8, 0, wpath, -1, apath, MAX_PATH, nullptr, nullptr) != 0)
dir = apath;
}
#else
if (const char* const homeEnv = getenv("HOME"))
dir = homeEnv;

if (dir.isEmpty())
if (struct passwd* const pwd = getpwuid(getuid()))
dir = pwd->pw_dir;

_createDirIfNeeded(dir);
#endif

if (dir.isNotEmpty() && ! dir.endsWith(DISTRHO_OS_SEP))
dir += DISTRHO_OS_SEP_STR;
}

return dir;
}

const char* getSpecialDir(const SpecialDir dir)
{
switch (dir)
{
case kSpecialDirHome:
return _getHomeDir();
case kSpecialDirConfig:
return _getConfigDir();
case kSpecialDirConfigForPlugin:
return _getConfigDirForPlugin();
case kSpecialDirDocuments:
return _getDocumentsDir();
case kSpecialDirDocumentsForPlugin:
return _getDocumentsDirForPlugin();
}
return nullptr;
}

const char* getResourcePath(const char* const bundlePath) noexcept
{
DISTRHO_SAFE_ASSERT_RETURN(bundlePath != nullptr, nullptr);
@@ -166,11 +406,6 @@ bool requestBufferSizeChange(uint) { return false; }
bool requestMIDI() { return false; }
#endif

/* define webview start */
#ifdef DPF_USING_LD_LINUX_WEBVIEW
int dpf_webview_start(int argc, char* argv[]);
#endif

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

END_NAMESPACE_DISTRHO

+ 28
- 17
distrho/src/jackbridge/JackBridge.cpp View File

@@ -1,6 +1,6 @@
/*
* JackBridge for DPF
* Copyright (C) 2013-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2013-2025 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
@@ -68,6 +68,9 @@ typedef void* lib_t;
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# pragma clang diagnostic ignored "-Wunused-but-set-variable"
# if __clang_major__ >= 17
# pragma clang diagnostic ignored "-Wvla-cxx-extension"
# endif
# endif
# include "RtAudioBridge.hpp"
# ifdef RTAUDIO_API_TYPE
@@ -467,29 +470,37 @@ struct JackBridge {
#endif
{
#ifdef HAVE_JACK
#if defined(DISTRHO_OS_MAC)
const char* const filename = "libjack.dylib";
#elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64)
const char* const filename = "libjack64.dll";
#elif defined(DISTRHO_OS_WINDOWS)
const char* const filename = "libjack.dll";
#else
const char* const filename = "libjack.so.0";
#endif
static constexpr const char* const filenames[] = {
#if defined(DISTRHO_OS_MAC)
"libjack.0.dylib",
"/usr/local/lib/libjack.0.dylib",
#elif defined(DISTRHO_OS_WINDOWS) && defined(_WIN64)
"libjack64.dll",
#elif defined(DISTRHO_OS_WINDOWS)
"libjack.dll",
#else
"libjack.so.0",
#endif
};

USE_NAMESPACE_DISTRHO

lib = lib_open(filename);
for (uint i = 0; i < ARRAY_SIZE(filenames); ++i)
{
lib = lib_open(filenames[i]);

if (lib != nullptr)
{
d_stdout("%s loaded successfully!", filenames[i]);
break;
}
}

if (lib == nullptr)
{
d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filename));
d_stderr("Failed to load JACK DLL, reason:\n%s", lib_error(filenames[0]));
return;
}
else
{
d_stdout("%s loaded successfully!", filename);
}

#define JOIN(a, b) a ## b
#define LIB_SYMBOL(NAME) JOIN(NAME, _ptr) = lib_symbol<jacksym_##NAME>(lib, "jack_" #NAME);
@@ -951,7 +962,7 @@ jack_client_t* jackbridge_client_open(const char* client_name, uint32_t options,
return kValidClient;
delete nativeBridge;
#endif
#if defined(HAVE_RTAUDIO) && defined(RTAUDIO_API_TYPE)
nativeBridge = new RtAudioBridge;
if (nativeBridge->open(client_name))


+ 46
- 20
distrho/src/jackbridge/NativeBridge.hpp View File

@@ -1,6 +1,6 @@
/*
* Native Bridge for DPF
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2025 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
@@ -19,6 +19,7 @@

#include "JackBridge.hpp"

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

#if DISTRHO_PLUGIN_NUM_INPUTS > 2
@@ -34,6 +35,8 @@
#endif

using DISTRHO_NAMESPACE::HeapRingBuffer;
using DISTRHO_NAMESPACE::RecursiveMutex;
using DISTRHO_NAMESPACE::RecursiveMutexLocker;

struct NativeBridge {
// Current status information
@@ -64,23 +67,25 @@ struct NativeBridge {
kPortMaskInputMIDI = kPortMaskInput|kPortMaskMIDI,
kPortMaskOutputMIDI = kPortMaskOutput|kPortMaskMIDI,
};
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
float* audioBuffers[DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS];
float* audioBufferStorage;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
bool midiAvailable;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
bool midiUsed;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
static constexpr const uint32_t kMaxMIDIInputMessageSize = 3;
static constexpr const uint32_t kRingBufferMessageSize = 1u /*+ sizeof(double)*/ + kMaxMIDIInputMessageSize;
uint8_t midiDataStorage[kMaxMIDIInputMessageSize];
HeapRingBuffer midiInBufferCurrent;
HeapRingBuffer midiInBufferPending;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
#endif
RecursiveMutex midiInLock;
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
HeapRingBuffer midiOutBuffer;
#endif
#endif

NativeBridge()
: bufferSize(0),
@@ -101,6 +106,7 @@ struct NativeBridge {
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
, midiAvailable(false)
, midiUsed(false)
#endif
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
@@ -132,8 +138,16 @@ struct NativeBridge {
#endif
}

virtual bool isMIDIEnabled() const
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
return midiUsed;
#else
return false;
#endif
}

virtual bool supportsBufferSizeChanges() const { return false; }
virtual bool isMIDIEnabled() const { return false; }
virtual bool requestAudioInput() { return false; }
virtual bool requestBufferSizeChange(uint32_t) { return false; }
virtual bool requestMIDI() { return false; }
@@ -158,7 +172,10 @@ struct NativeBridge {
if (midiAvailable)
{
// NOTE: this function is only called once per run
midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending);
{
const RecursiveMutexLocker cml(midiInLock);
midiInBufferCurrent.copyFromAndClearOther(midiInBufferPending);
}
return midiInBufferCurrent.getReadableDataSize() / kRingBufferMessageSize;
}
#endif
@@ -212,10 +229,10 @@ struct NativeBridge {
case 2: fail |= !midiOutBuffer.writeByte(0);
}
fail |= !midiOutBuffer.writeUInt(time);
midiOutBuffer.commitWrite();
midiOutBuffer.commitWrite("NativeBridge::writeEvent (with data)");
return !fail;
}
midiOutBuffer.commitWrite();
midiOutBuffer.commitWrite("NativeBridge::writeEvent (without data)");
}
#endif

@@ -231,7 +248,7 @@ struct NativeBridge {

if (audio)
{
#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
#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)
@@ -245,6 +262,9 @@ struct NativeBridge {

if (midi)
{
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiUsed = true;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.createBuffer(kMaxMIDIInputMessageSize * 512);
midiInBufferPending.createBuffer(kMaxMIDIInputMessageSize * 512);
@@ -261,12 +281,18 @@ struct NativeBridge {
delete[] audioBufferStorage;
audioBufferStorage = nullptr;
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.deleteBuffer();
midiInBufferPending.deleteBuffer();
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.deleteBuffer();
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if (midiUsed)
{
midiUsed = false;
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
midiInBufferCurrent.deleteBuffer();
midiInBufferPending.deleteBuffer();
#endif
#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
midiOutBuffer.deleteBuffer();
#endif
}
#endif
}



+ 29
- 5
distrho/src/jackbridge/RtAudioBridge.hpp View File

@@ -1,6 +1,6 @@
/*
* RtAudio Bridge for DPF
* Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2021-2025 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
@@ -19,7 +19,7 @@

#include "NativeBridge.hpp"

#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS == 0
#if (DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS) == 0
# error RtAudio without audio does not make sense
#endif

@@ -33,7 +33,7 @@
# define RTAUDIO_API_TYPE WINDOWS_WASAPI
# define RTMIDI_API_TYPE WINDOWS_MM
#else
# if defined(HAVE_PULSEAUDIO)
# if defined(HAVE_PULSEAUDIO) && !defined(DPF_JACK_STANDALONE_SKIP_PULSEAUDIO_FALLBACK)
# define __LINUX_PULSE__
# define RTAUDIO_API_TYPE LINUX_PULSE
# elif defined(HAVE_ALSA)
@@ -392,6 +392,28 @@ struct RtAudioBridge : NativeBridge {
const ScopedDenormalDisable sdd;
self->jackProcessCallback(numFrames, self->jackProcessArg);

#if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
if (self->midiOutBuffer.isDataAvailableForReading())
{
static_assert(kMaxMIDIInputMessageSize + 1u == 4, "change code if bumping this value");
uint8_t data[4] = {};

while (self->midiOutBuffer.isDataAvailableForReading() &&
self->midiOutBuffer.readCustomData(data, ARRAY_SIZE(data)))
{
// offset not used in RtMidiOut
self->midiOutBuffer.readUInt();

for (std::vector<RtMidiOut>::iterator it = self->midiOuts.begin(), end = self->midiOuts.end(); it != end; ++it)
{
static_cast<RtMidiOut&>(*it).sendMessage(data + 1, data[0]);
}
}

self->midiOutBuffer.flush();
}
#endif

return 0;
}

@@ -403,13 +425,15 @@ struct RtAudioBridge : NativeBridge {

RtAudioBridge* const self = static_cast<RtAudioBridge*>(userData);

const RecursiveMutexLocker rml(self->midiInLock);

self->midiInBufferPending.writeByte(static_cast<uint8_t>(len));
// TODO timestamp
// self->midiInBufferPending.writeDouble(timestamp);
self->midiInBufferPending.writeCustomData(message->data(), len);
for (uint8_t i=len; i<kMaxMIDIInputMessageSize; ++i)
for (uint8_t i = len; i < kMaxMIDIInputMessageSize; ++i)
self->midiInBufferPending.writeByte(0);
self->midiInBufferPending.commitWrite();
self->midiInBufferPending.commitWrite("RtMidiCallback");
}
#endif
};


+ 47
- 44
distrho/src/jackbridge/WebBridge.hpp View File

@@ -224,55 +224,58 @@ struct WebBridge : NativeBridge {

var constraints = {};
// we need to use this weird awkward way for objects, otherwise build fails
constraints['audio'] = true;
constraints['video'] = false;
constraints['autoGainControl'] = {};
constraints['autoGainControl']['ideal'] = false;
constraints['echoCancellation'] = {};
constraints['echoCancellation']['ideal'] = false;
constraints['noiseSuppression'] = {};
constraints['noiseSuppression']['ideal'] = false;
constraints['channelCount'] = {};
constraints['channelCount']['min'] = 0;
constraints['channelCount']['ideal'] = numInputs;
constraints['latency'] = {};
constraints['latency']['min'] = 0;
constraints['latency']['ideal'] = 0;
constraints['sampleSize'] = {};
constraints['sampleSize']['min'] = 8;
constraints['sampleSize']['max'] = 32;
constraints['sampleSize']['ideal'] = 16;
// old property for chrome
constraints['googAutoGainControl'] = false;
constraints['audio'] = {};
constraints['audio']['autoGainControl'] = {};
constraints['audio']['autoGainControl']['ideal'] = false;
constraints['audio']['echoCancellation'] = {};
constraints['audio']['echoCancellation']['ideal'] = false;
constraints['audio']['noiseSuppression'] = {};
constraints['audio']['noiseSuppression']['ideal'] = false;
constraints['audio']['channelCount'] = {};
constraints['audio']['channelCount']['min'] = 0;
constraints['audio']['channelCount']['ideal'] = numInputs;
constraints['audio']['latency'] = {};
constraints['audio']['latency']['min'] = 0;
constraints['audio']['latency']['ideal'] = 0;
constraints['audio']['sampleSize'] = {};
constraints['audio']['sampleSize']['min'] = 8;
constraints['audio']['sampleSize']['max'] = 32;
constraints['audio']['sampleSize']['ideal'] = 16;
// old properties for chrome
constraints['audio']['googAudioMirroring'] = {};
constraints['audio']['googAudioMirroring']['ideal'] = false;
constraints['audio']['googAutoGainControl'] = {};
constraints['audio']['googAutoGainControl']['ideal'] = false;
constraints['audio']['googAutoGainControl2'] = {};
constraints['audio']['googAutoGainControl2']['ideal'] = false;
constraints['audio']['googDAEchoCancellation'] = {};
constraints['audio']['googDAEchoCancellation']['ideal'] = false;
constraints['audio']['googEchoCancellation'] = {};
constraints['audio']['googEchoCancellation']['ideal'] = false;
constraints['audio']['googEchoCancellation2'] = {};
constraints['audio']['googEchoCancellation2']['ideal'] = false;
constraints['audio']['googHighpassFilter'] = {};
constraints['audio']['googHighpassFilter']['ideal'] = false;
constraints['audio']['googNoiseSuppression'] = {};
constraints['audio']['googNoiseSuppression']['ideal'] = false;
constraints['audio']['googNoiseSuppression2'] = {};
constraints['audio']['googNoiseSuppression2']['ideal'] = false;
constraints['audio']['googTypingNoiseDetection'] = {};
constraints['audio']['googTypingNoiseDetection']['ideal'] = false;
constraints['audio']['intelligibilityEnhancer'] = {};
constraints['audio']['intelligibilityEnhancer']['ideal'] = false;
constraints['audio']['levelControl'] = {};
constraints['audio']['levelControl']['ideal'] = false;
constraints['audio']['levelControlInitialPeakLevelDBFS'] = {};
constraints['audio']['levelControlInitialPeakLevelDBFS']['ideal'] = false;

var success = function(stream) {
var tracks = stream.getAudioTracks();

// try to force as much as we can
for (var i in tracks) {
var track = tracks[i];

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);
};
var fail = function() {
var fail = function(err) {
console.error(err);
};

if (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined) {
@@ -494,7 +497,7 @@ struct WebBridge : NativeBridge {
}, offset, bytes[0], bytes[1], bytes[2], bytes[3], timestamp);
}

self->midiOutBuffer.clearData();
self->midiOutBuffer.flush();
}
#endif
}


+ 2
- 1
distrho/src/lv2/lv2_kxstudio_properties.h View File

@@ -1,6 +1,6 @@
/*
LV2 KXStudio Properties Extension
Copyright 2014-2021 Filipe Coelho <falktx@falktx.com>
Copyright 2014-2024 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
@@ -27,6 +27,7 @@
#define LV2_KXSTUDIO_PROPERTIES_PREFIX LV2_KXSTUDIO_PROPERTIES_URI "#"

#define LV2_KXSTUDIO_PROPERTIES__NonAutomatable LV2_KXSTUDIO_PROPERTIES_PREFIX "NonAutomatable"
#define LV2_KXSTUDIO_PROPERTIES__Reset LV2_KXSTUDIO_PROPERTIES_PREFIX "Reset"
#define LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat LV2_KXSTUDIO_PROPERTIES_PREFIX "TimePositionTicksPerBeat"
#define LV2_KXSTUDIO_PROPERTIES__TransientWindowId LV2_KXSTUDIO_PROPERTIES_PREFIX "TransientWindowId"



+ 73
- 0
distrho/src/mapi/mapi.h View File

@@ -0,0 +1,73 @@
// Copyright 2025 Filipe Coelho <falktx@falktx.com>
// SPDX-License-Identifier: ISC

#pragma once

#ifdef __cplusplus
extern "C" {
# define MAPI_API extern "C"
#else
# define MAPI_API
#endif

/* Define MAPI_EXPORT for exporting function symbols */
#ifdef _WIN32
# define MAPI_EXPORT MAPI_API __declspec (dllexport)
#else
# define MAPI_EXPORT MAPI_API __attribute__ ((visibility("default")))
#endif

/** Handle used through-out this API. */
typedef void* mapi_handle_t;

/**
Create an effect.
@param sample_rate Sample rate in Hz to use for the effect.
@return A handle for the new effect, or NULL if creation failed.
*/
MAPI_EXPORT
mapi_handle_t mapi_create(unsigned int sample_rate);

/**
Process an effect.
@param handle A previously created effect.
@param ins An array of audio buffers used for audio input.
@param outs An array of audio buffers used for audio output.
@param frames How many frames to process.
@note Input and output buffers might share the same location in memory,
typically referred to as "in-place processing".
*/
MAPI_EXPORT
void mapi_process(mapi_handle_t handle,
const float* const* ins,
float** outs,
unsigned int frames);

/**
Set an effect parameter.
@param handle A previously created effect.
@param index A known index for this effect.
@param value A full-ranged value.
*/
MAPI_EXPORT
void mapi_set_parameter(mapi_handle_t handle, unsigned int index, float value);

/**
Set an effect state, using strings for both key and value.
@param handle A previously created effect.
@param key A known key for this effect, must not be NULL or empty.
@param value A non-NULL value, allowed to be empty.
*/
MAPI_EXPORT
void mapi_set_state(mapi_handle_t handle, const char* key, const char* value);

/**
Destroy a previously created effect.
@param handle A previously created effect.
*/
MAPI_EXPORT
void mapi_destroy(mapi_handle_t handle);

#ifdef __cplusplus
}
#endif

+ 1
- 5
examples/EmbedExternalUI/DistrhoPluginInfo.h View File

@@ -1,6 +1,6 @@
/*
* DISTRHO Plugin Framework (DPF)
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
* Copyright (C) 2012-2025 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,10 +33,6 @@
#define DISTRHO_UI_DEFAULT_WIDTH 768
#define DISTRHO_UI_DEFAULT_HEIGHT 512

// #ifdef _WIN32
// #define WEB_VIEW_USING_CHOC 1
// #endif

enum Parameters {
kParameterWidth = 0,
kParameterHeight,


+ 203
- 0
utils/emscripten.html.in View File

@@ -0,0 +1,203 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="description" content="@NAME@" />
<meta name="theme-color" content="#111111">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0" user-scalable="no">
<title>@NAME@</title>
<style>
html {
background-color: #111111;
color: #eee;
overflow: hidden;
}
body, canvas {
padding: 0;
margin: 0;
}

#canvas_file_open {
display: none;
}

#canvas_wrapper {
--device-pixel-ratio: 1;
display: none;
image-rendering: pixelated;
image-rendering: crisp-edges;
position: fixed;
transform-origin: 0 0 0;
transform: scale(calc(1 / var(--device-pixel-ratio)));
width: 100vw;
height: 100vh;
/* center */
margin: auto auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

#error {
background: rgba(0,0,0,0.75);
display: none;
position: fixed;
padding: 0.5em;
left: 0;
right: 0;
width: 100%;
z-index: 2;
}

.emscripten {
display: block;
margin-left: auto;
margin-right: auto;
padding-right: 0;
text-align: center;
}

.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
margin-top: 100px;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}

</style>
</head>
<body>
<figure style="overflow:visible;" id="spinner">
<div class="spinner"></div>
<center style="margin-top:0.5em"><strong>@NAME@</strong></center>
</figure>
<div class="emscripten" id="error"></div>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
<div id="canvas_wrapper">
<input type="file" id="canvas_file_open" ></input>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>

<script type='text/javascript'>
'use strict';

var wasmErrors = [];
var errorElement = document.getElementById('error');
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');

if (typeof(WebAssembly) === "undefined") {
wasmErrors.push('WebAssembly unsupported');
} else {
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]))) {
wasmErrors.push('Importable/Exportable mutable globals unsupported');
}
}

if (wasmErrors.length !== 0) {
errorElement.innerHTML = 'Cannot start @NAME@:<br>' + wasmErrors.join('<br>') + '<br><br>Perhaps try a different browser?';
errorElement.style.display = 'block';
statusElement.style.display = 'none';
progressElement.style.display = 'none';
spinnerElement.style.display = 'none';
} else {
var canvasWrapper = document.getElementById('canvas_wrapper');

var Module = {
preRun: [],
postRun: function() {
statusElement.style.display = 'none';
progressElement.style.display = 'none';
spinnerElement.style.display = 'none';
canvasWrapper.style.display = 'block';
window.dispatchEvent(new Event('resize'));
},
canvas: (function() {
var canvas = document.getElementById('canvas');

// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);

return canvas;
})(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
Module.setStatus('Downloading...');
window.onerror = function(err) {
errorElement.innerHTML = 'Exception thrown:<br>' + err
errorElement.style.display = 'block';
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if (text) console.error('[post-exception status] ' + text);
};
};

var jsModuleScript = document.createElement('script');
jsModuleScript.setAttribute('async', true);
jsModuleScript.setAttribute('src', "@NAME@.js");
jsModuleScript.setAttribute('type','text/javascript');
document.head.appendChild(jsModuleScript);
}
</script>
</body>
</html>

+ 77
- 0
utils/install-plugins-symlinks.sh View File

@@ -0,0 +1,77 @@
#!/bin/bash

# the realpath function is not available on some systems
if ! which realpath &>/dev/null; then
function realpath() {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
fi

set -e

DPF_UTILS_DIR="$(dirname $(realpath ${0}))"

if [ -d bin ]; then
cd bin
elif [ -d build/bin ]; then
cd build/bin
else
echo "Please run this script from the root folder"
exit
fi

if [ "$(uname -s)" = "Darwin" ]; then
CLAP_PATH=~/Library/Audio/Plug-Ins/CLAP
LV2_PATH=~/Library/Audio/Plug-Ins/LV2
VST2_PATH=~/Library/Audio/Plug-Ins/VST
VST3_PATH=~/Library/Audio/Plug-Ins/VST3
else
CLAP_PATH=~/.clap
LV2_PATH=~/.lv2
VST2_PATH=~/.vst
VST3_PATH=~/.vst3
fi

export IFS=$'\n'

# NOTE macOS ignores AU plugins installed in ~/Library/Audio/Plug-Ins/Components/
# we **MUST** install AU plugins system-wide, so we need sudo/root here
# they cannot be symlinks either, so we do a full copy
for p in $(find . -maxdepth 1 -name '*.component' -print | grep '.component'); do
basename=$(basename ${p})
if [ ! -L /Library/Audio/Plug-Ins/Components/"${basename}" ]; then
sudo cp -r $(pwd)/"${basename}" /Library/Audio/Plug-Ins/Components/
fi
done

for p in $(find . -maxdepth 1 -name '*.clap' -print); do
basename=$(basename ${p})
mkdir -p ${CLAP_PATH}
if [ ! -L ${CLAP_PATH}/"${basename}" ]; then
ln -s $(pwd)/"${basename}" ${CLAP_PATH}/"${basename}"
fi
done

for p in $(find . -maxdepth 1 -name '*.lv2' -print); do
basename=$(basename ${p})
mkdir -p ${LV2_PATH}
if [ ! -L ${LV2_PATH}/"${basename}" ]; then
ln -s $(pwd)/"${basename}" ${LV2_PATH}/"${basename}"
fi
done

for p in $(find . -maxdepth 1 -name '*.vst' -print); do
basename=$(basename ${p})
mkdir -p ${VST2_PATH}
if [ ! -L ${VST2_PATH}/"${basename}" ]; then
ln -s $(pwd)/"${basename}" ${VST2_PATH}/"${basename}"
fi
done

for p in $(find . -maxdepth 1 -name '*.vst3' -print); do
basename=$(basename ${p})
mkdir -p ${VST3_PATH}
if [ ! -L ${VST3_PATH}/"${basename}" ]; then
ln -s $(pwd)/"${basename}" ${VST3_PATH}/"${basename}"
fi
done

+ 6
- 0
utils/symbols/mapi.def View File

@@ -0,0 +1,6 @@
EXPORTS
mapi_create
mapi_process
mapi_set_parameter
mapi_set_state
mapi_destroy

+ 5
- 0
utils/symbols/mapi.exp View File

@@ -0,0 +1,5 @@
_mapi_create
_mapi_process
_mapi_set_parameter
_mapi_set_state
_mapi_destroy

+ 4
- 0
utils/symbols/mapi.version View File

@@ -0,0 +1,4 @@
{
global: mapi_create; mapi_process; mapi_set_parameter; mapi_set_state; mapi_destroy;
local: *;
};

+ 0
- 2
utils/symbols/shared.def View File

@@ -1,2 +0,0 @@
EXPORTS
createSharedPlugin

+ 0
- 1
utils/symbols/shared.exp View File

@@ -1 +0,0 @@
_createSharedPlugin

+ 0
- 4
utils/symbols/shared.version View File

@@ -1,4 +0,0 @@
{
global: createSharedPlugin;
local: *;
};

+ 24
- 0
utils/valgrind-dpf.supp View File

@@ -93,3 +93,27 @@
fun:fftwf_plan_dft_r2c_1d
...
}
{
leak in fftw plan
Memcheck:Leak
fun:malloc
fun:fftwf_malloc_plain
...
fun:fftwf_mkapiplan
fun:fftwf_plan_many_r2r
fun:fftwf_plan_r2r
fun:fftwf_plan_r2r_1d
...
}
{
leak in fftw plan
Memcheck:Leak
fun:memalign
fun:fftwf_malloc_plain
...
fun:fftwf_mkapiplan
fun:fftwf_plan_many_r2r
fun:fftwf_plan_r2r
fun:fftwf_plan_r2r_1d
...
}

Loading…
Cancel
Save