@@ -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) | |||
project(DPF) | |||
@@ -34,11 +34,11 @@ if(DPF_LIBRARIES) | |||
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) | |||
@@ -287,7 +287,7 @@ HAVE_DGL = false | |||
endif | |||
endif | |||
ifeq ($(HAVE_DGL)$(LINUX)$(USE_WEB_VIEW),truetruetrue) | |||
ifeq ($(HAVE_DGL)$(LINUX)$(UI_TYPE),truetruewebview) | |||
DGL_LIB_SHARED = $(shell $(CC) -print-file-name=Scrt1.o) | |||
endif | |||
@@ -121,23 +121,33 @@ 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 "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}") | |||
@@ -234,6 +244,112 @@ 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 | |||
# - 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 "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") | |||
if(_dpf_plugin_USE_FILE_BROWSER) | |||
target_compile_definitions("${NAME}" PUBLIC "DGL_USE_FILE_BROWSER") | |||
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__`) | |||
# ------------------------------------------------------------------------------ | |||
@@ -661,7 +777,7 @@ 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() | |||
@@ -710,6 +826,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 +861,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 +901,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) | |||
@@ -786,7 +932,7 @@ endfunction() | |||
# | |||
# 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() | |||
@@ -841,6 +987,21 @@ 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) | |||
@@ -856,7 +1017,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() | |||
@@ -911,6 +1072,21 @@ 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) | |||
@@ -926,7 +1102,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 +1152,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 +1193,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() | |||
@@ -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,11 +84,16 @@ class DISTRHO_API Application | |||
{ | |||
public: | |||
/** | |||
Constructor. | |||
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); | |||
/** | |||
Constructor for a standalone application. | |||
This specific constructor is required if using web views in standalone applications. | |||
*/ | |||
Application(int argc, char* argv[]); | |||
/** | |||
Destructor. | |||
*/ | |||
@@ -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,8 +41,8 @@ | |||
# 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) | |||
@@ -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. | |||
@@ -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; | |||
@@ -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) \ | |||
@@ -201,6 +205,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 | |||
@@ -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; | |||
@@ -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 |
@@ -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. | |||
*/ | |||
@@ -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 | |||
@@ -96,6 +112,43 @@ 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)) | |||
{ | |||
#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)); | |||
#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 | |||
@@ -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; | |||
@@ -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,6 +105,9 @@ struct ButtonEventHandler::PrivateData { | |||
bool motionEvent(const Widget::MotionEvent& ev) | |||
{ | |||
if (! enabledInput) | |||
return false; | |||
// keep pressed | |||
if (button != -1) | |||
{ | |||
@@ -171,6 +181,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 +248,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 +322,8 @@ struct KnobEventHandler::PrivateData { | |||
float value; | |||
float valueDef; | |||
float valueTmp; | |||
bool enabled; | |||
bool enabledInput; | |||
bool usingDefault; | |||
bool usingLog; | |||
Orientation orientation; | |||
@@ -301,6 +344,8 @@ struct KnobEventHandler::PrivateData { | |||
value(0.5f), | |||
valueDef(value), | |||
valueTmp(value), | |||
enabled(true), | |||
enabledInput(true), | |||
usingDefault(false), | |||
usingLog(false), | |||
orientation(Vertical), | |||
@@ -320,6 +365,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 +385,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 +412,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 +468,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 +556,9 @@ struct KnobEventHandler::PrivateData { | |||
bool scrollEvent(const Widget::ScrollEvent& ev) | |||
{ | |||
if (! enabledInput) | |||
return false; | |||
if (! widget->contains(ev.pos)) | |||
return false; | |||
@@ -541,6 +599,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 +678,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 +719,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); | |||
@@ -279,6 +279,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; | |||
@@ -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" |
@@ -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 | |||
@@ -421,6 +421,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) | |||
@@ -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 | |||
@@ -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(); | |||
@@ -394,13 +408,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); | |||
@@ -443,6 +465,11 @@ void Window::PrivateData::idleCallback() | |||
fileBrowserHandle = nullptr; | |||
} | |||
#endif | |||
#ifdef DGL_USE_WEB_VIEW | |||
if (webViewHandle != nullptr) | |||
webViewIdle(webViewHandle); | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------- | |||
@@ -479,7 +506,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 +518,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 +529,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 PuglRect rect = puglGetFrame(view); | |||
uint initialWidth = static_cast<uint>(rect.width) - options.offset.x; | |||
uint initialHeight = static_cast<uint>(rect.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 +582,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 +634,11 @@ 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); | |||
if (autoScaling) | |||
{ | |||
@@ -596,8 +651,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); | |||
@@ -860,7 +923,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 | |||
@@ -962,7 +1025,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 +1048,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 +1071,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 +1094,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 +1115,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 +1182,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 +1251,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, | |||
@@ -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 | |||
@@ -95,6 +95,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 +175,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 +192,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); | |||
@@ -188,7 +188,7 @@ getCurrentViewStyleFlags(PuglView* const view) | |||
} | |||
static PuglStatus | |||
dispatchCurrentChildViewConfiguration(PuglView* const view) | |||
dispatchCurrentChildViewConfiguration(PuglView* const view, bool drawViewResize) | |||
{ | |||
const NSRect framePt = [view->impl->wrapperView frame]; | |||
const NSRect framePx = nsRectFromPoints(view, framePt); | |||
@@ -197,6 +197,11 @@ dispatchCurrentChildViewConfiguration(PuglView* const view) | |||
return PUGL_SUCCESS; | |||
} | |||
if (drawViewResize) { | |||
const NSSize sizePt = [view->impl->drawView convertSizeFromBacking:framePx.size]; | |||
[view->impl->drawView setFrameSize:sizePt]; | |||
} | |||
const PuglConfigureEvent ev = { | |||
PUGL_CONFIGURE, | |||
0, | |||
@@ -317,7 +322,7 @@ dispatchCurrentChildViewConfiguration(PuglView* const view) | |||
if (puglview->impl->window) { | |||
[puglview->impl->window dispatchCurrentConfiguration]; | |||
} else { | |||
dispatchCurrentChildViewConfiguration(puglview); | |||
dispatchCurrentChildViewConfiguration(puglview, true); | |||
} | |||
reshaped = false; | |||
} | |||
@@ -1767,7 +1772,7 @@ puglSetFrame(PuglView* view, const PuglRect frame) | |||
[impl->wrapperView setFrame:framePt]; | |||
[impl->drawView setFrame:sizePt]; | |||
return dispatchCurrentChildViewConfiguration(view); | |||
return dispatchCurrentChildViewConfiguration(view, false); | |||
} | |||
PuglStatus | |||
@@ -1806,7 +1811,7 @@ puglSetPosition(PuglView* const view, const int x, const int y) | |||
[impl->drawView setFrameOrigin:drawPt.origin]; | |||
// Dispatch new configuration | |||
return dispatchCurrentChildViewConfiguration(view); | |||
return dispatchCurrentChildViewConfiguration(view, false); | |||
} | |||
PuglStatus | |||
@@ -1843,7 +1848,7 @@ puglSetSize(PuglView* const view, const unsigned width, const unsigned height) | |||
[impl->drawView setFrameSize:drawPt.size]; | |||
// Dispatch new configuration | |||
return dispatchCurrentChildViewConfiguration(view); | |||
return dispatchCurrentChildViewConfiguration(view, false); | |||
} | |||
PuglStatus | |||
@@ -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 | |||
@@ -363,7 +373,8 @@ PuglStatus puglSetSizeAndDefault(PuglView* view, uint width, uint height) | |||
#ifdef DGL_USING_X11 | |||
// workaround issues in fluxbox, see https://github.com/lv2/pugl/issues/118 | |||
if (view->impl->win && !view->parent && !view->transientParent) | |||
// NOTE troublesome if used under KDE | |||
if (view->impl->win && !view->parent && !view->transientParent && std::getenv("KDE_SESSION_VERSION") == nullptr) | |||
{ | |||
view->sizeHints[PUGL_DEFAULT_SIZE].width = view->sizeHints[PUGL_DEFAULT_SIZE].height = 0; | |||
} | |||
@@ -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; | |||
} | |||
} | |||
@@ -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 | |||
@@ -37,9 +37,29 @@ START_NAMESPACE_DISTRHO | |||
*/ | |||
const char* getBinaryFilename(); | |||
/** | |||
Get an OS-specific directory intended to store persistent configuration data about the plugin.@n | |||
Calling this function will ensure the dictory exists on the filesystem.@n | |||
The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. | |||
*/ | |||
const char* getConfigDir(); | |||
/** | |||
Get an OS-specific directory intended to store "documents" for the plugin.@n | |||
Calling this function will ensure the dictory exists on the filesystem.@n | |||
The returned path already includes DISTRHO_PLUGIN_NAME and final OS separator. | |||
*/ | |||
const char* getDocumentsDir(); | |||
/** | |||
Get the user "home" directory.@n | |||
This function is provided only for convenience, it should not be needed under normal circunstances. | |||
*/ | |||
const char* getHomeDir(); | |||
/** | |||
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" or "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. | |||
@@ -185,7 +185,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. | |||
@@ -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 | |||
@@ -66,7 +66,7 @@ | |||
# 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); | |||
@@ -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 |
@@ -0,0 +1,106 @@ | |||
/* | |||
* 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> | |||
#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)) {} | |||
/** | |||
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 |
@@ -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. | |||
*/ | |||
@@ -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 | |||
DISTRHO_NAMESPACE::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 |
@@ -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); } | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- |
@@ -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,13 +26,26 @@ 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 | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -41,9 +54,13 @@ END_NAMESPACE_DISTRHO | |||
# define WC_ERR_INVALID_CHARS 0 | |||
# 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 | |||
@@ -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 | |||
@@ -264,7 +264,7 @@ bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct PropertyListener { | |||
AudioUnitPropertyID prop; | |||
AudioUnitPropertyID prop; | |||
AudioUnitPropertyListenerProc proc; | |||
void* userData; | |||
}; | |||
@@ -348,7 +348,8 @@ 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 | |||
@@ -365,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); | |||
@@ -374,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; | |||
} | |||
} | |||
} | |||
@@ -923,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; | |||
} | |||
@@ -1143,6 +1153,8 @@ public: | |||
const CFStringRef keyRef = CFStringCreateWithCString(nullptr, | |||
fPlugin.getStateKey(i), | |||
kCFStringEncodingASCII); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(keyRef != nullptr); | |||
CFArrayAppendValue(keysRef, keyRef); | |||
CFRelease(keyRef); | |||
} | |||
@@ -1458,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; | |||
@@ -1480,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); | |||
@@ -1612,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; | |||
@@ -1637,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; | |||
@@ -1821,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(); | |||
@@ -2180,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; | |||
@@ -2220,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); | |||
} | |||
} | |||
@@ -2545,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); | |||
@@ -2845,7 +2879,7 @@ private: | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
struct AudioComponentPlugInInstance { | |||
AudioComponentPlugInInterface acpi; | |||
AudioComponentPlugInInterface acpi; | |||
PluginAU* plugin; | |||
AudioComponentPlugInInstance() noexcept | |||
@@ -2854,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() | |||
@@ -2864,7 +2898,7 @@ 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); | |||
@@ -2872,7 +2906,7 @@ struct AudioComponentPlugInInstance { | |||
return noErr; | |||
} | |||
static OSStatus Close(void* const self) | |||
static OSStatus Close(void* const self) | |||
{ | |||
d_debug("AudioComponentPlugInInstance::Close(%p)", self); | |||
@@ -2964,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; | |||
} | |||
@@ -3016,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; | |||
} | |||
@@ -751,6 +751,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 +764,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); | |||
@@ -823,7 +836,18 @@ public: | |||
void reset() | |||
{ | |||
fHost->request_restart(fHost); | |||
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) | |||
@@ -1119,14 +1143,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); | |||
@@ -1156,6 +1185,7 @@ public: | |||
} | |||
d_strncpy(info->module + wrtn, fPlugin.getParameterSymbol(index), CLAP_PATH_SIZE - wrtn); | |||
break; | |||
} | |||
info->id = index; | |||
@@ -1791,6 +1821,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 | |||
@@ -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,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 | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
@@ -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; | |||
@@ -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; | |||
@@ -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) | |||
@@ -758,6 +758,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; | |||
} | |||
} | |||
@@ -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) | |||
@@ -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) | |||
@@ -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 | |||
@@ -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,6 +61,12 @@ | |||
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 | |||
@@ -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 | |||
@@ -76,6 +82,177 @@ const char* getBinaryFilename() | |||
return filename; | |||
} | |||
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.isEmpty()) | |||
{ | |||
dir = getHomeDir(); | |||
dir += "/.config"; | |||
} | |||
// ensure main config dir exists | |||
if (access(dir, F_OK) != 0) | |||
mkdir(dir, 0755); | |||
// and also our custom subdir | |||
dir += "/" DISTRHO_PLUGIN_NAME "/"; | |||
if (access(dir, F_OK) != 0) | |||
mkdir(dir, 0755); | |||
} | |||
return dir; | |||
#endif | |||
} | |||
const char* getDocumentsDir() | |||
{ | |||
static String dir; | |||
if (dir.isEmpty()) | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
dir = getHomeDir(); | |||
dir += "/Documents/" DISTRHO_PLUGIN_NAME "/"; | |||
#elif defined(DISTRHO_OS_WASM) | |||
dir = getHomeDir(); | |||
dir += "/"; | |||
#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; | |||
dir += "\\" DISTRHO_PLUGIN_NAME "\\"; | |||
wcscat(wpath, L"\\" DISTRHO_PLUGIN_NAME "\\"); | |||
} | |||
} | |||
#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() + 5; | |||
} | |||
else | |||
{ | |||
dir = sdir; | |||
} | |||
// ensure main config dir exists | |||
if (access(dir, F_OK) != 0) | |||
mkdir(dir, 0755); | |||
} | |||
} | |||
std::free(filedata); | |||
} | |||
} | |||
} | |||
std::fclose(f); | |||
} | |||
// ${XDG_CONFIG_HOME}/user-dirs.dirs does not exist or has bad data | |||
if (dir.isEmpty()) | |||
{ | |||
dir = getDocumentsDir(); | |||
dir += DISTRHO_PLUGIN_NAME "/"; | |||
} | |||
#endif | |||
// ensure our custom subdir exists | |||
if (dir.isNotEmpty()) | |||
{ | |||
#ifdef DISTRHO_OS_WINDOWS | |||
_wmkdir(wpath); | |||
#else | |||
if (access(dir, F_OK) != 0) | |||
mkdir(dir, 0755); | |||
#endif | |||
} | |||
} | |||
return dir; | |||
} | |||
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; | |||
if (dir.isNotEmpty() && ! dir.endsWith('/')) | |||
dir += "/"; | |||
#endif | |||
} | |||
return dir; | |||
} | |||
const char* getPluginFormatName() noexcept | |||
{ | |||
#if defined(DISTRHO_PLUGIN_TARGET_AU) | |||
@@ -166,11 +343,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 |
@@ -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" | |||
@@ -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 |