diff --git a/.github/workflows/check-cla.yml b/.github/workflows/check-cla.yml index 70d082413d39..c657a4cff283 100644 --- a/.github/workflows/check-cla.yml +++ b/.github/workflows/check-cla.yml @@ -18,6 +18,7 @@ jobs: with urllib.request.urlopen(req) as response: return json.loads(response.read().decode('utf-8')) prCommits = jsonRequest('https://api.github.com/repos/juce-framework/JUCE/pulls/${{ github.event.number }}/commits') + print(f'Commit info:\n{json.dumps(prCommits, indent=4)}') allAuthors = [commit[authorType]['login'] for authorType in ['author', 'committer'] for commit in prCommits if commit[authorType]] uniqueAuthors = [name for name in list(set(allAuthors)) if name != 'web-flow'] if (len(uniqueAuthors) == 0): diff --git a/.gitignore b/.gitignore index 629e360a220c..e3ffc8ba94c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,69 +1,9 @@ -._* -*.mode1v3 -*.pbxuser -*.perspectivev3 -*.user -*.ncb -*.suo -*.ilk -*.pch -*.pdb -*.dep -*.idb -*.manifest -*.manifest.res -*.o -*.d -*.sdf -*.opensdf -*.VC.db -*.VC.opendb -xcuserdata -*.xccheckout -*.xcscmblueprint -*.xcscheme -contents.xcworkspacedata -.DS_Store -.svn -.deps -.dirstamp -profile -**/MacOSX/build -**/iOS/build -**/IDEWorkspaceChecks.plist -**/Linux/build -**/LinuxMakefile/build -**/VisualStudio[0-9]*/Win32 -**/VisualStudio[0-9]*/x64 -**/Builds/x64 -**/.vs -**/CodeBlocks/bin -**/CodeBlocks/obj -**/CodeBlocks/*.depend -**/CodeBlocks/*.layout -**/Builds/Android/.gradle -**/Builds/Android/.idea -**/Builds/Android/build -**/Builds/Android/**/*.iml -**/Builds/Android/local.properties -**/Builds/Android/app/build -**/Builds/Android/app/.externalNativeBuild -**/Builds/Android/app/.cxx -**/Builds/Android/lib/build -**/Builds/Android/lib/.externalNativeBuild -**/Builds/MacOSX/**/Index -**/Builds/MacOSX/**/Intermediates.noindex -**/doxygen/doc -**/doxygen/build -**/.idea -extras/Projucer/JUCECompileEngine.dylib +# Ignorar la carpeta de compilación y sus archivos generados +Builds/ -.idea -**/cmake-build* +# Ignorar la carpeta de código generado por JUCE +JuceLibraryCode/ -.vscode -/build - -CMakeUserPresets.json -.editorconfig +# Ignorar archivos específicos de Mac +**/.DS_Store diff --git a/BREAKING_CHANGES.md b/BREAKING_CHANGES.md index 2c1cee87ee5d..88f5a1bda30b 100644 --- a/BREAKING_CHANGES.md +++ b/BREAKING_CHANGES.md @@ -2,6 +2,23 @@ # Version 8.0.4 +## Change + +Support for Arm32 in Projucer has been removed for Windows targets. + +**Possible Issues** + +Projucer projects targeting Arm32 on Windows will no longer be able to select that option. + +**Workaround** + +Select Arm64 or Arm64EC instead of Arm32, and port any 32-bit specific code to 64-bit. + +**Rationale** + +32-bit Arm support has been deprecated in current versions of Windows 11. + + ## Change The Javascript implementation has been moved into a independent juce module. @@ -20,6 +37,27 @@ The Javascript implementation increases compilation times while being required by only a select number of projects. +## Change + +The return type for VST3ClientExtensions::getCompatibleClasses() has changed +from a String to an array of 16 bytes. + +**Possible Issues** + +Any inherited classes overriding this method might fail to compile. + +**Workaround** + +Either explicitly switch to creating a 16-byte std::array or use +VST3ClientExtensions::toInterfaceId() to convert a string to a 16-byte array. + +**Rationale** + +As part of adding functionality to support migrating parameter IDs from +compatible plugins it was useful to switch to a safer type for representing +VST3 interface IDs that closer matches the VST3 SDK types. + + ## Change The VBlankAttachment class' inheritance from the ComponentPeer::VBlankListener diff --git a/LICENSE.md b/LICENSE.md index fa9877036504..39e3c57e1725 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -42,7 +42,7 @@ The JUCE modules contain the following dependencies: - [GLEW](modules/juce_opengl/opengl/juce_gl.h) ([BSD](modules/juce_opengl/opengl/juce_gl.h)), including [Mesa](modules/juce_opengl/opengl/juce_gl.h) ([MIT](modules/juce_opengl/opengl/juce_gl.h)) and [Khronos](modules/juce_opengl/opengl/juce_gl.h) ([MIT](modules/juce_opengl/opengl/juce_gl.h)) - [Ogg Vorbis](modules/juce_audio_formats/codecs/oggvorbis/) ([BSD](modules/juce_audio_formats/codecs/oggvorbis/Ogg%20Vorbis%20Licence.txt)) - [jpeglib](modules/juce_graphics/image_formats/jpglib/) ([Independent JPEG Group License](modules/juce_graphics/image_formats/jpglib/README)) -- [CHOC](modules/juce_core/javascript/choc/) ([ISC](modules/juce_core/javascript/choc/LICENSE.md)), including [QuickJS](modules/juce_core/javascript/choc/javascript/choc_javascript_QuickJS.h) ([MIT](modules/juce_core/javascript/choc/javascript/choc_javascript_QuickJS.h)) +- [CHOC](modules/juce_javascript/choc/) ([ISC](modules/juce_javascript/choc/LICENSE.md)), including [QuickJS](modules/juce_javascript/choc/javascript/choc_javascript_QuickJS.h) ([MIT](modules/juce_javascript/choc/javascript/choc_javascript_QuickJS.h)) - [LV2](modules/juce_audio_processors/format_types/LV2_SDK/) ([ISC](modules/juce_audio_processors/format_types/LV2_SDK/lv2/COPYING)) - [pslextensions](modules/juce_audio_processors/format_types/pslextensions/ipslcontextinfo.h) ([Public domain](modules/juce_audio_processors/format_types/pslextensions/ipslcontextinfo.h)) - [AAX](modules/juce_audio_plugin_client/AAX/SDK/) ([Proprietary Avid AAX License/GPLv3](modules/juce_audio_plugin_client/AAX/SDK/LICENSE.txt)) diff --git a/examples/Assets/DemoUtilities.h b/examples/Assets/DemoUtilities.h index 1c5534bff731..4f1f0f26319b 100644 --- a/examples/Assets/DemoUtilities.h +++ b/examples/Assets/DemoUtilities.h @@ -63,7 +63,7 @@ inline File getExamplesDirectory() noexcept return File { CharPointer_UTF8 { PIP_JUCE_EXAMPLES_DIRECTORY_STRING } }; #else auto currentFile = File::getSpecialLocation (File::SpecialLocationType::currentApplicationFile); - auto exampleDir = currentFile.getParentDirectory().getChildFile ("examples"); + auto exampleDir = currentFile.getSiblingFile ("examples"); if (exampleDir.exists()) return exampleDir; @@ -109,10 +109,10 @@ inline std::unique_ptr createAssetInputStream (const char* resource #else #if JUCE_IOS auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getChildFile ("Assets"); + .getSiblingFile ("Assets"); #elif JUCE_MAC auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getParentDirectory().getChildFile ("Resources").getChildFile ("Assets"); + .getParentDirectory().getSiblingFile ("Resources").getChildFile ("Assets"); if (! assetsDir.exists()) assetsDir = getExamplesDirectory().getChildFile ("Assets"); diff --git a/examples/CMake/GuiApp/Main.cpp b/examples/CMake/GuiApp/Main.cpp index 6930f3e0db12..702aaa4dc60b 100644 --- a/examples/CMake/GuiApp/Main.cpp +++ b/examples/CMake/GuiApp/Main.cpp @@ -57,8 +57,8 @@ class GuiAppApplication final : public juce::JUCEApplication explicit MainWindow (juce::String name) : DocumentWindow (name, juce::Desktop::getInstance().getDefaultLookAndFeel() - .findColour (ResizableWindow::backgroundColourId), - DocumentWindow::allButtons) + .findColour (backgroundColourId), + allButtons) { setUsingNativeTitleBar (true); setContentOwned (new MainComponent(), true); @@ -78,7 +78,7 @@ class GuiAppApplication final : public juce::JUCEApplication // This is called when the user tries to close this window. Here, we'll just // ask the app to quit when this happens, but you can change this to do // whatever you need. - JUCEApplication::getInstance()->systemRequestedQuit(); + getInstance()->systemRequestedQuit(); } /* Note: Be careful if you override any DocumentWindow methods - the base diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt index 62f2831a24a7..31974123bb1f 100644 --- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt +++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt @@ -34,9 +34,9 @@ include_directories( AFTER enable_language(ASM) if(JUCE_BUILD_CONFIGURATION MATCHES "DEBUG") - add_definitions([[-DJUCE_PROJUCER_VERSION=0x80004]] [[-DJUCE_MODULE_AVAILABLE_juce_analytics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_animation=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1]] [[-DJUCE_MODULE_AVAILABLE_juce_box2d=1]] [[-DJUCE_MODULE_AVAILABLE_juce_core=1]] [[-DJUCE_MODULE_AVAILABLE_juce_cryptography=1]] [[-DJUCE_MODULE_AVAILABLE_juce_data_structures=1]] [[-DJUCE_MODULE_AVAILABLE_juce_dsp=1]] [[-DJUCE_MODULE_AVAILABLE_juce_events=1]] [[-DJUCE_MODULE_AVAILABLE_juce_graphics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1]] [[-DJUCE_MODULE_AVAILABLE_juce_javascript=1]] [[-DJUCE_MODULE_AVAILABLE_juce_opengl=1]] [[-DJUCE_MODULE_AVAILABLE_juce_osc=1]] [[-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1]] [[-DJUCE_MODULE_AVAILABLE_juce_video=1]] [[-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1]] [[-DJUCE_USE_MP3AUDIOFORMAT=1]] [[-DJUCE_PLUGINHOST_VST3=1]] [[-DJUCE_PLUGINHOST_LV2=1]] [[-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0]] [[-DJUCE_STRICT_REFCOUNTEDPOINTER=1]] [[-DJUCE_USE_CAMERA=1]] [[-DJUCE_STANDALONE_APPLICATION=1]] [[-DJUCE_DEMO_RUNNER=1]] [[-DJUCE_UNIT_TESTS=1]] [[-DJUCER_ANDROIDSTUDIO_7F0E4A25=1]] [[-DJUCE_APP_VERSION=8.0.4]] [[-DJUCE_APP_VERSION_HEX=0x80004]] [[-DDEBUG=1]] [[-D_DEBUG=1]]) + add_definitions([[-DJUCE_PROJUCER_VERSION=0x80004]] [[-DJUCE_MODULE_AVAILABLE_juce_analytics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_animation=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1]] [[-DJUCE_MODULE_AVAILABLE_juce_box2d=1]] [[-DJUCE_MODULE_AVAILABLE_juce_core=1]] [[-DJUCE_MODULE_AVAILABLE_juce_cryptography=1]] [[-DJUCE_MODULE_AVAILABLE_juce_data_structures=1]] [[-DJUCE_MODULE_AVAILABLE_juce_dsp=1]] [[-DJUCE_MODULE_AVAILABLE_juce_events=1]] [[-DJUCE_MODULE_AVAILABLE_juce_graphics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1]] [[-DJUCE_MODULE_AVAILABLE_juce_javascript=1]] [[-DJUCE_MODULE_AVAILABLE_juce_opengl=1]] [[-DJUCE_MODULE_AVAILABLE_juce_osc=1]] [[-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1]] [[-DJUCE_MODULE_AVAILABLE_juce_video=1]] [[-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1]] [[-DJUCE_USE_MP3AUDIOFORMAT=1]] [[-DJUCE_PLUGINHOST_VST3=1]] [[-DJUCE_PLUGINHOST_LV2=1]] [[-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0]] [[-DJUCE_STRICT_REFCOUNTEDPOINTER=1]] [[-DJUCE_USE_CAMERA=1]] [[-DJUCE_STANDALONE_APPLICATION=1]] [[-DJUCE_DEMO_RUNNER=1]] [[-DJUCE_UNIT_TESTS=1]] [[-DJUCE_PUSH_NOTIFICATIONS=1]] [[-DJUCER_ANDROIDSTUDIO_7F0E4A25=1]] [[-DJUCE_APP_VERSION=8.0.4]] [[-DJUCE_APP_VERSION_HEX=0x80004]] [[-DDEBUG=1]] [[-D_DEBUG=1]]) elseif(JUCE_BUILD_CONFIGURATION MATCHES "RELEASE") - add_definitions([[-DJUCE_PROJUCER_VERSION=0x80004]] [[-DJUCE_MODULE_AVAILABLE_juce_analytics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_animation=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1]] [[-DJUCE_MODULE_AVAILABLE_juce_box2d=1]] [[-DJUCE_MODULE_AVAILABLE_juce_core=1]] [[-DJUCE_MODULE_AVAILABLE_juce_cryptography=1]] [[-DJUCE_MODULE_AVAILABLE_juce_data_structures=1]] [[-DJUCE_MODULE_AVAILABLE_juce_dsp=1]] [[-DJUCE_MODULE_AVAILABLE_juce_events=1]] [[-DJUCE_MODULE_AVAILABLE_juce_graphics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1]] [[-DJUCE_MODULE_AVAILABLE_juce_javascript=1]] [[-DJUCE_MODULE_AVAILABLE_juce_opengl=1]] [[-DJUCE_MODULE_AVAILABLE_juce_osc=1]] [[-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1]] [[-DJUCE_MODULE_AVAILABLE_juce_video=1]] [[-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1]] [[-DJUCE_USE_MP3AUDIOFORMAT=1]] [[-DJUCE_PLUGINHOST_VST3=1]] [[-DJUCE_PLUGINHOST_LV2=1]] [[-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0]] [[-DJUCE_STRICT_REFCOUNTEDPOINTER=1]] [[-DJUCE_USE_CAMERA=1]] [[-DJUCE_STANDALONE_APPLICATION=1]] [[-DJUCE_DEMO_RUNNER=1]] [[-DJUCE_UNIT_TESTS=1]] [[-DJUCER_ANDROIDSTUDIO_7F0E4A25=1]] [[-DJUCE_APP_VERSION=8.0.4]] [[-DJUCE_APP_VERSION_HEX=0x80004]] [[-DNDEBUG=1]]) + add_definitions([[-DJUCE_PROJUCER_VERSION=0x80004]] [[-DJUCE_MODULE_AVAILABLE_juce_analytics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_animation=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1]] [[-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1]] [[-DJUCE_MODULE_AVAILABLE_juce_box2d=1]] [[-DJUCE_MODULE_AVAILABLE_juce_core=1]] [[-DJUCE_MODULE_AVAILABLE_juce_cryptography=1]] [[-DJUCE_MODULE_AVAILABLE_juce_data_structures=1]] [[-DJUCE_MODULE_AVAILABLE_juce_dsp=1]] [[-DJUCE_MODULE_AVAILABLE_juce_events=1]] [[-DJUCE_MODULE_AVAILABLE_juce_graphics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1]] [[-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1]] [[-DJUCE_MODULE_AVAILABLE_juce_javascript=1]] [[-DJUCE_MODULE_AVAILABLE_juce_opengl=1]] [[-DJUCE_MODULE_AVAILABLE_juce_osc=1]] [[-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1]] [[-DJUCE_MODULE_AVAILABLE_juce_video=1]] [[-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1]] [[-DJUCE_USE_MP3AUDIOFORMAT=1]] [[-DJUCE_PLUGINHOST_VST3=1]] [[-DJUCE_PLUGINHOST_LV2=1]] [[-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0]] [[-DJUCE_STRICT_REFCOUNTEDPOINTER=1]] [[-DJUCE_USE_CAMERA=1]] [[-DJUCE_STANDALONE_APPLICATION=1]] [[-DJUCE_DEMO_RUNNER=1]] [[-DJUCE_UNIT_TESTS=1]] [[-DJUCE_PUSH_NOTIFICATIONS=1]] [[-DJUCER_ANDROIDSTUDIO_7F0E4A25=1]] [[-DJUCE_APP_VERSION=8.0.4]] [[-DJUCE_APP_VERSION_HEX=0x80004]] [[-DNDEBUG=1]]) else() message( FATAL_ERROR "No matching build-configuration found." ) endif() @@ -860,6 +860,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" @@ -3455,6 +3456,7 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" diff --git a/examples/DemoRunner/Builds/Android/app/src/debug/res/raw/accessibilitynotificationicon.png b/examples/DemoRunner/Builds/Android/app/src/debug/res/raw/accessibilitynotificationicon.png new file mode 100644 index 000000000000..30b40d4d2805 Binary files /dev/null and b/examples/DemoRunner/Builds/Android/app/src/debug/res/raw/accessibilitynotificationicon.png differ diff --git a/examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml b/examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml index 062c97cbde5b..d2797d5ca22f 100644 --- a/examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml +++ b/examples/DemoRunner/Builds/Android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/examples/DemoRunner/Builds/Android/app/src/main/assets/DemoUtilities.h b/examples/DemoRunner/Builds/Android/app/src/main/assets/DemoUtilities.h index 1c5534bff731..4f1f0f26319b 100644 --- a/examples/DemoRunner/Builds/Android/app/src/main/assets/DemoUtilities.h +++ b/examples/DemoRunner/Builds/Android/app/src/main/assets/DemoUtilities.h @@ -63,7 +63,7 @@ inline File getExamplesDirectory() noexcept return File { CharPointer_UTF8 { PIP_JUCE_EXAMPLES_DIRECTORY_STRING } }; #else auto currentFile = File::getSpecialLocation (File::SpecialLocationType::currentApplicationFile); - auto exampleDir = currentFile.getParentDirectory().getChildFile ("examples"); + auto exampleDir = currentFile.getSiblingFile ("examples"); if (exampleDir.exists()) return exampleDir; @@ -109,10 +109,10 @@ inline std::unique_ptr createAssetInputStream (const char* resource #else #if JUCE_IOS auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getChildFile ("Assets"); + .getSiblingFile ("Assets"); #elif JUCE_MAC auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getParentDirectory().getChildFile ("Resources").getChildFile ("Assets"); + .getParentDirectory().getSiblingFile ("Resources").getChildFile ("Assets"); if (! assetsDir.exists()) assetsDir = getExamplesDirectory().getChildFile ("Assets"); diff --git a/examples/DemoRunner/Builds/Android/app/src/release/res/raw/accessibilitynotificationicon.png b/examples/DemoRunner/Builds/Android/app/src/release/res/raw/accessibilitynotificationicon.png new file mode 100644 index 000000000000..30b40d4d2805 Binary files /dev/null and b/examples/DemoRunner/Builds/Android/app/src/release/res/raw/accessibilitynotificationicon.png differ diff --git a/examples/DemoRunner/Builds/LinuxMakefile/Makefile b/examples/DemoRunner/Builds/LinuxMakefile/Makefile index c8835813dc92..6af1ed19b938 100644 --- a/examples/DemoRunner/Builds/LinuxMakefile/Makefile +++ b/examples/DemoRunner/Builds/LinuxMakefile/Makefile @@ -39,7 +39,7 @@ ifeq ($(CONFIG),Debug) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DJUCE_PROJUCER_VERSION=0x80004" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_animation=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_box2d=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_javascript=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_MODULE_AVAILABLE_juce_video=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_USE_MP3AUDIOFORMAT=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_USE_CAMERA=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=8.0.4" "-DJUCE_APP_VERSION_HEX=0x80004" $(shell $(PKG_CONFIG) --cflags $(shell ($(PKG_CONFIG) --exists webkit2gtk-4.1 && echo webkit2gtk-4.1) || echo webkit2gtk-4.0) alsa freetype2 fontconfig gl libcurl gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DDEBUG=1" "-D_DEBUG=1" "-DJUCE_PROJUCER_VERSION=0x80004" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_animation=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_box2d=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_javascript=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_MODULE_AVAILABLE_juce_video=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_USE_MP3AUDIOFORMAT=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_USE_CAMERA=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=8.0.4" "-DJUCE_APP_VERSION_HEX=0x80004" $(shell $(PKG_CONFIG) --cflags $(shell ($(PKG_CONFIG) --exists webkit2gtk-4.1 && echo webkit2gtk-4.1) || echo webkit2gtk-4.0) alsa freetype2 fontconfig gl libcurl gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) JUCE_CPPFLAGS_APP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_TARGET_APP := DemoRunner @@ -60,7 +60,7 @@ ifeq ($(CONFIG),Release) TARGET_ARCH := endif - JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DJUCE_PROJUCER_VERSION=0x80004" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_animation=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_box2d=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_javascript=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_MODULE_AVAILABLE_juce_video=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_USE_MP3AUDIOFORMAT=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_USE_CAMERA=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=8.0.4" "-DJUCE_APP_VERSION_HEX=0x80004" $(shell $(PKG_CONFIG) --cflags $(shell ($(PKG_CONFIG) --exists webkit2gtk-4.1 && echo webkit2gtk-4.1) || echo webkit2gtk-4.0) alsa freetype2 fontconfig gl libcurl gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) + JUCE_CPPFLAGS := $(DEPFLAGS) "-DLINUX=1" "-DNDEBUG=1" "-DJUCE_PROJUCER_VERSION=0x80004" "-DJUCE_MODULE_AVAILABLE_juce_analytics=1" "-DJUCE_MODULE_AVAILABLE_juce_animation=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_devices=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_formats=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_processors=1" "-DJUCE_MODULE_AVAILABLE_juce_audio_utils=1" "-DJUCE_MODULE_AVAILABLE_juce_box2d=1" "-DJUCE_MODULE_AVAILABLE_juce_core=1" "-DJUCE_MODULE_AVAILABLE_juce_cryptography=1" "-DJUCE_MODULE_AVAILABLE_juce_data_structures=1" "-DJUCE_MODULE_AVAILABLE_juce_dsp=1" "-DJUCE_MODULE_AVAILABLE_juce_events=1" "-DJUCE_MODULE_AVAILABLE_juce_graphics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_basics=1" "-DJUCE_MODULE_AVAILABLE_juce_gui_extra=1" "-DJUCE_MODULE_AVAILABLE_juce_javascript=1" "-DJUCE_MODULE_AVAILABLE_juce_opengl=1" "-DJUCE_MODULE_AVAILABLE_juce_osc=1" "-DJUCE_MODULE_AVAILABLE_juce_product_unlocking=1" "-DJUCE_MODULE_AVAILABLE_juce_video=1" "-DJUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1" "-DJUCE_USE_MP3AUDIOFORMAT=1" "-DJUCE_PLUGINHOST_VST3=1" "-DJUCE_PLUGINHOST_LV2=1" "-DJUCE_ALLOW_STATIC_NULL_VARIABLES=0" "-DJUCE_STRICT_REFCOUNTEDPOINTER=1" "-DJUCE_USE_CAMERA=1" "-DJUCE_STANDALONE_APPLICATION=1" "-DJUCE_DEMO_RUNNER=1" "-DJUCE_UNIT_TESTS=1" "-DJUCE_PUSH_NOTIFICATIONS=1" "-DJUCER_LINUX_MAKE_6D53C8B4=1" "-DJUCE_APP_VERSION=8.0.4" "-DJUCE_APP_VERSION_HEX=0x80004" $(shell $(PKG_CONFIG) --cflags $(shell ($(PKG_CONFIG) --exists webkit2gtk-4.1 && echo webkit2gtk-4.1) || echo webkit2gtk-4.0) alsa freetype2 fontconfig gl libcurl gtk+-x11-3.0) -pthread -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lilv -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sratom -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord/src -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/sord -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/serd -I../../../../modules/juce_audio_processors/format_types/LV2_SDK/lv2 -I../../../../modules/juce_audio_processors/format_types/LV2_SDK -I../../../../modules/juce_audio_processors/format_types/VST3_SDK -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) JUCE_CPPFLAGS_APP := "-DJucePlugin_Build_VST=0" "-DJucePlugin_Build_VST3=0" "-DJucePlugin_Build_AU=0" "-DJucePlugin_Build_AUv3=0" "-DJucePlugin_Build_AAX=0" "-DJucePlugin_Build_Standalone=0" "-DJucePlugin_Build_Unity=0" "-DJucePlugin_Build_LV2=0" JUCE_TARGET_APP := DemoRunner diff --git a/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj b/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj index 2c171167a1c3..2ee18cadcbcb 100644 --- a/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj +++ b/examples/DemoRunner/Builds/MacOSX/DemoRunner.xcodeproj/project.pbxproj @@ -577,6 +577,7 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_DEMO_RUNNER=1", "JUCE_UNIT_TESTS=1", + "JUCE_PUSH_NOTIFICATIONS=1", "JUCE_SILENCE_XCODE_15_LINKER_WARNING=1", "JUCER_XCODE_MAC_F6D2F4CF=1", "JUCE_APP_VERSION=8.0.4", @@ -669,6 +670,7 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_DEMO_RUNNER=1", "JUCE_UNIT_TESTS=1", + "JUCE_PUSH_NOTIFICATIONS=1", "JUCE_SILENCE_XCODE_15_LINKER_WARNING=1", "JUCER_XCODE_MAC_F6D2F4CF=1", "JUCE_APP_VERSION=8.0.4", diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj index b3c028a00893..6e97f627e6c1 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ DemoRunner true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ DemoRunner true + true + true @@ -64,7 +68,7 @@ Disabled ProgramDatabase ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -79,7 +83,7 @@ ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) $(OutDir)\DemoRunner.exe @@ -107,7 +111,7 @@ Full ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing @@ -122,7 +126,7 @@ ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2019_78A5026=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) $(OutDir)\DemoRunner.exe @@ -1091,6 +1095,9 @@ true + + true + true diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters index 6f25f4f2bbfb..25877052c308 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters @@ -1804,6 +1804,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj index 1145aa1b20e4..4be01a129034 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ DemoRunner true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ DemoRunner true + true + true @@ -64,7 +68,7 @@ Disabled ProgramDatabase ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) MultiThreadedDebugDLL true NotUsing @@ -79,7 +83,7 @@ ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) $(OutDir)\DemoRunner.exe @@ -107,7 +111,7 @@ Full ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) MultiThreaded true NotUsing @@ -122,7 +126,7 @@ ..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lilv;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sratom;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord\src;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\sord;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\serd;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK\lv2;..\..\..\..\modules\juce_audio_processors\format_types\LV2_SDK;..\..\..\..\modules\juce_audio_processors\format_types\VST3_SDK;..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) + _CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCE_PROJUCER_VERSION=0x80004;JUCE_MODULE_AVAILABLE_juce_analytics=1;JUCE_MODULE_AVAILABLE_juce_animation=1;JUCE_MODULE_AVAILABLE_juce_audio_basics=1;JUCE_MODULE_AVAILABLE_juce_audio_devices=1;JUCE_MODULE_AVAILABLE_juce_audio_formats=1;JUCE_MODULE_AVAILABLE_juce_audio_processors=1;JUCE_MODULE_AVAILABLE_juce_audio_utils=1;JUCE_MODULE_AVAILABLE_juce_box2d=1;JUCE_MODULE_AVAILABLE_juce_core=1;JUCE_MODULE_AVAILABLE_juce_cryptography=1;JUCE_MODULE_AVAILABLE_juce_data_structures=1;JUCE_MODULE_AVAILABLE_juce_dsp=1;JUCE_MODULE_AVAILABLE_juce_events=1;JUCE_MODULE_AVAILABLE_juce_graphics=1;JUCE_MODULE_AVAILABLE_juce_gui_basics=1;JUCE_MODULE_AVAILABLE_juce_gui_extra=1;JUCE_MODULE_AVAILABLE_juce_javascript=1;JUCE_MODULE_AVAILABLE_juce_opengl=1;JUCE_MODULE_AVAILABLE_juce_osc=1;JUCE_MODULE_AVAILABLE_juce_product_unlocking=1;JUCE_MODULE_AVAILABLE_juce_video=1;JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1;JUCE_USE_MP3AUDIOFORMAT=1;JUCE_PLUGINHOST_VST3=1;JUCE_PLUGINHOST_LV2=1;JUCE_ALLOW_STATIC_NULL_VARIABLES=0;JUCE_STRICT_REFCOUNTEDPOINTER=1;JUCE_USE_CAMERA=1;JUCE_STANDALONE_APPLICATION=1;JUCE_DEMO_RUNNER=1;JUCE_UNIT_TESTS=1;JUCE_PUSH_NOTIFICATIONS=1;JUCER_VS2022_78A503E=1;JUCE_APP_VERSION=8.0.4;JUCE_APP_VERSION_HEX=0x80004;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;JucePlugin_Build_Unity=0;JucePlugin_Build_LV2=0;%(PreprocessorDefinitions) $(OutDir)\DemoRunner.exe @@ -1091,6 +1095,9 @@ true + + true + true diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters index 730cd91e51a3..c1ba09e9889f 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters @@ -1804,6 +1804,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj b/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj index 5221c5e8cf0c..6490429422df 100644 --- a/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj +++ b/examples/DemoRunner/Builds/iOS/DemoRunner.xcodeproj/project.pbxproj @@ -588,6 +588,7 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_DEMO_RUNNER=1", "JUCE_UNIT_TESTS=1", + "JUCE_PUSH_NOTIFICATIONS=1", "JUCE_SILENCE_XCODE_15_LINKER_WARNING=1", "JUCER_XCODE_IPHONE_5BC26AE3=1", "JUCE_APP_VERSION=8.0.4", @@ -680,6 +681,7 @@ "JUCE_STANDALONE_APPLICATION=1", "JUCE_DEMO_RUNNER=1", "JUCE_UNIT_TESTS=1", + "JUCE_PUSH_NOTIFICATIONS=1", "JUCE_SILENCE_XCODE_15_LINKER_WARNING=1", "JUCER_XCODE_IPHONE_5BC26AE3=1", "JUCE_APP_VERSION=8.0.4", diff --git a/examples/DemoRunner/CMakeLists.txt b/examples/DemoRunner/CMakeLists.txt index 2c83f6ba15d4..247b27a969b5 100644 --- a/examples/DemoRunner/CMakeLists.txt +++ b/examples/DemoRunner/CMakeLists.txt @@ -56,6 +56,7 @@ target_compile_definitions(DemoRunner PRIVATE JUCE_DEMO_RUNNER=1 JUCE_PLUGINHOST_LV2=1 JUCE_PLUGINHOST_VST3=1 + JUCE_PUSH_NOTIFICATIONS=1 JUCE_STRICT_REFCOUNTEDPOINTER=1 JUCE_UNIT_TESTS=1 JUCE_USE_CAMERA=1 diff --git a/examples/DemoRunner/DemoRunner.jucer b/examples/DemoRunner/DemoRunner.jucer index 6f9fe0f2fe8c..665981048aa2 100644 --- a/examples/DemoRunner/DemoRunner.jucer +++ b/examples/DemoRunner/DemoRunner.jucer @@ -1,6 +1,6 @@ - - - + + diff --git a/examples/DemoRunner/Source/Demos/JUCEDemos.cpp b/examples/DemoRunner/Source/Demos/JUCEDemos.cpp index e6b42fad4d9d..506c27da8ce3 100644 --- a/examples/DemoRunner/Source/Demos/JUCEDemos.cpp +++ b/examples/DemoRunner/Source/Demos/JUCEDemos.cpp @@ -50,7 +50,7 @@ void JUCEDemos::registerDemo (std::function constructorCallback, c { #if JUCE_MAC auto f = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getParentDirectory().getChildFile ("Resources"); + .getParentDirectory().getSiblingFile ("Resources"); #else auto f = findExamplesDirectoryFromExecutable (File::getSpecialLocation (File::currentApplicationFile)); #endif @@ -69,7 +69,7 @@ void JUCEDemos::registerDemo (std::function constructorCallback, c File JUCEDemos::findExamplesDirectoryFromExecutable (File exec) { int numTries = 15; - auto exampleDir = exec.getParentDirectory().getChildFile ("examples"); + auto exampleDir = exec.getSiblingFile ("examples"); if (exampleDir.exists()) return exampleDir; diff --git a/examples/DemoRunner/Source/accessibilitynotificationicon.png b/examples/DemoRunner/Source/accessibilitynotificationicon.png new file mode 100644 index 000000000000..30b40d4d2805 Binary files /dev/null and b/examples/DemoRunner/Source/accessibilitynotificationicon.png differ diff --git a/examples/GUI/AccessibilityDemo.h b/examples/GUI/AccessibilityDemo.h index 6b3925f9b410..caaf6a7e5cd1 100644 --- a/examples/GUI/AccessibilityDemo.h +++ b/examples/GUI/AccessibilityDemo.h @@ -35,10 +35,11 @@ platforms. dependencies: juce_core, juce_data_structures, juce_events, juce_graphics, - juce_gui_basics + juce_gui_basics, juce_gui_extra exporters: xcode_mac, vs2022, androidstudio, xcode_iphone moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1 + JUCE_PUSH_NOTIFICATIONS=1 type: Component mainClass: AccessibilityDemo @@ -1356,86 +1357,172 @@ class CustomNavigationComponent final : public Component //============================================================================== /** - The top-level component containing an example of how to post system announcements. + The top-level component containing an example of how to post system announcements + and notifications. The AccessibilityHandler::postAnnouncement() method will post some text to the native screen reader application to be read out along with a priority determining how it should be read out (whether it should interrupt other announcements, etc.). + + The AccessibilityHandler::postSystemNotification() method will post a system + notification to the OS via the push notification client on macOS, Android, and iOS + and the system tray component on Windows. */ -class AnnouncementsComponent final : public Component +class AnnouncementsAndNotificationsComponent final : public Component { public: - AnnouncementsComponent() + AnnouncementsAndNotificationsComponent() { + setTitle ("Announcements and Notifications"); + setDescription ("A demo of posting system announcements and notifications."); + setFocusContainerType (FocusContainerType::focusContainer); + addAndMakeVisible (descriptionLabel); - textEntryBox.setMultiLine (true); - textEntryBox.setReturnKeyStartsNewLine (true); - textEntryBox.setText ("Announcement text."); - addAndMakeVisible (textEntryBox); + addAndMakeVisible (announcements); + addAndMakeVisible (notifications); + } + + void resized() override + { + Grid grid; + + grid.templateRows = { Grid::TrackInfo (Grid::Fr (1)), Grid::TrackInfo (Grid::Fr (3)), Grid::TrackInfo (Grid::Fr (3)) }; + grid.templateColumns = { Grid::TrackInfo (Grid::Fr (1)) }; - priorityComboBox.addItemList ({ "Priority - Low", "Priority - Medium", "Priority - High" }, 1); - priorityComboBox.setSelectedId (2); - addAndMakeVisible (priorityComboBox); + grid.items = { GridItem (descriptionLabel).withMargin ({ 2 }), + GridItem (announcements).withMargin ({ 2 }), + GridItem (notifications).withMargin ({ 2 }) }; - announceButton.onClick = [this] + grid.performLayout (getLocalBounds()); + } + +private: + struct AnnouncementsComponent : public Component + { + AnnouncementsComponent() { - auto priority = [this] + textEntryBox.setMultiLine (true); + textEntryBox.setReturnKeyStartsNewLine (true); + textEntryBox.setText ("Announcement text."); + addAndMakeVisible (textEntryBox); + + priorityComboBox.addItemList ({ "Priority - Low", "Priority - Medium", "Priority - High" }, 1); + priorityComboBox.setSelectedId (2); + addAndMakeVisible (priorityComboBox); + + announceButton.onClick = [this] { - switch (priorityComboBox.getSelectedId()) + auto priority = [this] { - case 1: return AccessibilityHandler::AnnouncementPriority::low; - case 2: return AccessibilityHandler::AnnouncementPriority::medium; - case 3: return AccessibilityHandler::AnnouncementPriority::high; - } + switch (priorityComboBox.getSelectedId()) + { + case 1: return AccessibilityHandler::AnnouncementPriority::low; + case 2: return AccessibilityHandler::AnnouncementPriority::medium; + case 3: return AccessibilityHandler::AnnouncementPriority::high; + } - jassertfalse; - return AccessibilityHandler::AnnouncementPriority::medium; - }(); + jassertfalse; + return AccessibilityHandler::AnnouncementPriority::medium; + }(); - AccessibilityHandler::postAnnouncement (textEntryBox.getText(), priority); - }; + AccessibilityHandler::postAnnouncement (textEntryBox.getText(), priority); + }; - addAndMakeVisible (announceButton); + addAndMakeVisible (announceButton); + } - setTitle ("Announcements"); - setHelpText ("Type some text into the box and click the announce button to have it read out."); - setFocusContainerType (FocusContainerType::focusContainer); - } + void resized() override + { + Grid grid; - void resized() override + grid.templateRows = { Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)) }; + + grid.templateColumns = { Grid::TrackInfo (Grid::Fr (3)), + Grid::TrackInfo (Grid::Fr (2)) }; + + grid.items = { GridItem (textEntryBox).withMargin (2).withArea ({ 1 }, { 1 }, { 4 }, { 2 }), + GridItem (priorityComboBox).withMargin (2).withArea ({ 4 }, { 1 }, { 5 }, { 2 }), + GridItem (announceButton).withMargin (2).withArea ({ 3 }, { 2 }, { 4 }, { 3 }) }; + + grid.performLayout (getLocalBounds()); + } + + TextEditor textEntryBox; + ComboBox priorityComboBox; + TextButton announceButton { "Announce" }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnnouncementsComponent) + }; + + struct NotificationsComponent : public Component { - Grid grid; + NotificationsComponent() + { + titleBox.setText ("Notification title."); + addAndMakeVisible (titleBox); - grid.templateRows = { Grid::TrackInfo (Grid::Fr (3)), - Grid::TrackInfo (Grid::Fr (1)), - Grid::TrackInfo (Grid::Fr (1)), - Grid::TrackInfo (Grid::Fr (1)), - Grid::TrackInfo (Grid::Fr (1)), - Grid::TrackInfo (Grid::Fr (1)) }; + descriptionBox.setMultiLine (true); + descriptionBox.setReturnKeyStartsNewLine (true); + descriptionBox.setText ("Notification body."); + addAndMakeVisible (descriptionBox); - grid.templateColumns = { Grid::TrackInfo (Grid::Fr (3)), - Grid::TrackInfo (Grid::Fr (2)) }; + postButton.onClick = [this] + { + AccessibilityHandler::postSystemNotification (titleBox.getText(), + descriptionBox.getText()); + }; - grid.items = { GridItem (descriptionLabel).withMargin (2).withColumn ({ GridItem::Span (2), {} }), - GridItem (textEntryBox).withMargin (2).withArea ({ 2 }, { 1 }, { 5 }, { 2 }), - GridItem (priorityComboBox).withMargin (2).withArea ({ 5 }, { 1 }, { 6 }, { 2 }), - GridItem (announceButton).withMargin (2).withArea ({ 4 }, { 2 }, { 5 }, { 3 }) }; + addAndMakeVisible (postButton); + } - grid.performLayout (getLocalBounds()); - } + void resized() override + { + Grid grid; -private: - Label descriptionLabel { {}, "This is a demo of posting system announcements that will be read out by an accessibility client.\n\n" - "You can enter some text to be read out in the text box below, set a priority for the message and then " - "post it using the \"Announce\" button." }; + grid.templateRows = { Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)), + Grid::TrackInfo (Grid::Fr (1)) }; + + grid.templateColumns = { Grid::TrackInfo (Grid::Fr (3)), + Grid::TrackInfo (Grid::Fr (2)) }; + + grid.items = { GridItem (titleBox).withMargin (2).withArea ({ 1 }, { 1 }, { 2 }, { 2 }), + GridItem (descriptionBox).withMargin (2).withArea ({ 2 }, { 1 }, { 5 }, { 2 }), + GridItem (postButton).withMargin (2).withArea ({ 3 }, { 2 }, { 4 }, { 3 }) }; + + grid.performLayout (getLocalBounds()); + } + + TextEditor titleBox, descriptionBox; + TextButton postButton { "Post" }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NotificationsComponent) + }; - TextEditor textEntryBox; - ComboBox priorityComboBox; - TextButton announceButton { "Announce" }; + Label descriptionLabel { {}, "This is a demo of posting system announcements and notifications.\n\n" + "The \"Announcements\" section will post an announcement to be read out by the screen reader client.\n" + "The \"Notifications\" section will post a system notification to the OS.\n" }; + + AnnouncementsComponent announcementsComponent; + NotificationsComponent notificationsComponent; + + ContentComponent announcements { "Announcements", + "Type some text into the box and click the announce button to have it read out.", + announcementsComponent }; + ContentComponent notifications { "Notifications", + "Fill out the notification title and description fields and click the post button " + "to post it to the system.", + notificationsComponent }; //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnnouncementsComponent) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnnouncementsAndNotificationsComponent) }; //============================================================================== @@ -1457,10 +1544,10 @@ class AccessibilityDemo final : public Component const auto tabColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId).darker (0.1f); - tabs.addTab ("JUCE Widgets", tabColour, &juceWidgetsComponent, false); - tabs.addTab ("Custom Widget", tabColour, &customWidgetComponent, false); - tabs.addTab ("Custom Navigation", tabColour, &customNavigationComponent, false); - tabs.addTab ("Announcements", tabColour, &announcementsComponent, false); + tabs.addTab ("JUCE Widgets", tabColour, &juceWidgetsComponent, false); + tabs.addTab ("Custom Widget", tabColour, &customWidgetComponent, false); + tabs.addTab ("Custom Navigation", tabColour, &customNavigationComponent, false); + tabs.addTab ("Announcements and Notifications", tabColour, &announcementsAndNotificationsComponent, false); addAndMakeVisible (tabs); setSize (800, 600); @@ -1484,7 +1571,7 @@ class AccessibilityDemo final : public Component JUCEWidgetsComponent juceWidgetsComponent; CustomWidgetComponent customWidgetComponent; CustomNavigationComponent customNavigationComponent; - AnnouncementsComponent announcementsComponent; + AnnouncementsAndNotificationsComponent announcementsAndNotificationsComponent; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityDemo) diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt index 34171f6197db..f9d6992c0349 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt @@ -815,6 +815,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" @@ -3070,6 +3071,7 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" diff --git a/extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml b/extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml index 5b9c0d726613..f499caa9f638 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml +++ b/extras/AudioPerformanceTest/Builds/Android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj index 1b6285e6c5bc..e64a688c9cc1 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ AudioPerformanceTest true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ AudioPerformanceTest true + true + true @@ -1051,6 +1055,9 @@ true + + true + true diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters index 7a475a3df1e6..b05a019507d4 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters @@ -1594,6 +1594,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt index 35e7501e7e20..a88bfc6b68f1 100644 --- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt @@ -848,6 +848,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" @@ -3256,6 +3257,7 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" diff --git a/extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml b/extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml index f99a0ecc2c28..7492a37f5bbb 100644 --- a/extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml +++ b/extras/AudioPluginHost/Builds/Android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/extras/AudioPluginHost/Builds/Android/app/src/main/assets/DemoUtilities.h b/extras/AudioPluginHost/Builds/Android/app/src/main/assets/DemoUtilities.h index 1c5534bff731..4f1f0f26319b 100644 --- a/extras/AudioPluginHost/Builds/Android/app/src/main/assets/DemoUtilities.h +++ b/extras/AudioPluginHost/Builds/Android/app/src/main/assets/DemoUtilities.h @@ -63,7 +63,7 @@ inline File getExamplesDirectory() noexcept return File { CharPointer_UTF8 { PIP_JUCE_EXAMPLES_DIRECTORY_STRING } }; #else auto currentFile = File::getSpecialLocation (File::SpecialLocationType::currentApplicationFile); - auto exampleDir = currentFile.getParentDirectory().getChildFile ("examples"); + auto exampleDir = currentFile.getSiblingFile ("examples"); if (exampleDir.exists()) return exampleDir; @@ -109,10 +109,10 @@ inline std::unique_ptr createAssetInputStream (const char* resource #else #if JUCE_IOS auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getChildFile ("Assets"); + .getSiblingFile ("Assets"); #elif JUCE_MAC auto assetsDir = File::getSpecialLocation (File::currentExecutableFile) - .getParentDirectory().getParentDirectory().getChildFile ("Resources").getChildFile ("Assets"); + .getParentDirectory().getSiblingFile ("Resources").getChildFile ("Assets"); if (! assetsDir.exists()) assetsDir = getExamplesDirectory().getChildFile ("Assets"); diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj index 6c59173e0218..c37d716ce5a0 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ AudioPluginHost true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ AudioPluginHost true + true + true @@ -1059,6 +1063,9 @@ true + + true + true diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters index c5886faeb6d0..957d187b47bf 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters @@ -1669,6 +1669,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj index d739b7def32b..b28adb205918 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ AudioPluginHost true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ AudioPluginHost true + true + true @@ -1059,6 +1063,9 @@ true + + true + true diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters index aad4602684ea..5e4d2e64e9e1 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters @@ -1669,6 +1669,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj index 93da43c13a24..366bc40fbe8a 100644 --- a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj +++ b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\ConsoleApp\ BinaryBuilder true + true + true $(SolutionDir)$(Platform)\$(Configuration)\ConsoleApp\ $(Platform)\$(Configuration)\ConsoleApp\ BinaryBuilder true + true + true diff --git a/extras/Build/CMake/JUCEConfig.cmake.in b/extras/Build/CMake/JUCEConfig.cmake.in index c0c1eef2ddef..03a09efb4af7 100644 --- a/extras/Build/CMake/JUCEConfig.cmake.in +++ b/extras/Build/CMake/JUCEConfig.cmake.in @@ -39,6 +39,7 @@ include("@PACKAGE_UTILS_INSTALL_DIR@/JUCEUtils.cmake") set(_juce_modules juce_analytics + juce_animation juce_audio_basics juce_audio_devices juce_audio_formats @@ -55,6 +56,7 @@ set(_juce_modules juce_gui_basics juce_gui_extra juce_javascript + juce_midi_ci juce_opengl juce_osc juce_product_unlocking diff --git a/extras/Build/CMake/JUCEHelperTargets.cmake b/extras/Build/CMake/JUCEHelperTargets.cmake index 244cccec7101..051296daea7c 100644 --- a/extras/Build/CMake/JUCEHelperTargets.cmake +++ b/extras/Build/CMake/JUCEHelperTargets.cmake @@ -140,9 +140,9 @@ if((CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") OR (CMAKE_CXX_COMPILER_FRONTEND_VARIA $<$:$,-GL,-flto>>) target_link_libraries(juce_recommended_lto_flags INTERFACE $<$:$<$:-LTCG>>) -elseif((NOT MINGW) AND ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") - OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))) +elseif((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")) target_compile_options(juce_recommended_lto_flags INTERFACE $<$:-flto>) target_link_libraries(juce_recommended_lto_flags INTERFACE $<$:-flto>) # Xcode 15.0 requires this flag to avoid a compiler bug diff --git a/extras/Build/CMake/JUCEModuleSupport.cmake b/extras/Build/CMake/JUCEModuleSupport.cmake index 0d93d36033a6..03378a2090bf 100644 --- a/extras/Build/CMake/JUCEModuleSupport.cmake +++ b/extras/Build/CMake/JUCEModuleSupport.cmake @@ -68,9 +68,7 @@ endfunction() if((CMAKE_SYSTEM_NAME STREQUAL "Windows") OR (CMAKE_SYSTEM_NAME STREQUAL "Linux") - OR (CMAKE_SYSTEM_NAME MATCHES ".*BSD") - OR MSYS - OR MINGW) + OR (CMAKE_SYSTEM_NAME MATCHES ".*BSD")) # If you really need to override the detected arch for some reason, # you can configure the build with -DJUCE_TARGET_ARCHITECTURE= if(NOT DEFINED JUCE_TARGET_ARCHITECTURE) @@ -654,12 +652,6 @@ function(juce_add_module module_path) endif() _juce_link_libs_from_metadata("${module_name}" "${metadata_dict}" windowsLibs) - elseif(MSYS OR MINGW) - if(module_name STREQUAL "juce_gui_basics") - target_compile_options(${module_name} INTERFACE "-Wa,-mbig-obj") - endif() - - _juce_link_libs_from_metadata("${module_name}" "${metadata_dict}" mingwLibs) endif() endif() diff --git a/extras/Build/CMake/JUCEUtils.cmake b/extras/Build/CMake/JUCEUtils.cmake index 8a6b0d6e1528..8daccbaa5f16 100644 --- a/extras/Build/CMake/JUCEUtils.cmake +++ b/extras/Build/CMake/JUCEUtils.cmake @@ -1051,10 +1051,6 @@ function(_juce_add_vst3_manifest_helper_target) target_compile_options(juce_vst3_helper PRIVATE -fobjc-arc) endif() - if(MSYS OR MINGW) - target_link_options(juce_vst3_helper PRIVATE -municode) - endif() - set_target_properties(juce_vst3_helper PROPERTIES BUILD_WITH_INSTALL_RPATH ON) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -1083,11 +1079,6 @@ function(juce_enable_vst3_manifest_step shared_code_target) "juce_enable_copy_plugin_step too.") endif() - if((MSYS OR MINGW) AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) - message(WARNING "VST3 manifest generation is disabled for ${shared_code_target} because the compiler is not supported.") - return() - endif() - if(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND NOT JUCE_WINDOWS_HELPERS_CAN_RUN) message(WARNING "VST3 manifest generation is disabled for ${shared_code_target} because a " "${JUCE_TARGET_ARCHITECTURE} manifest helper cannot run on a host system processor detected to be " diff --git a/extras/Build/juceaide/CMakeLists.txt b/extras/Build/juceaide/CMakeLists.txt index 7ef20eddf165..8f2f6be8c881 100644 --- a/extras/Build/juceaide/CMakeLists.txt +++ b/extras/Build/juceaide/CMakeLists.txt @@ -111,6 +111,20 @@ else() set(ENV{CMAKE_GENERATOR_PLATFORM} "${CMAKE_HOST_SYSTEM_PROCESSOR}") endif() + set(PASSTHROUGH_ARGS "") + + if(CMAKE_OSX_DEPLOYMENT_TARGET) + list(APPEND PASSTHROUGH_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + + if(CMAKE_C_COMPILER_LAUNCHER) + list(APPEND PASSTHROUGH_ARGS "-DCMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER}") + endif() + + if(CMAKE_CXX_COMPILER_LAUNCHER) + list(APPEND PASSTHROUGH_ARGS "-DCMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER}") + endif() + # Looks like we're bootstrapping, reinvoke CMake execute_process(COMMAND "${CMAKE_COMMAND}" "." @@ -120,6 +134,7 @@ else() "-DCMAKE_BUILD_TYPE=Debug" "-DJUCE_BUILD_HELPER_TOOLS=ON" "-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}" + ${PASSTHROUGH_ARGS} WORKING_DIRECTORY "${JUCE_SOURCE_DIR}" OUTPUT_VARIABLE command_output ERROR_VARIABLE command_output diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt index e46ad7a010e2..be29f69e1791 100644 --- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt +++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt @@ -819,6 +819,7 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" @@ -3154,6 +3155,7 @@ set_source_files_properties( "../../../../../modules/juce_audio_processors/utilities/juce_RangedAudioParameter.h" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST2ClientExtensions.h" + "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp" "../../../../../modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h" "../../../../../modules/juce_audio_processors/juce_audio_processors.cpp" "../../../../../modules/juce_audio_processors/juce_audio_processors.mm" diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml b/extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml index 7c000ddb7acb..a34771de6d83 100644 --- a/extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml +++ b/extras/NetworkGraphicsDemo/Builds/Android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ + diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj index 8e795be0f107..56d8b7707e2a 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ JUCE Network Graphics Demo true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ JUCE Network Graphics Demo true + true + true @@ -1051,6 +1055,9 @@ true + + true + true diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters index f3ddc3d53629..b954c7a5de13 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters @@ -1624,6 +1624,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj index fa6b37df6492..f044782ebf69 100644 --- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ Projucer true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ Projucer true + true + true diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj index 874aefdc5be2..04670029a0ec 100644 --- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\App\ Projucer true + true + true $(SolutionDir)$(Platform)\$(Configuration)\App\ $(Platform)\$(Configuration)\App\ Projucer true + true + true diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index 84fc56c56370..96d60a31de21 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -59,6 +59,8 @@ namespace ProjectMessages DECLARE_ID (pluginCodeInvalid); DECLARE_ID (manufacturerCodeInvalid); DECLARE_ID (deprecatedExporter); + DECLARE_ID (unsupportedArm32Config); + DECLARE_ID (arm64Warning); DECLARE_ID (notification); DECLARE_ID (warning); @@ -73,7 +75,7 @@ namespace ProjectMessages static Identifier warnings[] = { Ids::cppStandard, Ids::moduleNotFound, Ids::jucePath, Ids::jucerFileModified, Ids::missingModuleDependencies, Ids::oldProjucer, Ids::pluginCodeInvalid, Ids::manufacturerCodeInvalid, - Ids::deprecatedExporter }; + Ids::deprecatedExporter, Ids::unsupportedArm32Config, Ids::arm64Warning }; if (std::find (std::begin (warnings), std::end (warnings), message) != std::end (warnings)) return Ids::warning; @@ -99,6 +101,8 @@ namespace ProjectMessages if (message == Ids::pluginCodeInvalid) return "Invalid Plugin Code"; if (message == Ids::manufacturerCodeInvalid) return "Invalid Manufacturer Code"; if (message == Ids::deprecatedExporter) return "Deprecated Exporter"; + if (message == Ids::unsupportedArm32Config) return "Unsupported Architecture"; + if (message == Ids::arm64Warning) return "Prefer arm64ec over arm64"; jassertfalse; return {}; @@ -116,6 +120,8 @@ namespace ProjectMessages if (message == Ids::pluginCodeInvalid) return "The plugin code should be exactly four characters in length."; if (message == Ids::manufacturerCodeInvalid) return "The manufacturer code should be exactly four characters in length."; if (message == Ids::deprecatedExporter) return "The project includes a deprecated exporter."; + if (message == Ids::unsupportedArm32Config) return "The project includes a Visual Studio configuration that uses the 32-bit Arm architecture, which is no longer supported. This configuration has been hidden, and will be removed on save."; + if (message == Ids::arm64Warning) return "For software where interoperability is a concern (such as plugins and hosts), arm64ec will provide the best compatibility with existing x64 software"; jassertfalse; return {}; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h index 65d56b87fce0..8667a254008a 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h @@ -244,7 +244,7 @@ class AndroidProjectExporter final : public ProjectExporter if (androidExtraAssetsFolderValue.isNotEmpty()) { - auto extraAssets = getProject().getFile().getParentDirectory().getChildFile (androidExtraAssetsFolderValue); + auto extraAssets = getProject().getFile().getSiblingFile (androidExtraAssetsFolderValue); if (extraAssets.exists() && extraAssets.isDirectory()) { @@ -1284,7 +1284,7 @@ class AndroidProjectExporter final : public ProjectExporter if (remoteNotifsConfigFilePath.isEmpty()) remoteNotifsConfigFilePath = androidRemoteNotificationsConfigFile.get().toString(); - File file (getProject().getFile().getChildFile (remoteNotifsConfigFilePath)); + File file (getProject().getFile().getSiblingFile (remoteNotifsConfigFilePath)); // Settings file must be present for remote notifications to work and it must be called google-services.json. jassert (file.existsAsFile() && file.getFileName() == "google-services.json"); @@ -1303,7 +1303,7 @@ class AndroidProjectExporter final : public ProjectExporter for (auto& path : resourcePaths) { - auto file = getProject().getFile().getChildFile (path); + auto file = getProject().getFile().getSiblingFile (path); jassert (file.exists()); @@ -1992,6 +1992,9 @@ class AndroidProjectExporter final : public ProjectExporter if (androidVibratePermission.get()) s.add ("android.permission.VIBRATE"); + if (arePushNotificationsEnabled()) + s.add ("android.permission.POST_NOTIFICATIONS"); + return getCleanedStringArray (s); } diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h index 626df529081b..805e17977ee9 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h @@ -53,6 +53,260 @@ inline StringArray msBuildEscape (StringArray range) return range; } +class MSVCScriptBuilder +{ +public: + struct StringOrBuilder + { + StringOrBuilder() = default; + StringOrBuilder (const String& s) : value (s) {} + StringOrBuilder (const char* s) : StringOrBuilder (String { s }) {} + StringOrBuilder (const MSVCScriptBuilder& sb) : StringOrBuilder (sb.build()) {} + + bool isNotEmpty() const { return value.isNotEmpty(); } + + String value; + }; + + MSVCScriptBuilder& exit (int code) + { + script << "exit /b " << code; + script << newLine; + return *this; + } + + MSVCScriptBuilder& deleteFile (const StringOrBuilder& path) + { + script << "del /s /q " << path.value; + script << newLine; + return *this; + } + + MSVCScriptBuilder& mkdir (const StringOrBuilder& path) + { + script << "mkdir " << path.value; + script << newLine; + return *this; + } + + MSVCScriptBuilder& delay (int timeSeconds) + { + script << "timeout /t " << String { timeSeconds } << " /nobreak"; + script << newLine; + return *this; + } + + MSVCScriptBuilder& warning (const String& message) + { + script << "echo : Warning: " + message; + script << newLine; + return *this; + } + + MSVCScriptBuilder& info (const String& message) + { + script << "echo : Info: " << message; + script << newLine; + return *this; + } + + MSVCScriptBuilder& error (const String& message) + { + script << "echo : Error: " << message; + script << newLine; + return *this; + } + + MSVCScriptBuilder& run (const StringOrBuilder& command, bool echo = false) + { + if (echo) + script << "echo \"running " << command.value << "\"" << newLine; + + script << command.value; + script << newLine; + return *this; + } + + template + MSVCScriptBuilder& set (const String& name, T value) + { + script << "set " << name << "=" << value; + script << newLine; + return *this; + } + + MSVCScriptBuilder& runAndCheck (const StringOrBuilder& command, + const StringOrBuilder& success, + const StringOrBuilder& failed = {}) + { + run (command.value); + ifelse ("%ERRORLEVEL% equ 0", success.value, failed.value); + return *this; + } + + MSVCScriptBuilder& ifAllConditionsTrue (const StringArray& conditions, const StringOrBuilder& left) + { + jassert (left.isNotEmpty()); + + for (const auto& string : conditions) + script << "if " << string << " "; + + script << "(" << newLine << left.value << ")"; + script << newLine; + return *this; + } + + MSVCScriptBuilder& ifelse (const String& expr, + const StringOrBuilder& left, + const StringOrBuilder& right = {}) + { + jassert (left.isNotEmpty()); + + script << "if " << expr << " (" << newLine << left.value; + + if (right.isNotEmpty()) + script << ") else (" << newLine << right.value << ")"; + else + script << ")"; + + script << newLine; + return *this; + } + + MSVCScriptBuilder& append (const StringOrBuilder& string) + { + script << string.value << newLine; + return *this; + } + + MSVCScriptBuilder& labelledSection (const String& name, const StringOrBuilder& body) + { + script << ":" << name << newLine << body.value << newLine; + return *this; + } + + MSVCScriptBuilder& call (const String& label) + { + script << "call :" << label << newLine; + return *this; + } + + MSVCScriptBuilder& jump (const String& label) + { + script << "goto :" << label << newLine; + return *this; + } + + String build() const + { + MemoryOutputStream stream; + build (stream); + return stream.toUTF8(); + } + +private: + void build (OutputStream& stream) const + { + const auto genTab = [] (int depth, const int tabWidth = 4) + { + return String{}.paddedLeft (' ', depth * tabWidth); + }; + + int depth = 0; + auto lines = StringArray::fromLines (script); + + for (auto [lineIndex, line] : enumerate (lines)) + { + const auto trimmed = line.trim(); + const auto enter = trimmed.endsWith ("("); + const auto leave = trimmed.startsWith (")"); + + if (leave && depth > 0) + depth--; + + if (trimmed.isNotEmpty()) + { + stream << genTab (depth) << trimmed; + + if (lineIndex < lines.size() - 1) + stream << newLine; + } + + if (enter) + depth++; + } + } + + String script; +}; + +enum class Architecture +{ + win32, + win64, + arm64, + arm64ec +}; + +static String getArchitectureValueString (Architecture arch) +{ + switch (arch) + { + case Architecture::win32: return "Win32"; + case Architecture::win64: return "x64"; + case Architecture::arm64: return "ARM64"; + case Architecture::arm64ec: return "ARM64EC"; + } + + jassertfalse; + return ""; +} + +static std::optional architectureTypeFromString (const String& string) +{ + constexpr Architecture values[] { Architecture::win32, + Architecture::win64, + Architecture::arm64, + Architecture::arm64ec }; + + for (auto value : values) + { + if (getArchitectureValueString (value) == string) + return value; + } + + jassertfalse; + return {}; +} + +static String getVisualStudioArchitectureId (Architecture architecture) +{ + switch (architecture) + { + case Architecture::win32: return "x86"; + case Architecture::win64: return "AMD64"; + case Architecture::arm64: return "ARM64"; + case Architecture::arm64ec: return "ARM64"; + } + + jassertfalse; + return ""; +} + +static String getVisualStudioPlatformId (Architecture architecture) +{ + switch (architecture) + { + case Architecture::win32: return "x86"; + case Architecture::win64: return "x64"; + case Architecture::arm64: return "ARM64"; + case Architecture::arm64ec: return "ARM64EC"; + } + + jassertfalse; + return ""; +} + //============================================================================== class MSVCProjectExporterBase : public ProjectExporter { @@ -125,7 +379,7 @@ class MSVCProjectExporterBase : public ProjectExporter if (oldStylePrebuildCommand.isNotEmpty()) for (ConfigIterator config (*this); config.next();) - dynamic_cast (*config).getValue (Ids::prebuildCommand) = oldStylePrebuildCommand; + static_cast (&*config)->getValue (Ids::prebuildCommand) = oldStylePrebuildCommand; } { @@ -148,8 +402,47 @@ class MSVCProjectExporterBase : public ProjectExporter config->getValue (Ids::targetName) = oldStyleLibName; } + { + std::vector toErase; + + for (const auto& config : getConfigurations()) + { + if (config.getProperty (Ids::winArchitecture) == "ARM") + toErase.push_back (config); + } + + if (! toErase.empty()) + { + for (const auto& e : toErase) + e.getParent().removeChild (e, nullptr); + + getProject().addProjectMessage (ProjectMessages::Ids::unsupportedArm32Config, {}); + } + } + for (ConfigIterator i (*this); i.next();) - dynamic_cast (*i).updateOldLTOSetting(); + { + auto& config = *static_cast (&*i); + config.updateOldLTOSetting(); + config.updateOldArchSetting(); + } + } + + Array getAllActiveArchitectures() const + { + Array archs; + + for (ConstConfigIterator i (*this); i.next();) + { + const auto& config = *static_cast (&*i); + const auto configArchs = config.getArchitectures(); + + for (const auto& arch : configArchs) + if (! archs.contains (arch)) + archs.add (arch); + } + + return archs; } void initialiseDependencyPathValues() override @@ -166,7 +459,7 @@ class MSVCProjectExporterBase : public ProjectExporter //============================================================================== class MSVCBuildConfiguration final : public BuildConfiguration, - private Value::Listener + private ValueTree::Listener { public: MSVCBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e) @@ -181,7 +474,7 @@ class MSVCProjectExporterBase : public ProjectExporter multiProcessorCompilationValue (config, Ids::multiProcessorCompilation, getUndoManager(), true), intermediatesPathValue (config, Ids::intermediatesPath, getUndoManager()), characterSetValue (config, Ids::characterSet, getUndoManager()), - architectureTypeValue (config, Ids::winArchitecture, getUndoManager(), getIntel64BitArchName()), + architectureTypeValue (config, Ids::winArchitecture, getUndoManager(), Array { getArchitectureValueString (Architecture::win64) }, ","), fastMathValue (config, Ids::fastMath, getUndoManager()), debugInformationFormatValue (config, Ids::debugInformationFormat, getUndoManager(), isDebug() ? "ProgramDatabase" : "None"), pluginBinaryCopyStepValue (config, Ids::enablePluginBinaryCopyStep, getUndoManager(), false), @@ -189,33 +482,93 @@ class MSVCProjectExporterBase : public ProjectExporter vst3BinaryLocation (config, Ids::vst3BinaryLocation, getUndoManager()), aaxBinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager()), lv2BinaryLocation (config, Ids::lv2BinaryLocation, getUndoManager()), - unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager(), {}) + unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager()) { - setPluginBinaryCopyLocationDefaults(); + constexpr std::tuple paths[] + { + { Architecture::win32, "%programfiles(x86)%", "%CommonProgramFiles(x86)%" }, + { Architecture::win64, "%ProgramW6432%", "%CommonProgramW6432%" }, + { Architecture::arm64, "%ProgramW6432%", "%CommonProgramW6432%" }, + { Architecture::arm64ec, "%ProgramW6432%", "%CommonProgramW6432%" } + }; + + for (const auto& [arch, programFolderPath, commonFolderPath] : paths) + { + setBinaryPathDefault (Ids::vstBinaryLocation, arch, programFolderPath + String ("\\Steinberg\\Vstplugins")); + setBinaryPathDefault (Ids::vst3BinaryLocation, arch, commonFolderPath + String ("\\VST3")); + setBinaryPathDefault (Ids::aaxBinaryLocation, arch, commonFolderPath + String ("\\Avid\\Audio\\Plug-Ins")); + setBinaryPathDefault (Ids::lv2BinaryLocation, arch, "%APPDATA%\\LV2"); + } + optimisationLevelValue.setDefault (isDebug() ? optimisationOff : optimiseFull); - architectureValueToListenTo = architectureTypeValue.getPropertyAsValue(); - architectureValueToListenTo.addListener (this); + config.addListener (this); + } + + ~MSVCBuildConfiguration() override + { + config.removeListener (this); + } + + void valueTreePropertyChanged (ValueTree&, const Identifier& property) override + { + if (property != Ids::winArchitecture) + return; + + project.removeProjectMessage (ProjectMessages::Ids::arm64Warning); + + const auto selectedArchs = architectureTypeValue.get(); + + if (! selectedArchs.getArray()->contains (getArchitectureValueString (Architecture::arm64))) + return; + + if (selectedArchs.getArray()->contains (getArchitectureValueString (Architecture::arm64ec))) + return; + + project.addProjectMessage (ProjectMessages::Ids::arm64Warning, {}); + } + + String getBinaryPath (const Identifier& id, Architecture arch) const + { + if (auto* location = getLocationForArchitecture (id, arch)) + return location->get().toString(); + + return ""; + } + + void setBinaryPathDefault (const Identifier& id, Architecture arch, const String& path) + { + if (auto* location = getLocationForArchitecture (id, arch)) + location->setDefault (path); } //============================================================================== int getWarningLevel() const { return warningLevelValue.get(); } bool areWarningsTreatedAsErrors() const { return warningsAreErrorsValue.get(); } + Array getArchitectures() const + { + auto value = architectureTypeValue.get(); + auto* array = value.getArray(); + + if (array == nullptr) + return {}; + + Array result; + result.resize (array->size()); + + std::transform (array->begin(), array->end(), result.begin(), [] (const var& archVar) + { + return *architectureTypeFromString (archVar.toString()); + }); + + return result; + } + String getPrebuildCommandString() const { return prebuildCommandValue.get(); } String getPostbuildCommandString() const { return postbuildCommandValue.get(); } - String getVSTBinaryLocationString() const { return vstBinaryLocation.get(); } - String getVST3BinaryLocationString() const { return vst3BinaryLocation.get(); } - String getAAXBinaryLocationString() const { return aaxBinaryLocation.get();} - String getLV2BinaryLocationString() const { return lv2BinaryLocation.get();} - String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); } String getIntermediatesPathString() const { return intermediatesPathValue.get(); } String getCharacterSetString() const { return characterSetValue.get(); } - String getIntel64BitArchName() const { return "x64"; } - String getIntel32BitArchName() const { return "Win32"; } - String getArm64BitArchName() const { return "ARM64"; } - String getArm32BitArchName() const { return "ARM"; } - String getArchitectureString() const { return architectureTypeValue.get(); } String getDebugInformationFormatString() const { return debugInformationFormatValue.get(); } bool shouldGenerateDebugSymbols() const { return generateDebugSymbolsValue.get(); } @@ -225,10 +578,16 @@ class MSVCProjectExporterBase : public ProjectExporter bool isFastMathEnabled() const { return fastMathValue.get(); } bool isPluginBinaryCopyStepEnabled() const { return pluginBinaryCopyStepValue.get(); } + static bool shouldBuildTarget (build_tools::ProjectType::Target::Type targetType, Architecture arch) + { + return targetType != build_tools::ProjectType::Target::AAXPlugIn + || (arch != Architecture::arm64 && arch != Architecture::arm64ec); + } + //============================================================================== - String createMSVCConfigName() const + String createMSVCConfigName (Architecture arch) const { - return getName() + "|" + getArchitectureString(); + return getName() + "|" + getArchitectureValueString (arch); } String getOutputFilename (const String& suffix, @@ -257,9 +616,20 @@ class MSVCProjectExporterBase : public ProjectExporter if (project.isAudioPluginProject()) addVisualStudioPluginInstallPathProperties (props); - props.add (new ChoicePropertyComponent (architectureTypeValue, "Architecture", - { getIntel32BitArchName(), getIntel64BitArchName(), getArm32BitArchName(), getArm64BitArchName() }, - { getIntel32BitArchName(), getIntel64BitArchName(), getArm32BitArchName(), getArm64BitArchName() }), + const auto architectureList = exporter.getExporterIdentifier() == Identifier { "VS2022" } + ? std::vector { Architecture::win32, Architecture::win64, Architecture::arm64, Architecture::arm64ec } + : std::vector { Architecture::win32, Architecture::win64, Architecture::arm64 }; + + Array architectureListAsStrings; + Array architectureListAsVars; + + for (const auto& arch : architectureList) + { + architectureListAsStrings.add (getArchitectureValueString (arch)); + architectureListAsVars.add (getArchitectureValueString (arch)); + } + + props.add (new MultiChoicePropertyComponent (architectureTypeValue, "Architecture", architectureListAsStrings, architectureListAsVars), "Which Windows architecture to use."); props.add (new ChoicePropertyComponentWithEnablement (debugInformationFormatValue, @@ -344,15 +714,111 @@ class MSVCProjectExporterBase : public ProjectExporter linkTimeOptimisationValue = (static_cast (config ["wholeProgramOptimisation"]) == 0); } + void updateOldArchSetting() + { + if (architectureTypeValue.get().isArray()) + return; + + const auto archString = architectureTypeValue.get().toString(); + const auto archType = architectureTypeFromString (archString); + + if (! archType) + return; + + const auto pluginBinaryPathLocationIds = + { + Ids::vstBinaryLocation, + Ids::vst3BinaryLocation, + Ids::lv2BinaryLocation, + Ids::aaxBinaryLocation, + Ids::unityPluginBinaryLocation + }; + + for (const auto& location : pluginBinaryPathLocationIds) + { + if (auto* prop = config.getPropertyPointer (location)) + { + setBinaryPathDefault (location, *archType, prop->toString()); + } + } + + architectureTypeValue = Array { archString }; + } + private: ValueTreePropertyWithDefault warningLevelValue, warningsAreErrorsValue, prebuildCommandValue, postbuildCommandValue, generateDebugSymbolsValue, enableIncrementalLinkingValue, useRuntimeLibDLLValue, multiProcessorCompilationValue, intermediatesPathValue, characterSetValue, architectureTypeValue, fastMathValue, debugInformationFormatValue, pluginBinaryCopyStepValue; - ValueTreePropertyWithDefault vstBinaryLocation, vst3BinaryLocation, aaxBinaryLocation, lv2BinaryLocation, unityPluginBinaryLocation; + struct LocationProperties + { + LocationProperties (ValueTree& tree, const Identifier& propertyID, UndoManager* um) + : win32 (tree, propertyID + "_Win32", um), + x64 (tree, propertyID + "_x64", um), + arm64 (tree, propertyID + "_arm64", um), + arm64ec (tree, propertyID + "_arm64ec", um) + { + } + + const ValueTreePropertyWithDefault* get (Architecture arch) const + { + return get (*this, arch); + } + + ValueTreePropertyWithDefault* get (Architecture arch) + { + return get (*this, arch); + } + + ValueTreePropertyWithDefault win32, x64, arm64, arm64ec; + + private: + template + static auto get (This& t, Architecture arch) -> decltype (t.get (arch)) + { + switch (arch) + { + case Architecture::win32: return &t.win32; + case Architecture::win64: return &t.x64; + case Architecture::arm64: return &t.arm64; + case Architecture::arm64ec: return &t.arm64ec; + } + + jassertfalse; + return nullptr; + } + }; + + template + static auto getLocationForArchitecture (This& t, const Identifier& id, Architecture arch) + { + const auto properties = + { + std::pair { Ids::vstBinaryLocation, &t.vstBinaryLocation }, + std::pair { Ids::vst3BinaryLocation, &t.vst3BinaryLocation }, + std::pair { Ids::aaxBinaryLocation, &t.aaxBinaryLocation }, + std::pair { Ids::lv2BinaryLocation, &t.lv2BinaryLocation } + }; + + const auto iter = std::find_if (properties.begin(), + properties.end(), + [id] (auto pair) { return id == pair.first; }); + + return iter != properties.end() ? iter->second->get (arch) : nullptr; + } - Value architectureValueToListenTo; + ValueTreePropertyWithDefault* getLocationForArchitecture (const Identifier& id, Architecture arch) + { + return getLocationForArchitecture (*this, id, arch); + } + + const ValueTreePropertyWithDefault* getLocationForArchitecture (const Identifier& id, Architecture arch) const + { + return getLocationForArchitecture (*this, id, arch); + } + + LocationProperties vstBinaryLocation, vst3BinaryLocation, aaxBinaryLocation, lv2BinaryLocation, unityPluginBinaryLocation; //============================================================================== void addVisualStudioPluginInstallPathProperties (PropertyListBuilder& props) @@ -364,61 +830,33 @@ class MSVCProjectExporterBase : public ProjectExporter props.add (new ChoicePropertyComponent (pluginBinaryCopyStepValue, "Enable Plugin Copy Step"), "Enable this to copy plugin binaries to a specified folder after building."); + const auto addLocationProperties = [&] (auto& locationProps, const String& format) + { + for (const auto& [member, arch] : { std::tuple (&LocationProperties::win32, Architecture::win32), + std::tuple (&LocationProperties::x64, Architecture::win64), + std::tuple (&LocationProperties::arm64, Architecture::arm64), + std::tuple (&LocationProperties::arm64ec, Architecture::arm64ec) }) + { + const auto archAndFormat = getArchitectureValueString (arch) + " " + format; + props.add (new TextPropertyComponentWithEnablement (locationProps.*member, pluginBinaryCopyStepValue, archAndFormat + " Binary Location", 1024, false), + "The folder in which the compiled " + archAndFormat + " binary should be placed."); + } + }; + if (project.shouldBuildVST3()) - props.add (new TextPropertyComponentWithEnablement (vst3BinaryLocation, pluginBinaryCopyStepValue, "VST3 Binary Location", - 1024, false), - "The folder in which the compiled VST3 binary should be placed."); + addLocationProperties (vst3BinaryLocation, "VST3"); if (project.shouldBuildAAX()) - props.add (new TextPropertyComponentWithEnablement (aaxBinaryLocation, pluginBinaryCopyStepValue, "AAX Binary Location", - 1024, false), - "The folder in which the compiled AAX binary should be placed."); + addLocationProperties (aaxBinaryLocation, "AAX"); if (project.shouldBuildLV2()) - props.add (new TextPropertyComponentWithEnablement (lv2BinaryLocation, pluginBinaryCopyStepValue, "LV2 Binary Location", - 1024, false), - "The folder in which the compiled LV2 binary should be placed."); + addLocationProperties (lv2BinaryLocation, "LV2"); if (project.shouldBuildUnityPlugin()) - props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepValue, "Unity Binary Location", - 1024, false), - "The folder in which the compiled Unity plugin binary and associated C# GUI script should be placed."); + addLocationProperties (unityPluginBinaryLocation, "Unity"); if (project.shouldBuildVST()) - props.add (new TextPropertyComponentWithEnablement (vstBinaryLocation, pluginBinaryCopyStepValue, "VST (Legacy) Binary Location", - 1024, false), - "The folder in which the compiled legacy VST binary should be placed."); - - } - - void setPluginBinaryCopyLocationDefaults() - { - const auto [programsFolderPath, commonsFolderPath] = [&]() -> std::tuple - { - static const std::map> options - { - { "Win32", { "%programfiles(x86)%", "%CommonProgramFiles(x86)%" } }, - { "x64", { "%ProgramW6432%", "%CommonProgramW6432%" } }, - { "ARM", { "%programfiles(arm)%", "%CommonProgramFiles(arm)%" } }, - { "ARM64", { "%ProgramW6432%", "%CommonProgramW6432%" } } - }; - - if (const auto iter = options.find (getArchitectureString()); iter != options.cend()) - return iter->second; - - jassertfalse; - return { "%programfiles%", "%CommonProgramFiles%" }; - }(); - - vstBinaryLocation.setDefault (programsFolderPath + String ("\\Steinberg\\Vstplugins")); - vst3BinaryLocation.setDefault (commonsFolderPath + String ("\\VST3")); - aaxBinaryLocation.setDefault (commonsFolderPath + String ("\\Avid\\Audio\\Plug-Ins")); - lv2BinaryLocation.setDefault ("%APPDATA%\\LV2"); - } - - void valueChanged (Value&) override - { - setPluginBinaryCopyLocationDefaults(); + addLocationProperties (vstBinaryLocation, "VST (Legacy)"); } }; @@ -432,7 +870,7 @@ class MSVCProjectExporterBase : public ProjectExporter projectGuid = createGUID (owner.getProject().getProjectUIDString() + getName()); } - virtual ~MSVCTarget() {} + virtual ~MSVCTarget() = default; String getProjectVersionString() const { return "10.00"; } String getProjectFileSuffix() const { return ".vcxproj"; } @@ -446,17 +884,22 @@ class MSVCProjectExporterBase : public ProjectExporter projectXml.setAttribute ("ToolsVersion", getOwner().getToolsVersion()); projectXml.setAttribute ("xmlns", "http://schemas.microsoft.com/developer/msbuild/2003"); + const auto allArchitectures = owner.getAllActiveArchitectures(); + { auto* configsGroup = projectXml.createNewChildElement ("ItemGroup"); configsGroup->setAttribute ("Label", "ProjectConfigurations"); for (ConstConfigIterator i (owner); i.next();) { - auto& config = dynamic_cast (*i); - auto* e = configsGroup->createNewChildElement ("ProjectConfiguration"); - e->setAttribute ("Include", config.createMSVCConfigName()); - e->createNewChildElement ("Configuration")->addTextElement (config.getName()); - e->createNewChildElement ("Platform")->addTextElement (config.getArchitectureString()); + auto& config = *static_cast (&*i); + for (const auto& arch : allArchitectures) + { + auto* e = configsGroup->createNewChildElement ("ProjectConfiguration"); + e->setAttribute ("Include", config.createMSVCConfigName (arch)); + e->createNewChildElement ("Configuration")->addTextElement (config.getName()); + e->createNewChildElement ("Platform")->addTextElement (getArchitectureValueString (arch)); + } } } @@ -473,40 +916,43 @@ class MSVCProjectExporterBase : public ProjectExporter for (ConstConfigIterator i (owner); i.next();) { - auto& config = dynamic_cast (*i); + auto& config = *static_cast (&*i); - auto* e = projectXml.createNewChildElement ("PropertyGroup"); - setConditionAttribute (*e, config); - e->setAttribute ("Label", "Configuration"); - e->createNewChildElement ("ConfigurationType")->addTextElement (getProjectType()); - e->createNewChildElement ("UseOfMfc")->addTextElement ("false"); - e->createNewChildElement ("WholeProgramOptimization")->addTextElement (config.isLinkTimeOptimisationEnabled() ? "true" - : "false"); + for (const auto& arch : allArchitectures) + { + auto* e = projectXml.createNewChildElement ("PropertyGroup"); + setConditionAttribute (*e, config, arch); + e->setAttribute ("Label", "Configuration"); + e->createNewChildElement ("ConfigurationType")->addTextElement (getProjectType()); + e->createNewChildElement ("UseOfMfc")->addTextElement ("false"); + e->createNewChildElement ("WholeProgramOptimization")->addTextElement (config.isLinkTimeOptimisationEnabled() ? "true" + : "false"); - auto charSet = config.getCharacterSetString(); + auto charSet = config.getCharacterSetString(); - if (charSet.isNotEmpty()) - e->createNewChildElement ("CharacterSet")->addTextElement (charSet); + if (charSet.isNotEmpty()) + e->createNewChildElement ("CharacterSet")->addTextElement (charSet); - if (config.shouldLinkIncremental()) - e->createNewChildElement ("LinkIncremental")->addTextElement ("true"); + if (config.shouldLinkIncremental()) + e->createNewChildElement ("LinkIncremental")->addTextElement ("true"); - e->createNewChildElement ("PlatformToolset")->addTextElement (owner.getPlatformToolset()); + e->createNewChildElement ("PlatformToolset")->addTextElement (owner.getPlatformToolset()); - addWindowsTargetPlatformToConfig (*e); + addWindowsTargetPlatformToConfig (*e); - struct IntelLibraryInfo - { - String libraryKind; - String configString; - }; + struct IntelLibraryInfo + { + String libraryKind; + String configString; + }; - for (const auto& info : { IntelLibraryInfo { owner.getIPPLibrary(), "UseIntelIPP" }, - IntelLibraryInfo { owner.getIPP1ALibrary(), "UseIntelIPP1A" }, - IntelLibraryInfo { owner.getMKL1ALibrary(), "UseInteloneMKL" } }) - { - if (info.libraryKind.isNotEmpty()) - e->createNewChildElement (info.configString)->addTextElement (info.libraryKind); + for (const auto& info : { IntelLibraryInfo { owner.getIPPLibrary(), "UseIntelIPP" }, + IntelLibraryInfo { owner.getIPP1ALibrary(), "UseIntelIPP1A" }, + IntelLibraryInfo { owner.getMKL1ALibrary(), "UseInteloneMKL" } }) + { + if (info.libraryKind.isNotEmpty()) + e->createNewChildElement (info.configString)->addTextElement (info.libraryKind); + } } } @@ -536,47 +982,59 @@ class MSVCProjectExporterBase : public ProjectExporter for (ConstConfigIterator i (owner); i.next();) { - auto& config = dynamic_cast (*i); + auto& config = *static_cast (&*i); - if (getConfigTargetPath (config).isNotEmpty()) + for (const auto& arch : allArchitectures) { - auto* outdir = props->createNewChildElement ("OutDir"); - setConditionAttribute (*outdir, config); - outdir->addTextElement (build_tools::windowsStylePath (getConfigTargetPath (config)) + "\\"); - } + if (getConfigTargetPath (config).isNotEmpty()) + { + auto* outdir = props->createNewChildElement ("OutDir"); + setConditionAttribute (*outdir, config, arch); + outdir->addTextElement (build_tools::windowsStylePath (getConfigTargetPath (config)) + "\\"); + } - { - auto* intdir = props->createNewChildElement ("IntDir"); - setConditionAttribute (*intdir, config); + { + auto* intdir = props->createNewChildElement ("IntDir"); + setConditionAttribute (*intdir, config, arch); - auto intermediatesPath = getIntermediatesPath (config); - if (! intermediatesPath.endsWithChar (L'\\')) - intermediatesPath += L'\\'; + auto intermediatesPath = getIntermediatesPath (config); + if (! intermediatesPath.endsWithChar (L'\\')) + intermediatesPath += L'\\'; - intdir->addTextElement (build_tools::windowsStylePath (intermediatesPath)); - } + intdir->addTextElement (build_tools::windowsStylePath (intermediatesPath)); + } - { - auto* targetName = props->createNewChildElement ("TargetName"); - setConditionAttribute (*targetName, config); - targetName->addTextElement (msBuildEscape (config.getOutputFilename ("", false, type))); - } + { + auto* targetName = props->createNewChildElement ("TargetName"); + setConditionAttribute (*targetName, config, arch); + targetName->addTextElement (msBuildEscape (config.getOutputFilename ("", false, type))); + } - { - auto* manifest = props->createNewChildElement ("GenerateManifest"); - setConditionAttribute (*manifest, config); - manifest->addTextElement ("true"); - } + { + auto* manifest = props->createNewChildElement ("GenerateManifest"); + setConditionAttribute (*manifest, config, arch); + manifest->addTextElement ("true"); + } - if (type != SharedCodeTarget) - { - auto librarySearchPaths = getLibrarySearchPaths (config); + if (type != SharedCodeTarget) + { + auto librarySearchPaths = getLibrarySearchPaths (config); + + if (! librarySearchPaths.isEmpty()) + { + auto* libPath = props->createNewChildElement ("LibraryPath"); + setConditionAttribute (*libPath, config, arch); + libPath->addTextElement ("$(LibraryPath);" + librarySearchPaths.joinIntoString (";")); + } + } + + const auto enabled = config.getArchitectures().contains (arch) ? "true" : "false"; - if (! librarySearchPaths.isEmpty()) + for (const auto optionName : { "PreBuildEventUseInBuild", "PostBuildEventUseInBuild" }) { - auto* libPath = props->createNewChildElement ("LibraryPath"); - setConditionAttribute (*libPath, config); - libPath->addTextElement ("$(LibraryPath);" + librarySearchPaths.joinIntoString (";")); + auto* tag = props->createNewChildElement (optionName); + setConditionAttribute (*tag, config, arch); + tag->addTextElement (enabled); } } } @@ -584,191 +1042,194 @@ class MSVCProjectExporterBase : public ProjectExporter for (ConstConfigIterator i (owner); i.next();) { - auto& config = dynamic_cast (*i); - - enum class EscapeQuotes { no, yes }; + auto& config = *static_cast (&*i); - // VS doesn't correctly escape double quotes in preprocessor definitions, so we have - // to add our own layer of escapes - const auto addIncludePathsAndPreprocessorDefinitions = [this, &config] (XmlElement& xml, EscapeQuotes escapeQuotes) + for (const auto& arch : allArchitectures) { - auto includePaths = getOwner().getHeaderSearchPaths (config); - includePaths.add ("%(AdditionalIncludeDirectories)"); - xml.createNewChildElement ("AdditionalIncludeDirectories")->addTextElement (includePaths.joinIntoString (";")); - - const auto preprocessorDefs = getPreprocessorDefs (config, ";") + ";%(PreprocessorDefinitions)"; - const auto preprocessorDefsEscaped = escapeQuotes == EscapeQuotes::yes ? preprocessorDefs.replace ("\"", "\\\"") - : preprocessorDefs; - xml.createNewChildElement ("PreprocessorDefinitions")->addTextElement (preprocessorDefsEscaped); - }; - - bool isDebug = config.isDebug(); - - auto* group = projectXml.createNewChildElement ("ItemDefinitionGroup"); - setConditionAttribute (*group, config); + enum class EscapeQuotes { no, yes }; - { - auto* midl = group->createNewChildElement ("Midl"); - midl->createNewChildElement ("PreprocessorDefinitions")->addTextElement (isDebug ? "_DEBUG;%(PreprocessorDefinitions)" - : "NDEBUG;%(PreprocessorDefinitions)"); - midl->createNewChildElement ("MkTypLibCompatible")->addTextElement ("true"); - midl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); - midl->createNewChildElement ("TargetEnvironment")->addTextElement ("Win32"); - midl->createNewChildElement ("HeaderFileName"); - } + // VS doesn't correctly escape double quotes in preprocessor definitions, so we have + // to add our own layer of escapes + const auto addIncludePathsAndPreprocessorDefinitions = [this, &config] (XmlElement& xml, EscapeQuotes escapeQuotes) + { + auto includePaths = getOwner().getHeaderSearchPaths (config); + includePaths.add ("%(AdditionalIncludeDirectories)"); + xml.createNewChildElement ("AdditionalIncludeDirectories")->addTextElement (includePaths.joinIntoString (";")); - bool isUsingEditAndContinue = false; - const auto pdbFilename = getOwner().getIntDirFile (config, config.getOutputFilename (".pdb", true, type)); + const auto preprocessorDefs = getPreprocessorDefs (config, ";") + ";%(PreprocessorDefinitions)"; + const auto preprocessorDefsEscaped = escapeQuotes == EscapeQuotes::yes ? preprocessorDefs.replace ("\"", "\\\"") + : preprocessorDefs; + xml.createNewChildElement ("PreprocessorDefinitions")->addTextElement (preprocessorDefsEscaped); + }; - { - auto* cl = group->createNewChildElement ("ClCompile"); + bool isDebug = config.isDebug(); - cl->createNewChildElement ("Optimization")->addTextElement (getOptimisationLevelString (config.getOptimisationLevelInt())); + auto* group = projectXml.createNewChildElement ("ItemDefinitionGroup"); + setConditionAttribute (*group, config, arch); - if (isDebug || config.shouldGenerateDebugSymbols()) { - cl->createNewChildElement ("DebugInformationFormat") - ->addTextElement (config.getDebugInformationFormatString()); + auto* midl = group->createNewChildElement ("Midl"); + midl->createNewChildElement ("PreprocessorDefinitions")->addTextElement (isDebug ? "_DEBUG;%(PreprocessorDefinitions)" + : "NDEBUG;%(PreprocessorDefinitions)"); + midl->createNewChildElement ("MkTypLibCompatible")->addTextElement ("true"); + midl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); + midl->createNewChildElement ("TargetEnvironment")->addTextElement ("Win32"); + midl->createNewChildElement ("HeaderFileName"); } - addIncludePathsAndPreprocessorDefinitions (*cl, EscapeQuotes::no); + bool isUsingEditAndContinue = false; + const auto pdbFilename = getOwner().getIntDirFile (config, config.getOutputFilename (".pdb", true, type)); - cl->createNewChildElement ("RuntimeLibrary")->addTextElement (config.isUsingRuntimeLibDLL() ? (isDebug ? "MultiThreadedDebugDLL" : "MultiThreadedDLL") - : (isDebug ? "MultiThreadedDebug" : "MultiThreaded")); - cl->createNewChildElement ("RuntimeTypeInfo")->addTextElement ("true"); - cl->createNewChildElement ("PrecompiledHeader")->addTextElement ("NotUsing"); - cl->createNewChildElement ("AssemblerListingLocation")->addTextElement ("$(IntDir)\\"); - cl->createNewChildElement ("ObjectFileName")->addTextElement ("$(IntDir)\\"); - cl->createNewChildElement ("ProgramDataBaseFileName")->addTextElement (pdbFilename); - cl->createNewChildElement ("WarningLevel")->addTextElement ("Level" + String (config.getWarningLevel())); - cl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); - cl->createNewChildElement ("MultiProcessorCompilation")->addTextElement (config.shouldUseMultiProcessorCompilation() ? "true" : "false"); + { + auto* cl = group->createNewChildElement ("ClCompile"); - if (config.isFastMathEnabled()) - cl->createNewChildElement ("FloatingPointModel")->addTextElement ("Fast"); + cl->createNewChildElement ("Optimization")->addTextElement (getOptimisationLevelString (config.getOptimisationLevelInt())); - auto extraFlags = getOwner().replacePreprocessorTokens (config, config.getAllCompilerFlagsString()).trim(); + if (isDebug || config.shouldGenerateDebugSymbols()) + { + cl->createNewChildElement ("DebugInformationFormat") + ->addTextElement (config.getDebugInformationFormatString()); + } - if (extraFlags.isNotEmpty()) - cl->createNewChildElement ("AdditionalOptions")->addTextElement (extraFlags + " %(AdditionalOptions)"); + addIncludePathsAndPreprocessorDefinitions (*cl, EscapeQuotes::no); - if (config.areWarningsTreatedAsErrors()) - cl->createNewChildElement ("TreatWarningAsError")->addTextElement ("true"); + cl->createNewChildElement ("RuntimeLibrary")->addTextElement (config.isUsingRuntimeLibDLL() ? (isDebug ? "MultiThreadedDebugDLL" : "MultiThreadedDLL") + : (isDebug ? "MultiThreadedDebug" : "MultiThreaded")); + cl->createNewChildElement ("RuntimeTypeInfo")->addTextElement ("true"); + cl->createNewChildElement ("PrecompiledHeader")->addTextElement ("NotUsing"); + cl->createNewChildElement ("AssemblerListingLocation")->addTextElement ("$(IntDir)\\"); + cl->createNewChildElement ("ObjectFileName")->addTextElement ("$(IntDir)\\"); + cl->createNewChildElement ("ProgramDataBaseFileName")->addTextElement (pdbFilename); + cl->createNewChildElement ("WarningLevel")->addTextElement ("Level" + String (config.getWarningLevel())); + cl->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); + cl->createNewChildElement ("MultiProcessorCompilation")->addTextElement (config.shouldUseMultiProcessorCompilation() ? "true" : "false"); - auto cppStandard = owner.project.getCppStandardString(); - cl->createNewChildElement ("LanguageStandard")->addTextElement ("stdcpp" + cppStandard); - } + if (config.isFastMathEnabled()) + cl->createNewChildElement ("FloatingPointModel")->addTextElement ("Fast"); - { - auto* res = group->createNewChildElement ("ResourceCompile"); - addIncludePathsAndPreprocessorDefinitions (*res, EscapeQuotes::yes); - } + auto extraFlags = getOwner().replacePreprocessorTokens (config, config.getAllCompilerFlagsString()).trim(); - auto externalLibraries = getExternalLibraries (config, getOwner().getExternalLibrariesStringArray()); - auto additionalDependencies = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && ! externalLibraries.isEmpty() - ? externalLibraries.joinIntoString (";") + ";%(AdditionalDependencies)" - : String(); + if (extraFlags.isNotEmpty()) + cl->createNewChildElement ("AdditionalOptions")->addTextElement (extraFlags + " %(AdditionalOptions)"); - auto librarySearchPaths = config.getLibrarySearchPaths(); - auto additionalLibraryDirs = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && librarySearchPaths.size() > 0 - ? getOwner().replacePreprocessorTokens (config, librarySearchPaths.joinIntoString (";")) + ";%(AdditionalLibraryDirectories)" - : String(); + if (config.areWarningsTreatedAsErrors()) + cl->createNewChildElement ("TreatWarningAsError")->addTextElement ("true"); + + auto cppStandard = owner.project.getCppStandardString(); + cl->createNewChildElement ("LanguageStandard")->addTextElement ("stdcpp" + cppStandard); + } - { - auto* link = group->createNewChildElement ("Link"); - link->createNewChildElement ("OutputFile")->addTextElement (getOutputFilePath (config)); - link->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); - link->createNewChildElement ("IgnoreSpecificDefaultLibraries")->addTextElement (isDebug ? "libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries)" - : "%(IgnoreSpecificDefaultLibraries)"); - link->createNewChildElement ("GenerateDebugInformation")->addTextElement ((isDebug || config.shouldGenerateDebugSymbols()) ? "true" : "false"); - link->createNewChildElement ("ProgramDatabaseFile")->addTextElement (pdbFilename); - link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp || type == LV2Helper || type == VST3Helper ? "Console" : "Windows"); - - if (config.getArchitectureString() == "Win32") - link->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86"); - - if (isUsingEditAndContinue) - link->createNewChildElement ("ImageHasSafeExceptionHandlers")->addTextElement ("false"); - - if (! isDebug) { - link->createNewChildElement ("OptimizeReferences")->addTextElement ("true"); - link->createNewChildElement ("EnableCOMDATFolding")->addTextElement ("true"); + auto* res = group->createNewChildElement ("ResourceCompile"); + addIncludePathsAndPreprocessorDefinitions (*res, EscapeQuotes::yes); } - if (additionalLibraryDirs.isNotEmpty()) - link->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs); + auto externalLibraries = getExternalLibraries (config, getOwner().getExternalLibrariesStringArray()); + auto additionalDependencies = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && ! externalLibraries.isEmpty() + ? externalLibraries.joinIntoString (";") + ";%(AdditionalDependencies)" + : String(); - link->createNewChildElement ("LargeAddressAware")->addTextElement ("true"); + auto librarySearchPaths = config.getLibrarySearchPaths(); + auto additionalLibraryDirs = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && librarySearchPaths.size() > 0 + ? getOwner().replacePreprocessorTokens (config, librarySearchPaths.joinIntoString (";")) + ";%(AdditionalLibraryDirectories)" + : String(); - if (config.isLinkTimeOptimisationEnabled()) - link->createNewChildElement ("LinkTimeCodeGeneration")->addTextElement ("UseLinkTimeCodeGeneration"); + { + auto* link = group->createNewChildElement ("Link"); + link->createNewChildElement ("OutputFile")->addTextElement (getOutputFilePath (config)); + link->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); + link->createNewChildElement ("IgnoreSpecificDefaultLibraries")->addTextElement (isDebug ? "libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries)" + : "%(IgnoreSpecificDefaultLibraries)"); + link->createNewChildElement ("GenerateDebugInformation")->addTextElement ((isDebug || config.shouldGenerateDebugSymbols()) ? "true" : "false"); + link->createNewChildElement ("ProgramDatabaseFile")->addTextElement (pdbFilename); + link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp || type == LV2Helper || type == VST3Helper ? "Console" : "Windows"); + + if (arch == Architecture::win32) + link->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86"); + + if (isUsingEditAndContinue) + link->createNewChildElement ("ImageHasSafeExceptionHandlers")->addTextElement ("false"); + + if (! isDebug) + { + link->createNewChildElement ("OptimizeReferences")->addTextElement ("true"); + link->createNewChildElement ("EnableCOMDATFolding")->addTextElement ("true"); + } - if (additionalDependencies.isNotEmpty()) - link->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies); + if (additionalLibraryDirs.isNotEmpty()) + link->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs); - auto extraLinkerOptions = config.getAllLinkerFlagsString(); - if (extraLinkerOptions.isNotEmpty()) - link->createNewChildElement ("AdditionalOptions")->addTextElement (getOwner().replacePreprocessorTokens (config, extraLinkerOptions).trim() - + " %(AdditionalOptions)"); + link->createNewChildElement ("LargeAddressAware")->addTextElement ("true"); - auto delayLoadedDLLs = getOwner().msvcDelayLoadedDLLs; - if (delayLoadedDLLs.isNotEmpty()) - link->createNewChildElement ("DelayLoadDLLs")->addTextElement (delayLoadedDLLs); + if (config.isLinkTimeOptimisationEnabled()) + link->createNewChildElement ("LinkTimeCodeGeneration")->addTextElement ("UseLinkTimeCodeGeneration"); - auto moduleDefinitionsFile = getModuleDefinitions (config); - if (moduleDefinitionsFile.isNotEmpty()) - link->createNewChildElement ("ModuleDefinitionFile") - ->addTextElement (moduleDefinitionsFile); - } + if (additionalDependencies.isNotEmpty()) + link->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies); - { - auto* bsc = group->createNewChildElement ("Bscmake"); - bsc->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); - bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type))); - } + auto extraLinkerOptions = config.getAllLinkerFlagsString(); + if (extraLinkerOptions.isNotEmpty()) + link->createNewChildElement ("AdditionalOptions")->addTextElement (getOwner().replacePreprocessorTokens (config, extraLinkerOptions).trim() + + " %(AdditionalOptions)"); - if (type != SharedCodeTarget && type != LV2Helper && type != VST3Helper) - { - auto* lib = group->createNewChildElement ("Lib"); + auto delayLoadedDLLs = getOwner().msvcDelayLoadedDLLs; + if (delayLoadedDLLs.isNotEmpty()) + link->createNewChildElement ("DelayLoadDLLs")->addTextElement (delayLoadedDLLs); - if (additionalDependencies.isNotEmpty()) - lib->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies); + auto moduleDefinitionsFile = getModuleDefinitions (config); + if (moduleDefinitionsFile.isNotEmpty()) + link->createNewChildElement ("ModuleDefinitionFile") + ->addTextElement (moduleDefinitionsFile); + } - if (additionalLibraryDirs.isNotEmpty()) - lib->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs); - } + { + auto* bsc = group->createNewChildElement ("Bscmake"); + bsc->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true"); + bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type))); + } - if (auto manifestFile = getOwner().getManifestPath(); manifestFile.getRoot() != build_tools::RelativePath::unknown || type == VST3Helper) - { - auto* bsc = group->createNewChildElement ("Manifest"); - auto* additional = bsc->createNewChildElement ("AdditionalManifestFiles"); + if (type != SharedCodeTarget && type != LV2Helper && type != VST3Helper) + { + auto* lib = group->createNewChildElement ("Lib"); + + if (additionalDependencies.isNotEmpty()) + lib->createNewChildElement ("AdditionalDependencies")->addTextElement (additionalDependencies); + + if (additionalLibraryDirs.isNotEmpty()) + lib->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs); + } - if (manifestFile.getRoot() != build_tools::RelativePath::unknown) + if (auto manifestFile = getOwner().getManifestPath(); manifestFile.getRoot() != build_tools::RelativePath::unknown || type == VST3Helper) { - additional->addTextElement (manifestFile.rebased (getOwner().getProject().getFile().getParentDirectory(), - getOwner().getTargetFolder(), - build_tools::RelativePath::buildTargetFolder).toWindowsStyle()); + auto* bsc = group->createNewChildElement ("Manifest"); + auto* additional = bsc->createNewChildElement ("AdditionalManifestFiles"); + + if (manifestFile.getRoot() != build_tools::RelativePath::unknown) + { + additional->addTextElement (manifestFile.rebased (getOwner().getProject().getFile().getParentDirectory(), + getOwner().getTargetFolder(), + build_tools::RelativePath::buildTargetFolder).toWindowsStyle()); + } } - } - if (getTargetFileType() == staticLibrary && config.getArchitectureString() == "Win32") - { - auto* lib = group->createNewChildElement ("Lib"); - lib->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86"); - } + if (getTargetFileType() == staticLibrary && arch == Architecture::win32) + { + auto* lib = group->createNewChildElement ("Lib"); + lib->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86"); + } - auto preBuild = getPreBuildSteps (config); - if (preBuild.isNotEmpty()) - group->createNewChildElement ("PreBuildEvent") - ->createNewChildElement ("Command") - ->addTextElement (preBuild); - - auto postBuild = getPostBuildSteps (config); - if (postBuild.isNotEmpty()) - group->createNewChildElement ("PostBuildEvent") - ->createNewChildElement ("Command") - ->addTextElement (postBuild); + auto preBuild = getPreBuildSteps (config, arch); + if (preBuild.isNotEmpty()) + group->createNewChildElement ("PreBuildEvent") + ->createNewChildElement ("Command") + ->addTextElement (preBuild); + + auto postBuild = getPostBuildSteps (config, arch); + if (postBuild.isNotEmpty()) + group->createNewChildElement ("PostBuildEvent") + ->createNewChildElement ("Command") + ->addTextElement (postBuild); + } } std::unique_ptr otherFilesGroup (new XmlElement ("ItemGroup")); @@ -862,11 +1323,15 @@ class MSVCProjectExporterBase : public ProjectExporter } //============================================================================== - static void setSourceFilePCHSettings (XmlElement& element, const File& pchFile, const String& option, const BuildConfiguration& config) + static void setSourceFilePCHSettings (XmlElement& element, + const File& pchFile, + const String& option, + const BuildConfiguration& config, + Architecture arch) { - auto setConfigConditionAttribute = [&config] (XmlElement* elementToSet) -> XmlElement* + auto setConfigConditionAttribute = [&config, arch] (XmlElement* elementToSet) -> XmlElement* { - setConditionAttribute (*elementToSet, config); + setConditionAttribute (*elementToSet, config, arch); return elementToSet; }; @@ -878,39 +1343,44 @@ class MSVCProjectExporterBase : public ProjectExporter void writePrecompiledHeaderFiles (XmlElement& cpps) const { - for (ConstConfigIterator config (owner); config.next();) + for (ConstConfigIterator i (owner); i.next();) { - if (config->shouldUsePrecompiledHeaderFile()) + if (! i->shouldUsePrecompiledHeaderFile()) + continue; + + auto& config = *static_cast (&*i); + + for (const auto& arch : config.getArchitectures()) { - auto pchFileContent = config->getPrecompiledHeaderFileContent(); + auto pchFileContent = config.getPrecompiledHeaderFileContent(); - if (pchFileContent.isNotEmpty()) - { - auto pchFile = owner.getTargetFolder().getChildFile (config->getPrecompiledHeaderFilename()).withFileExtension (".h"); + if (pchFileContent.isEmpty()) + continue; - build_tools::writeStreamToFile (pchFile, [&] (MemoryOutputStream& mo) - { - mo << pchFileContent; - }); + auto pchFile = owner.getTargetFolder().getChildFile (config.getPrecompiledHeaderFilename()).withFileExtension (".h"); - auto pchSourceFile = pchFile.withFileExtension (".cpp"); + build_tools::writeStreamToFile (pchFile, [&] (MemoryOutputStream& mo) + { + mo << pchFileContent; + }); - build_tools::writeStreamToFile (pchSourceFile, [this] (MemoryOutputStream& mo) - { - mo.setNewLineString (owner.getNewLineString()); + auto pchSourceFile = pchFile.withFileExtension (".cpp"); - writeAutoGenWarningComment (mo); + build_tools::writeStreamToFile (pchSourceFile, [this] (MemoryOutputStream& mo) + { + mo.setNewLineString (owner.getNewLineString()); - mo << " This is an empty source file generated by JUCE required for Visual Studio PCH." << newLine - << newLine - << "*/" << newLine - << newLine; - }); + writeAutoGenWarningComment (mo); - auto* pchSourceElement = cpps.createNewChildElement ("ClCompile"); - pchSourceElement->setAttribute ("Include", prependDot (pchSourceFile.getFileName())); - setSourceFilePCHSettings (*pchSourceElement, pchFile, "Create", *config); - } + mo << " This is an empty source file generated by JUCE required for Visual Studio PCH." << newLine + << newLine + << "*/" << newLine + << newLine; + }); + + auto* pchSourceElement = cpps.createNewChildElement ("ClCompile"); + pchSourceElement->setAttribute ("Include", prependDot (pchSourceFile.getFileName())); + setSourceFilePCHSettings (*pchSourceElement, pchFile, "Create", config, arch); } } } @@ -958,12 +1428,17 @@ class MSVCProjectExporterBase : public ProjectExporter { for (ConstConfigIterator i (owner); i.next();) { - if (i->shouldUsePrecompiledHeaderFile()) + auto& config = *static_cast (&*i); + + if (config.shouldUsePrecompiledHeaderFile()) { - auto pchFile = owner.getTargetFolder().getChildFile (i->getPrecompiledHeaderFilename()).withFileExtension (".h"); + for (const auto& arch : config.getArchitectures()) + { + auto pchFile = owner.getTargetFolder().getChildFile (i->getPrecompiledHeaderFilename()).withFileExtension (".h"); - if (pchFile.existsAsFile()) - setSourceFilePCHSettings (*e, pchFile, "Use", *i); + if (pchFile.existsAsFile()) + setSourceFilePCHSettings (*e, pchFile, "Use", *i, arch); + } } } } @@ -984,10 +1459,10 @@ class MSVCProjectExporterBase : public ProjectExporter } } - static void setConditionAttribute (XmlElement& xml, const BuildConfiguration& config) + static void setConditionAttribute (XmlElement& xml, const BuildConfiguration& config, Architecture arch) { - auto& msvcConfig = dynamic_cast (config); - xml.setAttribute ("Condition", "'$(Configuration)|$(Platform)'=='" + msvcConfig.createMSVCConfigName() + "'"); + auto& msvcConfig = *static_cast (&config); + xml.setAttribute ("Condition", "'$(Configuration)|$(Platform)'=='" + msvcConfig.createMSVCConfigName (arch) + "'"); } //============================================================================== @@ -1064,7 +1539,7 @@ class MSVCProjectExporterBase : public ProjectExporter for (int i = 0; i < getOwner().getAllGroups().size(); ++i) { - auto& group = getOwner().getAllGroups().getReference(i); + auto& group = getOwner().getAllGroups().getReference (i); if (group.getNumChildren() > 0) addFilesToFilter (group, group.getName(), *cpps, *headers, *otherFilesGroup, *groupsXml); @@ -1239,8 +1714,10 @@ class MSVCProjectExporterBase : public ProjectExporter return aaxSdk.getChildFile ("Utilities").getChildFile ("PlugIn.ico"); } - String getExtraPostBuildSteps (const MSVCBuildConfiguration& config) const + String getExtraPostBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const { + using Builder = MSVCScriptBuilder; + const auto copyBuildOutputIntoBundle = [&] (const StringArray& segments) { return "copy /Y " @@ -1266,7 +1743,7 @@ class MSVCProjectExporterBase : public ProjectExporter const auto bundleScript = aaxSdk.getChildFile ("Utilities").getChildFile ("CreatePackage.bat"); const auto iconFilePath = getAAXIconFile(); - const auto segments = getAaxBundleStructure (config); + const auto segments = getAaxBundleStructure (config, arch); const auto pkgScript = copyBuildOutputIntoBundle (segments); @@ -1279,7 +1756,7 @@ class MSVCProjectExporterBase : public ProjectExporter + String (" ") + createRebasedPath (iconFilePath); - const auto copyScript = copyBundleToInstallDirectory (segments, config.getAAXBinaryLocationString()); + const auto copyScript = copyBundleToInstallDirectory (segments, config.getBinaryPath (Ids::aaxBinaryLocation, arch)); return pkgScript + fixScript + copyScript; } @@ -1294,7 +1771,7 @@ class MSVCProjectExporterBase : public ProjectExporter if (config.isPluginBinaryCopyStepEnabled()) { - auto copyLocation = config.getUnityPluginBinaryLocationString(); + auto copyLocation = config.getBinaryPath (Ids::unityPluginBinaryLocation, arch); pkgScript += "\r\ncopy /Y \"$(OutDir)$(TargetFileName)\" " + String (copyLocation + "\\$(TargetFileName)").quoted(); pkgScript += "\r\ncopy /Y " + String ("$(OutDir)" + config.project.getUnityScriptName()).quoted() + " " + String (copyLocation + "\\" + config.project.getUnityScriptName()).quoted(); @@ -1314,24 +1791,29 @@ class MSVCProjectExporterBase : public ProjectExporter return nullptr; }(); - const auto writer = writerTarget->getExpandedConfigTargetPath (config) + Builder builder; + const auto writer = (writerTarget->getExpandedConfigTargetPath (config) + "\\" - + writerTarget->getBinaryNameWithSuffix (config); + + writerTarget->getBinaryNameWithSuffix (config)).quoted() + + " \"$(OutDir)$(TargetFileName)\"\r\n"; const auto copyStep = "xcopy /E /H /I /K /R /Y \"$(OutDir)\" \"" - + config.getLV2BinaryLocationString() + + config.getBinaryPath (Ids::lv2BinaryLocation, arch) + '\\' + config.getTargetBinaryNameString() + ".lv2\"\r\n"; - return writer.quoted() - + " \"$(OutDir)$(TargetFileName)\"\r\n" - + (config.isPluginBinaryCopyStepEnabled() ? copyStep : ""); + builder.runAndCheck (writer, + config.isPluginBinaryCopyStepEnabled() ? copyStep : Builder{}.info ("Sucessfully generated LV2 manifest").build(), + Builder{}.error ("Failed to generate LV2 manifest.") + .exit (-1)); + + return builder.build(); } if (type == VST3PlugIn) { - const auto segments = getVst3BundleStructure (config); + const auto segments = getVst3BundleStructure (config, arch); const auto manifestScript = [&]() -> String { @@ -1347,77 +1829,200 @@ class MSVCProjectExporterBase : public ProjectExporter if (writerTarget == nullptr) return ""; - const auto writer = writerTarget->getExpandedConfigTargetPath (config) - + "\\" - + writerTarget->getBinaryNameWithSuffix (config); - - // moduleinfotool doesn't handle Windows-style path separators properly when computing the bundle name - const auto normalisedBundlePath = getOwner().getOutDirFile (config, segments[0]).replace ("\\", "/"); - const auto contentsDir = normalisedBundlePath + "\\Contents"; - const auto resourceDir = contentsDir + "\\Resources"; - - return "\r\ndel /s /q " + (contentsDir + "\\moduleinfo.json").quoted() + "\r\n" - "if not exist \"" + resourceDir + "\\\" del /s /q " + resourceDir.quoted() + " && mkdir " + resourceDir.quoted() + "\r\n" - + writer.quoted() - + " -create -version " - + getOwner().project.getVersionString().quoted() - + " -path " - + normalisedBundlePath.quoted() - + " -output " - + (resourceDir + "\\moduleinfo.json").quoted(); + const auto helperExecutablePath = writerTarget->getExpandedConfigTargetPath (config) + + "\\" + + writerTarget->getBinaryNameWithSuffix (config); + + { + // moduleinfotool doesn't handle Windows-style path separators properly when computing the bundle name + const auto normalisedBundlePath = getOwner().getOutDirFile (config, segments[0]).replace ("\\", "/"); + const auto contentsDir = normalisedBundlePath + "\\Contents"; + const auto resourceDir = contentsDir + "\\Resources"; + const auto manifestPath = (resourceDir + "\\moduleinfo.json"); + const auto resourceDirPath = resourceDir + "\\"; + const auto pluginName = getOwner().project.getPluginNameString(); + + const auto manifestInvocationString = StringArray + { + helperExecutablePath.quoted(), + "-create", + "-version", getOwner().project.getVersionString().quoted(), + "-path", normalisedBundlePath.quoted(), + "-output", manifestPath.quoted() + }.joinIntoString (" "); + + const auto crossCompilationPairs = + { + // This catches ARM64 and EC for x64 manifest generation + std::pair { Architecture::arm64, Architecture::win64 }, + std::pair { arch, arch } + }; + + Builder builder; + + builder.set ("manifest_generated", 0); + + for (auto [hostArch, targetArch] : crossCompilationPairs) + { + const StringArray expr + { + "\"$(PROCESSOR_ARCHITECTURE)\" == " + getVisualStudioArchitectureId (hostArch).quoted(), + "\"$(Platform)\"" " == " + getVisualStudioPlatformId (targetArch).quoted() + }; + + builder.ifAllConditionsTrue (expr, Builder{}.call ("_generate_manifest") + .set ("manifest_generated", 1)); + } + + const auto archMismatchErrorString = StringArray + { + "VST3 manifest generation is disabled for", + pluginName, + "because a", + getVisualStudioArchitectureId (arch), + "manifest helper cannot run on a host system", + "processor detected to be $(PROCESSOR_ARCHITECTURE)." + }.joinIntoString (" "); + + const auto architectureMatched = Builder{} + .ifelse ("exist " + manifestPath.quoted(), + Builder{}.deleteFile (manifestPath.quoted())) + .ifelse ("not exist " + resourceDirPath.quoted(), + Builder{}.mkdir (resourceDirPath.quoted())) + .runAndCheck (manifestInvocationString, + Builder{}.info ("Successfully generated a manifest for " + pluginName) + .jump ("_continue"), + Builder{}.info ("The manifest helper failed") + .jump ("_continue")); + + builder.ifelse ("%manifest_generated% equ 0", + Builder{}.jump ("_arch_mismatch")); + + builder.jump ("_continue"); + builder.labelledSection ("_generate_manifest", architectureMatched); + builder.labelledSection ("_arch_mismatch", Builder{}.info (archMismatchErrorString)); + builder.labelledSection ("_continue", ""); + + return builder.build(); + } }(); const auto pkgScript = copyBuildOutputIntoBundle (segments); - const auto copyScript = copyBundleToInstallDirectory (segments, config.getVST3BinaryLocationString()); - - return pkgScript + manifestScript + copyScript; + const auto copyScript = copyBundleToInstallDirectory (segments, config.getBinaryPath (Ids::vst3BinaryLocation, arch)); + + const auto binCopyScript = config.isPluginBinaryCopyStepEnabled() + ? MSVCScriptBuilder{}.append ("copy /Y \"$(OutDir)$(TargetFileName)\" \"" + + config.getBinaryPath (Ids::vstBinaryLocation, arch) + + "\\$(TargetFileName)\"") + : MSVCScriptBuilder{}; + + return MSVCScriptBuilder{} + .append (pkgScript) + .append (manifestScript) + .append (copyScript) + .append (binCopyScript) + .build(); } - if (type == VSTPlugIn && config.isPluginBinaryCopyStepEnabled()) - return "copy /Y \"$(OutDir)$(TargetFileName)\" \"" + config.getVSTBinaryLocationString() + "\\$(TargetFileName)\""; - return {}; } - String getExtraPreBuildSteps (const MSVCBuildConfiguration& config) const + String getExtraPreBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const { const auto createBundleStructure = [&] (const StringArray& segments) { auto directory = getOwner().getOutDirFile (config, ""); - String script; + MSVCScriptBuilder script; std::for_each (segments.begin(), std::prev (segments.end()), [&] (const auto& s) { directory += (directory.isEmpty() ? "" : "\\") + s; - script += "if not exist \"" + directory + "\\\" del /s /q " + directory.quoted() + " && mkdir " + directory.quoted() + "\r\n"; + + script.ifelse ("not exist " + (directory + "\\").quoted(), + MSVCScriptBuilder{}.deleteFile (directory.quoted()) + .mkdir (directory.quoted()).build()); }); - return script; + return script.build(); }; + if (type == LV2PlugIn) + { + const auto crossCompilationPairs = + { + // This catches ARM64 and EC for x64 manifest generation + std::pair { Architecture::arm64, Architecture::win64 }, + std::pair { arch, arch } + }; + + MSVCScriptBuilder builder; + + for (auto [hostArch, targetArch] : crossCompilationPairs) + { + const StringArray expr + { + "\"$(PROCESSOR_ARCHITECTURE)\" == " + getVisualStudioArchitectureId (hostArch).quoted(), + "\"$(Platform)\"" " == " + getVisualStudioPlatformId (targetArch).quoted() + }; + + builder.ifAllConditionsTrue (expr, MSVCScriptBuilder{}.jump ("_continue")); + } + + builder.error (StringArray + { + "\"$(Platform)\"", + "LV2 cross-compilation is not available on", + "\"$(PROCESSOR_ARCHITECTURE)\" hosts." + }.joinIntoString (" ")); + builder.exit (-1); + builder.labelledSection ("_continue", ""); + + return builder.build(); + } + if (type == AAXPlugIn) - return createBundleStructure (getAaxBundleStructure (config)); + return createBundleStructure (getAaxBundleStructure (config, arch)); if (type == VST3PlugIn) - return createBundleStructure (getVst3BundleStructure (config)); + return createBundleStructure (getVst3BundleStructure (config, arch)); return {}; } - String getPostBuildSteps (const MSVCBuildConfiguration& config) const + String getPostBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const { - auto postBuild = config.getPostbuildCommandString().replace ("\n", "\r\n");; - auto extraPostBuild = getExtraPostBuildSteps (config); + const auto post = config.getPostbuildCommandString(); + const auto extra = getExtraPostBuildSteps (config, arch); + + if (post.isNotEmpty() || extra.isNotEmpty()) + { + return MSVCScriptBuilder{} + .append ("cmd /c (") + .append (post.replace ("\n", "\r\n")) + .append (extra) + .append (")") + .build(); + } - return postBuild + String (postBuild.isNotEmpty() && extraPostBuild.isNotEmpty() ? "\r\n" : "") + extraPostBuild; + return ""; } - String getPreBuildSteps (const MSVCBuildConfiguration& config) const + String getPreBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const { - auto preBuild = config.getPrebuildCommandString().replace ("\n", "\r\n");; - auto extraPreBuild = getExtraPreBuildSteps (config); + const auto pre = config.getPrebuildCommandString(); + const auto extra = getExtraPreBuildSteps (config, arch); + + if (pre.isNotEmpty() || extra.isNotEmpty()) + { + return MSVCScriptBuilder{} + .append ("cmd /c (") + .append (pre.replace ("\n", "\r\n")) + .append (extra) + .append (")") + .build(); + } - return preBuild + String (preBuild.isNotEmpty() && extraPreBuild.isNotEmpty() ? "\r\n" : "") + extraPreBuild; + return ""; } String getBinaryNameWithSuffix (const MSVCBuildConfiguration& config) const @@ -1497,24 +2102,31 @@ class MSVCProjectExporterBase : public ProjectExporter } protected: - StringArray getAaxBundleStructure (const MSVCBuildConfiguration& config) const + StringArray getAaxBundleStructure (const MSVCBuildConfiguration& config, Architecture arch) const { const auto dllName = config.getOutputFilename (".aaxplugin", false, type); - return { dllName, "Contents", config.getArchitectureString(), dllName }; + return { dllName, "Contents", getArchitectureValueString (arch), dllName }; } - StringArray getVst3BundleStructure (const MSVCBuildConfiguration& config) const + StringArray getVst3BundleStructure (const MSVCBuildConfiguration& config, Architecture arch) const { - static const std::map suffixes + // These suffixes are defined in the VST3 SDK docs + const auto suffix = std::invoke ([&]() -> String { - { "Win32", "x86" }, - { "x64", "x86_64" }, - }; + switch (arch) + { + case Architecture::win32: return "x86"; + case Architecture::win64: return "x86_64"; + case Architecture::arm64: return "arm64"; + case Architecture::arm64ec: return "arm64ec"; + } - const auto iter = suffixes.find (config.getArchitectureString()); + jassertfalse; + return {}; + }); const auto dllName = config.getOutputFilename (".vst3", false, type); - return { dllName, "Contents", iter != suffixes.cend() ? iter->second + "-win" : "win", dllName }; + return { dllName, "Contents", suffix + "-win", dllName }; } const MSVCProjectExporterBase& owner; @@ -1651,7 +2263,7 @@ class MSVCProjectExporterBase : public ProjectExporter msvcExtraPreprocessorDefs.set ("_CRT_SECURE_NO_WARNINGS", ""); if (type.isCommandLineApp()) - msvcExtraPreprocessorDefs.set("_CONSOLE", ""); + msvcExtraPreprocessorDefs.set ("_CONSOLE", ""); callForAllSupportedTargets ([this] (build_tools::ProjectType::Target::Type targetType) { @@ -1833,23 +2445,41 @@ class MSVCProjectExporterBase : public ProjectExporter for (ConstConfigIterator i (*this); i.next();) { - auto& config = dynamic_cast (*i); - auto configName = config.createMSVCConfigName(); - out << "\t\t" << configName << " = " << configName << newLine; + auto& config = *static_cast (&*i); + + for (const auto& arch : config.getArchitectures()) + { + auto configName = config.createMSVCConfigName (arch); + out << "\t\t" << configName << " = " << configName << newLine; + } } out << "\tEndGlobalSection" << newLine << "\tGlobalSection(ProjectConfigurationPlatforms) = postSolution" << newLine; + const auto allArchitectures = getAllActiveArchitectures(); + for (auto& target : targets) + { for (ConstConfigIterator i (*this); i.next();) { - auto& config = dynamic_cast (*i); - auto configName = config.createMSVCConfigName(); + auto& config = *static_cast (&*i); - for (auto& suffix : { "ActiveCfg", "Build.0" }) - out << "\t\t" << target->getProjectGuid() << "." << configName << "." << suffix << " = " << configName << newLine; + // Add a configuration for all projects but only mark the desired to be built. + // We have to do this as VS will automatically add the entry anyway. + for (const auto& arch : allArchitectures) + { + auto configName = config.createMSVCConfigName (arch); + + out << "\t\t" << target->getProjectGuid() << "." << configName << "." << "ActiveCfg" << " = " << configName << newLine; + + const auto shouldBuild = config.shouldBuildTarget (target->type, arch) && config.getArchitectures().contains (arch); + + if (shouldBuild) + out << "\t\t" << target->getProjectGuid() << "." << configName << "." << "Build.0" << " = " << configName << newLine; + } } + } out << "\tEndGlobalSection" << newLine << "\tGlobalSection(SolutionProperties) = preSolution" << newLine diff --git a/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp b/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp index 11f7698caed4..4e66e8afedb1 100644 --- a/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp +++ b/extras/Projucer/Source/Utility/PIPs/jucer_PIPGenerator.cpp @@ -507,7 +507,7 @@ Array PIPGenerator::replaceRelativeIncludesAndGetFilesToMove() if (path.startsWith ("<") && path.endsWith (">")) continue; - auto file = pipFile.getParentDirectory().getChildFile (path); + auto file = pipFile.getSiblingFile (path); files.add (file); line = line.replace (path, file.getFileName()); diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj index b603fc4129d4..6451d6a7fcdb 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\ConsoleApp\ UnitTestRunner true + true + true $(SolutionDir)$(Platform)\$(Configuration)\ConsoleApp\ $(Platform)\$(Configuration)\ConsoleApp\ UnitTestRunner true + true + true @@ -1067,6 +1071,9 @@ true + + true + true diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters index 399197e763d2..308240036510 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1717,6 +1717,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj index 013f1f84f9da..7a0e63f8eb72 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\ConsoleApp\ UnitTestRunner true + true + true $(SolutionDir)$(Platform)\$(Configuration)\ConsoleApp\ $(Platform)\$(Configuration)\ConsoleApp\ UnitTestRunner true + true + true @@ -1067,6 +1071,9 @@ true + + true + true diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters index 97053ec167f9..182b38de9c95 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1717,6 +1717,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj index dc4478d9200c..28d7d579de2e 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj @@ -47,10 +47,14 @@ $(Platform)\$(Configuration)\Dynamic Library\ juce_dll true + true + true $(SolutionDir)$(Platform)\$(Configuration)\Dynamic Library\ $(Platform)\$(Configuration)\Dynamic Library\ juce_dll true + true + true @@ -1050,6 +1054,9 @@ true + + true + true diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters index b6dcc7091bf0..343fe5911fac 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_DynamicLibrary.vcxproj.filters @@ -1621,6 +1621,9 @@ JUCE Modules\juce_audio_processors\utilities + + JUCE Modules\juce_audio_processors\utilities + JUCE Modules\juce_audio_processors diff --git a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index 15450baa5b82..192019ac774d 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -35,8 +35,7 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample) { @@ -641,7 +640,6 @@ static AudioConversionTests audioConversionUnitTests; #endif -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index cfc841150f23..5fbad7aac56f 100644 --- a/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -569,17 +569,17 @@ class AudioBuffer */ void clear() noexcept { - if (! isClear) - { - for (int i = 0; i < numChannels; ++i) - { - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) - FloatVectorOperations::clear (channels[i], size); - JUCE_END_IGNORE_WARNINGS_MSVC - } + if (isClear) + return; - isClear = true; + for (int i = 0; i < numChannels; ++i) + { + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) + FloatVectorOperations::clear (channels[i], size); + JUCE_END_IGNORE_WARNINGS_MSVC } + + isClear = true; } /** Clears a specified region of all the channels. @@ -598,13 +598,13 @@ class AudioBuffer { jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (! isClear) - { - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::clear (channels[i] + startSample, numSamples); + if (isClear) + return; - isClear = (startSample == 0 && numSamples == size); - } + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::clear (channels[i] + startSample, numSamples); + + isClear = (startSample == 0 && numSamples == size); } /** Clears a specified region of just one channel. @@ -700,15 +700,11 @@ class AudioBuffer jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (! approximatelyEqual (gain, Type (1)) && ! isClear) - { - auto* d = channels[channel] + startSample; + if (isClear) + return; - if (approximatelyEqual (gain, Type())) - FloatVectorOperations::clear (d, numSamples); - else - FloatVectorOperations::multiply (d, gain, numSamples); - } + auto* d = channels[channel] + startSample; + FloatVectorOperations::multiply (d, gain, numSamples); } /** Applies a gain multiple to a region of all the channels. @@ -740,26 +736,19 @@ class AudioBuffer void applyGainRamp (int channel, int startSample, int numSamples, Type startGain, Type endGain) noexcept { - if (! isClear) - { - if (approximatelyEqual (startGain, endGain)) - { - applyGain (channel, startSample, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - const auto increment = (endGain - startGain) / (float) numSamples; - auto* d = channels[channel] + startSample; + if (isClear) + return; - while (--numSamples >= 0) - { - *d++ *= startGain; - startGain += increment; - } - } + const auto increment = (endGain - startGain) / (float) numSamples; + auto* d = channels[channel] + startSample; + + while (--numSamples >= 0) + { + *d++ *= startGain; + startGain += increment; } } @@ -812,32 +801,25 @@ class AudioBuffer jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - if (! approximatelyEqual (gainToApplyToSource, (Type) 0) && numSamples > 0 && ! source.isClear) - { - auto* d = channels[destChannel] + destStartSample; - auto* s = source.channels[sourceChannel] + sourceStartSample; + if (numSamples <= 0 || source.isClear) + return; - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) + auto* d = channels[destChannel] + destStartSample; + auto* s = source.channels[sourceChannel] + sourceStartSample; - if (isClear) - { - isClear = false; - - if (! approximatelyEqual (gainToApplyToSource, Type (1))) - FloatVectorOperations::copyWithMultiply (d, s, gainToApplyToSource, numSamples); - else - FloatVectorOperations::copy (d, s, numSamples); - } - else - { - if (! approximatelyEqual (gainToApplyToSource, Type (1))) - FloatVectorOperations::addWithMultiply (d, s, gainToApplyToSource, numSamples); - else - FloatVectorOperations::add (d, s, numSamples); - } + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4661) - JUCE_END_IGNORE_WARNINGS_MSVC + if (isClear) + { + isClear = false; + FloatVectorOperations::copyWithMultiply (d, s, gainToApplyToSource, numSamples); } + else + { + FloatVectorOperations::addWithMultiply (d, s, gainToApplyToSource, numSamples); + } + + JUCE_END_IGNORE_WARNINGS_MSVC } /** Adds samples from an array of floats to one of the channels. @@ -864,26 +846,19 @@ class AudioBuffer jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); - if (! approximatelyEqual (gainToApplyToSource, Type()) && numSamples > 0) - { - auto* d = channels[destChannel] + destStartSample; + if (numSamples <= 0) + return; - if (isClear) - { - isClear = false; + auto* d = channels[destChannel] + destStartSample; - if (! approximatelyEqual (gainToApplyToSource, Type (1))) - FloatVectorOperations::copyWithMultiply (d, source, gainToApplyToSource, numSamples); - else - FloatVectorOperations::copy (d, source, numSamples); - } - else - { - if (! approximatelyEqual (gainToApplyToSource, Type (1))) - FloatVectorOperations::addWithMultiply (d, source, gainToApplyToSource, numSamples); - else - FloatVectorOperations::add (d, source, numSamples); - } + if (isClear) + { + isClear = false; + FloatVectorOperations::copyWithMultiply (d, source, gainToApplyToSource, numSamples); + } + else + { + FloatVectorOperations::addWithMultiply (d, source, gainToApplyToSource, numSamples); } } @@ -913,30 +888,23 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (approximatelyEqual (startGain, endGain)) - { - addFrom (destChannel, destStartSample, source, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); + jassert (isPositiveAndBelow (destChannel, numChannels)); + jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); + jassert (source != nullptr); - if (numSamples > 0) - { - isClear = false; - const auto increment = (endGain - startGain) / (Type) numSamples; - auto* d = channels[destChannel] + destStartSample; + if (numSamples <= 0) + return; - while (--numSamples >= 0) - { - *d++ += startGain * *source++; - startGain += increment; - } - } + isClear = false; + const auto increment = (endGain - startGain) / (Type) numSamples; + auto* d = channels[destChannel] + destStartSample; + + while (--numSamples >= 0) + { + *d++ += startGain * *source++; + startGain += increment; } - } +} /** Copies samples from another buffer to this one. @@ -965,20 +933,20 @@ class AudioBuffer jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && numSamples >= 0 && sourceStartSample + numSamples <= source.size); - if (numSamples > 0) + if (numSamples <= 0) + return; + + if (source.isClear) { - if (source.isClear) - { - if (! isClear) - FloatVectorOperations::clear (channels[destChannel] + destStartSample, numSamples); - } - else - { - isClear = false; - FloatVectorOperations::copy (channels[destChannel] + destStartSample, - source.channels[sourceChannel] + sourceStartSample, - numSamples); - } + if (! isClear) + FloatVectorOperations::clear (channels[destChannel] + destStartSample, numSamples); + } + else + { + isClear = false; + FloatVectorOperations::copy (channels[destChannel] + destStartSample, + source.channels[sourceChannel] + sourceStartSample, + numSamples); } } @@ -1003,11 +971,11 @@ class AudioBuffer jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); - if (numSamples > 0) - { - isClear = false; - FloatVectorOperations::copy (channels[destChannel] + destStartSample, source, numSamples); - } + if (numSamples <= 0) + return; + + isClear = false; + FloatVectorOperations::copy (channels[destChannel] + destStartSample, source, numSamples); } /** Copies samples from an array of floats into one of the channels, applying a gain to it. @@ -1033,29 +1001,12 @@ class AudioBuffer jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); jassert (source != nullptr); - if (numSamples > 0) - { - auto* d = channels[destChannel] + destStartSample; + if (numSamples <= 0) + return; - if (! approximatelyEqual (gain, Type (1))) - { - if (approximatelyEqual (gain, Type())) - { - if (! isClear) - FloatVectorOperations::clear (d, numSamples); - } - else - { - isClear = false; - FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); - } - } - else - { - isClear = false; - FloatVectorOperations::copy (d, source, numSamples); - } - } + auto* d = channels[destChannel] + destStartSample; + isClear = false; + FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); } /** Copies samples from an array of floats into one of the channels, applying a gain ramp. @@ -1085,28 +1036,21 @@ class AudioBuffer Type startGain, Type endGain) noexcept { - if (approximatelyEqual (startGain, endGain)) - { - copyFrom (destChannel, destStartSample, source, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); + jassert (isPositiveAndBelow (destChannel, numChannels)); + jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); + jassert (source != nullptr); - if (numSamples > 0) - { - isClear = false; - const auto increment = (endGain - startGain) / (Type) numSamples; - auto* d = channels[destChannel] + destStartSample; + if (numSamples <= 0) + return; - while (--numSamples >= 0) - { - *d++ = startGain * *source++; - startGain += increment; - } - } + isClear = false; + const auto increment = (endGain - startGain) / (Type) numSamples; + auto* d = channels[destChannel] + destStartSample; + + while (--numSamples >= 0) + { + *d++ = startGain * *source++; + startGain += increment; } } @@ -1136,8 +1080,7 @@ class AudioBuffer if (isClear) return Type (0); - auto r = findMinMax (channel, startSample, numSamples); - + const auto r = findMinMax (channel, startSample, numSamples); return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd()); } @@ -1146,9 +1089,11 @@ class AudioBuffer { Type mag (0); - if (! isClear) - for (int i = 0; i < numChannels; ++i) - mag = jmax (mag, getMagnitude (i, startSample, numSamples)); + if (isClear) + return mag; + + for (int i = 0; i < numChannels; ++i) + mag = jmax (mag, getMagnitude (i, startSample, numSamples)); return mag; } @@ -1159,7 +1104,7 @@ class AudioBuffer jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear) + if (numSamples <= 0 || isClear || ! isPositiveAndBelow (channel, numChannels)) return Type (0); auto* data = channels[channel] + startSample; @@ -1180,14 +1125,21 @@ class AudioBuffer jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - if (! isClear) - std::reverse (channels[channel] + startSample, - channels[channel] + startSample + numSamples); + if (isClear) + return; + + std::reverse (channels[channel] + startSample, + channels[channel] + startSample + numSamples); } /** Reverses a part of the buffer. */ void reverse (int startSample, int numSamples) const noexcept { + jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); + + if (isClear) + return; + for (int i = 0; i < numChannels; ++i) reverse (i, startSample, numSamples); } diff --git a/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index fa1dafc6bf3b..b089840e8bff 100644 --- a/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -221,8 +221,7 @@ MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const } //============================================================================== -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept : buffer (b), iterator (b.data.begin()) @@ -257,8 +256,7 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio return true; } -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS //============================================================================== //============================================================================== diff --git a/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index 940cb001116c..530a880f3add 100644 --- a/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -1231,8 +1231,7 @@ struct MidiMessageTest final : public UnitTest size_t index = 0; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS for (const auto& input : inputs) { @@ -1258,8 +1257,7 @@ struct MidiMessageTest final : public UnitTest ++index; } - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_DEPRECATION_WARNINGS } beginTest ("ReadVariableLengthVal should return 0 if input is truncated"); diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index c0c9537f93fe..682b8de3ff8f 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -1398,6 +1398,8 @@ class AudioDeviceManagerTests final : public UnitTest void runTest() override { + ScopedJuceInitialiser_GUI libraryInitialiser; + beginTest ("When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices"); { AudioDeviceManager manager; diff --git a/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp b/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp index 89e843d765b4..c80d2168de03 100644 --- a/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp +++ b/modules/juce_audio_devices/midi_io/juce_MidiDeviceListConnectionBroadcaster.cpp @@ -57,20 +57,22 @@ class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater void notify() { - if (MessageManager::getInstance()->isThisTheMessageThread()) - { - cancelPendingUpdate(); + auto* mm = MessageManager::getInstanceWithoutCreating(); - const State newState; + if (mm == nullptr) + return; - if (std::exchange (lastNotifiedState, newState) != newState) - for (auto it = callbacks.begin(); it != callbacks.end();) - NullCheckedInvocation::invoke ((it++)->second); - } - else + if (! mm->isThisTheMessageThread()) { triggerAsyncUpdate(); + return; } + + cancelPendingUpdate(); + + if (auto prev = std::exchange (lastNotifiedState, State{}); prev != lastNotifiedState) + for (auto it = callbacks.begin(); it != callbacks.end();) + NullCheckedInvocation::invoke ((it++)->second); } static auto& get() @@ -108,7 +110,7 @@ class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater } std::map> callbacks; - State lastNotifiedState; + std::optional lastNotifiedState; MidiDeviceListConnection::Key key = 0; }; diff --git a/modules/juce_audio_devices/native/juce_Audio_ios.cpp b/modules/juce_audio_devices/native/juce_Audio_ios.cpp index 709777cec0f2..36f2449dfd46 100644 --- a/modules/juce_audio_devices/native/juce_Audio_ios.cpp +++ b/modules/juce_audio_devices/native/juce_Audio_ios.cpp @@ -518,10 +518,6 @@ struct iOSAudioIODevice::Pimpl final : public AsyncUpdater availableSampleRates.addIfNotAlreadyThere (highestRate); - // Reset sample rate back to the original, so that we don't end up stuck on the highest rate - sampleRate = trySampleRate (sampleRate); - bufferSize = getBufferSize (sampleRate); - AudioUnitAddPropertyListener (audioUnit, kAudioUnitProperty_StreamFormat, dispatchAudioUnitPropertyChange, @@ -553,6 +549,13 @@ struct iOSAudioIODevice::Pimpl final : public AsyncUpdater JUCE_IOS_AUDIO_LOG ("Updating hardware info"); updateAvailableSampleRates(); + + // The sample rate and buffer size may have been affected by + // updateAvailableSampleRates(), so try restoring the last good + // sample rate + sampleRate = trySampleRate (sampleRate); + bufferSize = getBufferSize (sampleRate); + updateAvailableBufferSizes(); if (deviceType != nullptr) @@ -815,7 +818,7 @@ struct iOSAudioIODevice::Pimpl final : public AsyncUpdater //============================================================================== #if JUCE_MODULE_AVAILABLE_juce_graphics - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS Image getIcon (int size) { #if TARGET_OS_MACCATALYST @@ -831,7 +834,7 @@ struct iOSAudioIODevice::Pimpl final : public AsyncUpdater return {}; } - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif void switchApplication() diff --git a/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index 69cbd1b30ce6..8bf92ba0aa72 100644 --- a/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -168,8 +168,9 @@ double AudioTransportSource::getLengthInSeconds() const bool AudioTransportSource::hasStreamFinished() const noexcept { - return positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 - && ! positionableSource->isLooping(); + return positionableSource == nullptr + || (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 + && ! positionableSource->isLooping()); } void AudioTransportSource::setNextReadPosition (int64 newPosition) diff --git a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index e45016b49328..15f744ac16e9 100644 --- a/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -44,11 +44,10 @@ namespace juce namespace OggVorbisNamespace { #if JUCE_INCLUDE_OGGVORBIS_CODE || ! defined (JUCE_INCLUDE_OGGVORBIS_CODE) - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459 6297 6011 6001 6308 6255 6386 6385 6246 6387 6263 6262 28182) + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4267 4127 4244 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365 4456 4457 4459 6297 6011 6001 6308 6255 6386 6385 6246 6387 6263 6262 28182) JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-align", "-Wconversion", - "-Wdeprecated-declarations", "-Wdeprecated-register", "-Wfloat-conversion", "-Wfloat-equal", @@ -61,6 +60,9 @@ namespace OggVorbisNamespace "-Wswitch-default", "-Wswitch-enum", "-Wzero-as-null-pointer-constant") + + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS + JUCE_BEGIN_NO_SANITIZE ("undefined") #include "oggvorbis/vorbisenc.h" @@ -92,6 +94,7 @@ namespace OggVorbisNamespace #include "oggvorbis/libvorbis-1.3.7/lib/window.c" JUCE_END_NO_SANITIZE + JUCE_END_IGNORE_DEPRECATION_WARNINGS JUCE_END_IGNORE_WARNINGS_MSVC JUCE_END_IGNORE_WARNINGS_GCC_LIKE #else diff --git a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 3235920cf58d..77e47f79f9b5 100644 --- a/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -2145,7 +2145,11 @@ struct WaveAudioFormatTests final : public UnitTest for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;) metadataValues[WavFileHelpers::ListInfoChunk::types[i]] = WavFileHelpers::ListInfoChunk::types[i]; - metadataValues[WavAudioFormat::internationalStandardRecordingCode] = WavAudioFormat::internationalStandardRecordingCode; + const String prefixCode { "AA6Q7" }; // two letters followed by three alphanumeric characters + const String yearOfReference { "20" }; // two digits, 20 meaning the year 2020 + const String designationCode { "00047" }; // five digits + + metadataValues[WavAudioFormat::internationalStandardRecordingCode] = prefixCode + yearOfReference + designationCode;; if (metadataValues.size() > 0) metadataValues["MetaDataSource"] = "WAV"; diff --git a/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 57efbccf7dd1..5d61027bc502 100644 --- a/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -299,11 +299,12 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, return -1; const int bufferSize = 4096; - HeapBlock tempSpace (bufferSize * 2 + 64); + const size_t channels = numChannels; + HeapBlock tempSpace (bufferSize * channels + 64); + std::vector channelPointers (channels); - int* tempBuffer[3] = { tempSpace.get(), - tempSpace.get() + bufferSize, - nullptr }; + for (auto [index, ptr] : enumerate (channelPointers, size_t{})) + ptr = tempSpace + (bufferSize * index); int consecutive = 0; int64 firstMatchPos = -1; @@ -326,7 +327,7 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, if (bufferStart >= lengthInSamples) break; - read (tempBuffer, 2, bufferStart, numThisTime, false); + read (channelPointers.data(), (int) channels, bufferStart, numThisTime, false); auto num = numThisTime; while (--num >= 0) @@ -334,43 +335,25 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, if (numSamplesToSearch < 0) --startSample; - bool matches = false; auto index = (int) (startSample - bufferStart); - if (usesFloatingPointData) + const auto matches = std::invoke ([&] { - const float sample1 = std::abs (((float*) tempBuffer[0]) [index]); - - if (sample1 >= magnitudeRangeMinimum - && sample1 <= magnitudeRangeMaximum) + if (usesFloatingPointData) { - matches = true; + return std::any_of (channelPointers.begin(), channelPointers.end(), [&] (const auto& ptr) + { + const float sample = std::abs (((float*) ptr) [index]); + return magnitudeRangeMinimum <= sample && sample <= magnitudeRangeMaximum; + }); } - else if (numChannels > 1) - { - const float sample2 = std::abs (((float*) tempBuffer[1]) [index]); - matches = (sample2 >= magnitudeRangeMinimum - && sample2 <= magnitudeRangeMaximum); - } - } - else - { - const int sample1 = std::abs (tempBuffer[0] [index]); - - if (sample1 >= intMagnitudeRangeMinimum - && sample1 <= intMagnitudeRangeMaximum) - { - matches = true; - } - else if (numChannels > 1) + return std::any_of (channelPointers.begin(), channelPointers.end(), [&] (const auto& ptr) { - const int sample2 = std::abs (tempBuffer[1][index]); - - matches = (sample2 >= intMagnitudeRangeMinimum - && sample2 <= intMagnitudeRangeMaximum); - } - } + const int sample = std::abs (ptr[index]); + return intMagnitudeRangeMinimum <= sample && sample <= intMagnitudeRangeMaximum; + }); + }); if (matches) { diff --git a/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp b/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp index 93d1564913a4..be8cbe77f13b 100644 --- a/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp +++ b/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp @@ -61,46 +61,256 @@ void AudioFormatReaderSource::releaseResources() {} void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) { - if (info.numSamples > 0) + if (info.numSamples <= 0) + return; + + for (auto destOffset = 0; destOffset < info.numSamples;) { - const int64 start = nextPlayPos; + const auto readFrom = looping ? nextPlayPos % reader->lengthInSamples : nextPlayPos; + + const auto numSamplesToRead = jlimit ((int64) 0, + (int64) (info.numSamples - destOffset), + reader->lengthInSamples - readFrom); + + reader->read (info.buffer, info.startSample + destOffset, + (int) numSamplesToRead, readFrom, true, true); + + destOffset += (int) numSamplesToRead; + nextPlayPos += numSamplesToRead; + + if (! looping) + { + const auto numSamplesToClear = info.numSamples - destOffset; + info.buffer->clear (info.startSample + destOffset, numSamplesToClear); + + destOffset += numSamplesToClear; + nextPlayPos += numSamplesToClear; + } + } +} + +#if JUCE_UNIT_TESTS - if (looping) +struct AudioFormatReaderSourceTests : public UnitTest +{ + AudioFormatReaderSourceTests() + : UnitTest ("AudioFormatReaderSource", UnitTestCategories::audio) + {} + + //============================================================================== + struct GetNextAudioBlockTestParams + { + int audioFormatReaderLength; + int readFrom; + int numSamplesToRead; + bool enableLooping; + }; + + static void mockReadSamples (float* dest, int64 audioFormatReaderLength, int64 readFrom, int numSamplesToRead) + { + for (auto i = readFrom; i < readFrom + numSamplesToRead; ++i) + { + *dest = i < audioFormatReaderLength ? 0.001f * (float) i : 0.0f; + ++dest; + } + } + + static void createGetNextAudioBlockExpectedOutput (const GetNextAudioBlockTestParams& params, + std::vector& expected) + { + for (auto i = params.readFrom, end = i + params.numSamplesToRead; i < end; ++i) { - const int64 newStart = start % reader->lengthInSamples; - const int64 newEnd = (start + info.numSamples) % reader->lengthInSamples; + const auto expectedResult = params.enableLooping || i < params.audioFormatReaderLength + ? 0.001f * (float) (i % params.audioFormatReaderLength) + : 0.0f; + + expected.push_back (expectedResult); + } + } + + //============================================================================== + struct TestAudioFormatReader : public AudioFormatReader + { + explicit TestAudioFormatReader (int audioFormatReaderLength) + : AudioFormatReader { nullptr, "test_format" } + { + jassert (audioFormatReaderLength < 1000); + + lengthInSamples = (int64) audioFormatReaderLength; + numChannels = 1; + usesFloatingPointData = true; + bitsPerSample = 32; + } + + void readMaxLevels (int64, int64, Range*, int) override { jassertfalse; } + void readMaxLevels (int64, int64, float&, float&, float&, float&) override { jassertfalse; } + + AudioChannelSet getChannelLayout() override + { + return AudioChannelSet::mono(); + } + + bool readSamples (int* const* destChannels, + [[maybe_unused]] int numDestChannels, + int startOffsetInDestBuffer, + int64 startSampleInFile, + int numSamples) override + { + jassert (numDestChannels == 1); + mockReadSamples (reinterpret_cast (*destChannels + startOffsetInDestBuffer), + lengthInSamples, + startSampleInFile, + numSamples); + return true; + } + }; + + static auto createTestAudioFormatReaderSource (const GetNextAudioBlockTestParams& params) + { + return AudioFormatReaderSource { new TestAudioFormatReader (params.audioFormatReaderLength), true }; + } + + static void getNextAudioBlock (AudioFormatReaderSource& source, + const GetNextAudioBlockTestParams& params, + std::vector& result) + { + source.setLooping (params.enableLooping); + source.setNextReadPosition (params.readFrom); - if (newEnd > newStart) + AudioBuffer buffer { 1, params.numSamplesToRead }; + AudioSourceChannelInfo info { &buffer, 0, buffer.getNumSamples() }; + + source.getNextAudioBlock (info); + + result.insert (result.end(), + buffer.getReadPointer (0), + buffer.getReadPointer (0) + buffer.getNumSamples()); + } + + static auto createFailureMessage (const GetNextAudioBlockTestParams& params) + { + return String { "AudioFormatReaderSource::getNextAudioBlock() failed for " + "audioFormatReaderLength=%audioFormatReaderLength%, " + "readFrom=%readFrom%, " + "numSamplesToRead=%numSamplesToRead%, " + "enableLooping=%enableLooping%" } + .replace ("%audioFormatReaderLength%", String { params.audioFormatReaderLength }) + .replace ("%readFrom%", String { params.readFrom }) + .replace ("%numSamplesToRead%", String { params.numSamplesToRead }) + .replace ("%enableLooping%", params.enableLooping ? "true" : "false"); + } + + void runTest() override + { + const auto predicate = [] (auto a, auto b) { return exactlyEqual (a, b); }; + + const auto testGetNextAudioBlock = [this, &predicate] (const GetNextAudioBlockTestParams& params) + { + auto uut = createTestAudioFormatReaderSource (params); + std::vector actual; + getNextAudioBlock (uut, params, actual); + + std::vector expected; + createGetNextAudioBlockExpectedOutput (params, expected); + + expect (std::equal (expected.begin(), expected.end(), actual.begin(), actual.end(), predicate), + createFailureMessage (params)); + }; + + beginTest ("A buffer without looping is played once and followed by silence"); + { + GetNextAudioBlockTestParams testParams { 32, 0, 48, false }; + testGetNextAudioBlock (testParams); + } + + beginTest ("A buffer with looping is played multiple times"); + { + GetNextAudioBlockTestParams params { 32, 0, 24, true }; + + auto uut = createTestAudioFormatReaderSource (params); + std::vector actual; + std::vector expected; + const auto numReads = 4; + + for (auto i = 0; i < numReads; ++i) { - reader->read (info.buffer, info.startSample, - (int) (newEnd - newStart), newStart, true, true); + getNextAudioBlock (uut, params, actual); + createGetNextAudioBlockExpectedOutput (params, expected); + params.readFrom += params.numSamplesToRead; } - else + + expect (std::equal (expected.begin(), expected.end(), actual.begin(), actual.end(), predicate), + createFailureMessage (params) + " numReads=" + String { numReads }); + } + + beginTest ("A buffer with looping, loops even if the blockSize is greater than the internal buffer"); + { + GetNextAudioBlockTestParams testParams { 32, 16, 128, true }; + testGetNextAudioBlock (testParams); + } + + beginTest ("Behavioural invariants hold even if we turn on looping after prior reads"); + { + GetNextAudioBlockTestParams params { 32, 0, 24, false }; + + auto uut = createTestAudioFormatReaderSource (params); + std::vector actual; + std::vector expected; + + for (auto i = 0; i < 4; ++i) { - const int endSamps = (int) (reader->lengthInSamples - newStart); + getNextAudioBlock (uut, params, actual); + createGetNextAudioBlockExpectedOutput (params, expected); + params.readFrom += params.numSamplesToRead; + } - reader->read (info.buffer, info.startSample, - endSamps, newStart, true, true); + params.enableLooping = true; - reader->read (info.buffer, info.startSample + endSamps, - (int) newEnd, 0, true, true); + for (auto i = 0; i < 4; ++i) + { + getNextAudioBlock (uut, params, actual); + createGetNextAudioBlockExpectedOutput (params, expected); + params.readFrom += params.numSamplesToRead; } - nextPlayPos = newEnd; + expect (std::equal (expected.begin(), expected.end(), actual.begin(), actual.end(), predicate), + createFailureMessage (params)); } - else + + beginTest ("Fuzzing: getNextAudioBlock() should return correct results for all possible inputs"); { - const auto samplesToRead = jlimit (int64{}, - (int64) info.numSamples, - reader->lengthInSamples - start); + for (auto params : { GetNextAudioBlockTestParams { 32, 0, 32, false }, + GetNextAudioBlockTestParams { 32, 16, 32, false }, + GetNextAudioBlockTestParams { 32, 16, 32, true }, + GetNextAudioBlockTestParams { 32, 16, 48, false }, + GetNextAudioBlockTestParams { 32, 16, 128, false }, + GetNextAudioBlockTestParams { 32, 16, 48, true }, + GetNextAudioBlockTestParams { 32, 16, 128, true } }) + { + testGetNextAudioBlock (params); + } + + const Range audioFormatReaderLengthRange { 16, 128 }; + const Range startFromRange { 0, 128 }; + const Range numSamplesRange { 0, 128 }; + + auto r = getRandom(); - reader->read (info.buffer, info.startSample, (int) samplesToRead, start, true, true); - info.buffer->clear ((int) (info.startSample + samplesToRead), - (int) (info.numSamples - samplesToRead)); + for (int i = 0; i < 100; ++i) + { + GetNextAudioBlockTestParams params { r.nextInt (audioFormatReaderLengthRange), + r.nextInt (startFromRange), + r.nextInt (numSamplesRange), + r.nextBool() }; - nextPlayPos += info.numSamples; + testGetNextAudioBlock (params); + } } } -} +}; + +static AudioFormatReaderSourceTests audioFormatReaderSourceTests; + +#endif } // namespace juce diff --git a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h index 1e7744f94c1a..ca2193dd961b 100644 --- a/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h +++ b/modules/juce_audio_plugin_client/detail/juce_PluginUtilities.h @@ -66,88 +66,6 @@ struct PluginUtilities return hostType; } - // NB: Nasty old-fashioned code in here because it's copied from the Steinberg example code. - static void getUUIDForVST2ID (bool forControllerUID, uint8 uuid[16]) - { - #if JUCE_WINDOWS - const auto juce_sprintf = [] (auto&& head, auto&&... tail) { sprintf_s (head, (size_t) numElementsInArray (head), tail...); }; - const auto juce_strcpy = [] (auto&& head, auto&&... tail) { strcpy_s (head, (size_t) numElementsInArray (head), tail...); }; - const auto juce_strcat = [] (auto&& head, auto&&... tail) { strcat_s (head, (size_t) numElementsInArray (head), tail...); }; - const auto juce_sscanf = [] (auto&&... args) { sscanf_s (args...); }; - #else - const auto juce_sprintf = [] (auto&& head, auto&&... tail) { snprintf (head, (size_t) numElementsInArray (head), tail...); }; - const auto juce_strcpy = [] (auto&&... args) { strcpy (args...); }; - const auto juce_strcat = [] (auto&&... args) { strcat (args...); }; - const auto juce_sscanf = [] (auto&&... args) { sscanf (args...); }; - #endif - - char uidString[33]; - - const int vstfxid = (('V' << 16) | ('S' << 8) | (forControllerUID ? 'E' : 'T')); - char vstfxidStr[7] = { 0 }; - juce_sprintf (vstfxidStr, "%06X", vstfxid); - - juce_strcpy (uidString, vstfxidStr); - - char uidStr[9] = { 0 }; - juce_sprintf (uidStr, "%08X", JucePlugin_VSTUniqueID); - juce_strcat (uidString, uidStr); - - char nameidStr[3] = { 0 }; - const size_t len = strlen (JucePlugin_Name); - - for (size_t i = 0; i <= 8; ++i) - { - juce::uint8 c = i < len ? static_cast (JucePlugin_Name[i]) : 0; - - if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - - juce_sprintf (nameidStr, "%02X", c); - juce_strcat (uidString, nameidStr); - } - - unsigned long p0; - unsigned int p1, p2; - unsigned int p3[8]; - - juce_sscanf (uidString, "%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", - &p0, &p1, &p2, &p3[0], &p3[1], &p3[2], &p3[3], &p3[4], &p3[5], &p3[6], &p3[7]); - - union q0_u { - uint32 word; - uint8 bytes[4]; - } q0; - - union q1_u { - uint16 half; - uint8 bytes[2]; - } q1, q2; - - q0.word = static_cast (p0); - q1.half = static_cast (p1); - q2.half = static_cast (p2); - - // VST3 doesn't use COM compatible UUIDs on non windows platforms - #if ! JUCE_WINDOWS - q0.word = ByteOrder::swap (q0.word); - q1.half = ByteOrder::swap (q1.half); - q2.half = ByteOrder::swap (q2.half); - #endif - - for (int i = 0; i < 4; ++i) - uuid[i+0] = q0.bytes[i]; - - for (int i = 0; i < 2; ++i) - uuid[i+4] = q1.bytes[i]; - - for (int i = 0; i < 2; ++i) - uuid[i+6] = q2.bytes[i]; - - for (int i = 0; i < 8; ++i) - uuid[i+8] = static_cast (p3[i]); - } - #if JucePlugin_Build_VST static bool handleManufacturerSpecificVST2Opcode ([[maybe_unused]] int32 index, [[maybe_unused]] pointer_sized_int value, @@ -158,9 +76,10 @@ struct PluginUtilities if ((index == (int32) ByteOrder::bigEndianInt ("stCA") || index == (int32) ByteOrder::bigEndianInt ("stCa")) && value == (int32) ByteOrder::bigEndianInt ("FUID") && ptr != nullptr) { - uint8 fuid[16]; - getUUIDForVST2ID (false, fuid); - ::memcpy (ptr, fuid, 16); + const auto uidString = VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, VST3ClientExtensions::InterfaceType::component); + MemoryBlock uidValue; + uidValue.loadFromHexString (uidString); + uidValue.copyTo (ptr, 0, uidValue.getSize()); return true; } #endif diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AAX.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AAX.cpp index 416423fd15bb..ade6f87bd9a9 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AAX.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AAX.cpp @@ -2466,6 +2466,9 @@ namespace AAXClasses properties->AddProperty (AAX_eProperty_InputStemFormat, static_cast (aaxInputFormat)); properties->AddProperty (AAX_eProperty_OutputStemFormat, static_cast (aaxOutputFormat)); + // If the plugin doesn't have an editor, ask the host to provide one + properties->AddProperty (AAX_eProperty_UsesClientGUI, static_cast (! processor.hasEditor())); + const auto& extensions = processor.getAAXClientExtensions(); // This value needs to match the RTAS wrapper's Type ID, so that @@ -2546,30 +2549,6 @@ namespace AAXClasses check (desc.AddProcessProc_Native (algorithmProcessCallback, properties)); } - static inline bool hostSupportsStemFormat (AAX_EStemFormat stemFormat, const AAX_IFeatureInfo* featureInfo) - { - if (featureInfo != nullptr) - { - AAX_ESupportLevel supportLevel; - - if (featureInfo->SupportLevel (supportLevel) == AAX_SUCCESS && supportLevel == AAX_eSupportLevel_ByProperty) - { - std::unique_ptr props (featureInfo->AcquireProperties()); - - // Due to a bug in ProTools 12.8, ProTools thinks that AAX_eStemFormat_Ambi_1_ACN is not supported - // To workaround this bug, check if ProTools supports AAX_eStemFormat_Ambi_2_ACN, and, if yes, - // we can safely assume that it will also support AAX_eStemFormat_Ambi_1_ACN - if (stemFormat == AAX_eStemFormat_Ambi_1_ACN) - stemFormat = AAX_eStemFormat_Ambi_2_ACN; - - if (props != nullptr && props->GetProperty ((AAX_EProperty) stemFormat, (AAX_CPropertyValue*) &supportLevel) != 0) - return (supportLevel == AAX_eSupportLevel_Supported); - } - } - - return (AAX_STEM_FORMAT_INDEX (stemFormat) <= 12); - } - static void getPlugInDescription (AAX_IEffectDescriptor& descriptor, [[maybe_unused]] const AAX_IFeatureInfo* featureInfo) { auto plugin = createPluginFilterOfType (AudioProcessor::wrapperType_AAX); @@ -2628,19 +2607,15 @@ namespace AAXClasses auto aaxOutFormat = numOuts > 0 ? aaxFormats[outIdx] : AAX_eStemFormat_None; auto outLayout = channelSetFromStemFormat (aaxOutFormat, false); - if (hostSupportsStemFormat (aaxInFormat, featureInfo) - && hostSupportsStemFormat (aaxOutFormat, featureInfo)) - { - AudioProcessor::BusesLayout fullLayout; + AudioProcessor::BusesLayout fullLayout; - if (! JuceAAX_Processor::fullBusesLayoutFromMainLayout (*plugin, inLayout, outLayout, fullLayout)) - continue; + if (! JuceAAX_Processor::fullBusesLayoutFromMainLayout (*plugin, inLayout, outLayout, fullLayout)) + continue; - if (auto* desc = descriptor.NewComponentDescriptor()) - { - createDescriptor (*desc, fullLayout, *plugin, pluginIds, numMeters); - check (descriptor.AddComponent (desc)); - } + if (auto* desc = descriptor.NewComponentDescriptor()) + { + createDescriptor (*desc, fullLayout, *plugin, pluginIds, numMeters); + check (descriptor.AddComponent (desc)); } } } diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm index 8e1375263b74..7057a0f7f923 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm @@ -2193,7 +2193,9 @@ void pushMidiOutput ([[maybe_unused]] UInt32 nFrames) noexcept const auto init = [&] { + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") end = MIDIEventListInit (&stackList, kMIDIProtocol_1_0); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE }; const auto send = [&] @@ -2206,6 +2208,7 @@ void pushMidiOutput ([[maybe_unused]] UInt32 nFrames) noexcept static_assert (sizeof (uint32_t) == sizeof (UInt32) && alignof (uint32_t) == alignof (UInt32), "If this fails, the cast below will be broken too!"); + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") using List = struct MIDIEventList; end = MIDIEventListAdd (&stackList, sizeof (List::packet), @@ -2213,6 +2216,7 @@ void pushMidiOutput ([[maybe_unused]] UInt32 nFrames) noexcept (MIDITimeStamp) timeStamp, view.size(), reinterpret_cast (view.data())); + JUCE_END_IGNORE_WARNINGS_GCC_LIKE }; init(); diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST2.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST2.cpp index f3702c6c95c6..629185e1b6c6 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST2.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST2.cpp @@ -274,10 +274,38 @@ class JuceVSTWrapper final : public AudioProcessorListener, memset (&vstEffect, 0, sizeof (vstEffect)); vstEffect.magic = 0x56737450 /* 'VstP' */; - vstEffect.dispatcher = (Vst2::AEffectDispatcherProc) dispatcherCB; + vstEffect.dispatcher = [] (Vst2::AEffect* vstInterface, + Vst2::VstInt32 opCode, + Vst2::VstInt32 index, + Vst2::VstIntPtr value, + void* ptr, + float opt) -> Vst2::VstIntPtr + { + auto* wrapper = getWrapper (vstInterface); + VstOpCodeArguments args = { index, value, ptr, opt }; + + if (opCode == Vst2::effClose) + { + wrapper->dispatcher (opCode, args); + delete wrapper; + return 1; + } + + return wrapper->dispatcher (opCode, args); + }; + vstEffect.process = nullptr; - vstEffect.setParameter = (Vst2::AEffectSetParameterProc) setParameterCB; - vstEffect.getParameter = (Vst2::AEffectGetParameterProc) getParameterCB; + + vstEffect.setParameter = [] (Vst2::AEffect* vstInterface, Vst2::VstInt32 index, float value) + { + getWrapper (vstInterface)->setParameter (index, value); + }; + + vstEffect.getParameter = [] (Vst2::AEffect* vstInterface, Vst2::VstInt32 index) -> float + { + return getWrapper (vstInterface)->getParameter (index); + }; + vstEffect.numPrograms = jmax (1, processor->getNumPrograms()); vstEffect.numParams = juceParameters.getNumParameters(); vstEffect.numInputs = maxNumInChannels; @@ -292,8 +320,21 @@ class JuceVSTWrapper final : public AudioProcessorListener, vstEffect.version = JucePlugin_VersionCode; #endif - vstEffect.processReplacing = (Vst2::AEffectProcessProc) processReplacingCB; - vstEffect.processDoubleReplacing = (Vst2::AEffectProcessDoubleProc) processDoubleReplacingCB; + vstEffect.processReplacing = [] (Vst2::AEffect* vstInterface, + float** inputs, + float** outputs, + Vst2::VstInt32 sampleFrames) + { + getWrapper (vstInterface)->processReplacing (inputs, outputs, sampleFrames); + }; + + vstEffect.processDoubleReplacing = [] (Vst2::AEffect* vstInterface, + double** inputs, + double** outputs, + Vst2::VstInt32 sampleFrames) + { + getWrapper (vstInterface)->processDoubleReplacing (inputs, outputs, sampleFrames); + }; vstEffect.flags |= Vst2::effFlagsHasEditor; @@ -505,22 +546,12 @@ class JuceVSTWrapper final : public AudioProcessorListener, internalProcessReplacing (inputs, outputs, sampleFrames, floatTempBuffers); } - static void processReplacingCB (Vst2::AEffect* vstInterface, float** inputs, float** outputs, int32 sampleFrames) - { - getWrapper (vstInterface)->processReplacing (inputs, outputs, sampleFrames); - } - void processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames) { jassert (processor->isUsingDoublePrecision()); internalProcessReplacing (inputs, outputs, sampleFrames, doubleTempBuffers); } - static void processDoubleReplacingCB (Vst2::AEffect* vstInterface, double** inputs, double** outputs, int32 sampleFrames) - { - getWrapper (vstInterface)->processDoubleReplacing (inputs, outputs, sampleFrames); - } - //============================================================================== void resume() { @@ -678,22 +709,12 @@ class JuceVSTWrapper final : public AudioProcessorListener, return 0.0f; } - static float getParameterCB (Vst2::AEffect* vstInterface, int32 index) - { - return getWrapper (vstInterface)->getParameter (index); - } - void setParameter (int32 index, float value) { if (auto* param = juceParameters.getParamForIndex (index)) setValueAndNotifyIfChanged (*param, value); } - static void setParameterCB (Vst2::AEffect* vstInterface, int32 index, float value) - { - getWrapper (vstInterface)->setParameter (index, value); - } - void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) override { if (inParameterChangedCallback.get()) @@ -902,22 +923,6 @@ class JuceVSTWrapper final : public AudioProcessorListener, } } - static pointer_sized_int dispatcherCB (Vst2::AEffect* vstInterface, int32 opCode, int32 index, - pointer_sized_int value, void* ptr, float opt) - { - auto* wrapper = getWrapper (vstInterface); - VstOpCodeArguments args = { index, value, ptr, opt }; - - if (opCode == Vst2::effClose) - { - wrapper->dispatcher (opCode, args); - delete wrapper; - return 1; - } - - return wrapper->dispatcher (opCode, args); - } - //============================================================================== // A component to hold the AudioProcessorEditor, and cope with some housekeeping // chores when it changes or repaints. diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp index aa6b9800bec7..2e62ed015ca1 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp @@ -38,6 +38,38 @@ //============================================================================== #if JucePlugin_Build_VST3 +#if JUCE_VST3_CAN_REPLACE_VST2 && ! JUCE_FORCE_USE_LEGACY_PARAM_IDS && ! JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING + + // If you encounter this error there may be an issue migrating parameter + // automation between sessions saved using the VST2 and VST3 versions of this + // plugin. + // + // If you have released neither a VST2 or VST3 version of the plugin, + // consider only releasing a VST3 version and disabling JUCE_VST3_CAN_REPLACE_VST2. + // + // If you have released a VST2 version of the plugin but have not yet released + // a VST3 version of the plugin, consider enabling JUCE_FORCE_USE_LEGACY_PARAM_IDS. + // This will ensure that the parameter IDs remain compatible between both the + // VST2 and VST3 versions of the plugin in all hosts. + // + // If you have released a VST3 version of the plugin but have not released a + // VST2 version of the plugin, enable JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING. + // DO NOT change the JUCE_VST3_CAN_REPLACE_VST2 or JUCE_FORCE_USE_LEGACY_PARAM_IDS + // values as this will break compatibility with currently released VST3 + // versions of the plugin. + // + // If you have already released a VST2 and VST3 version of the plugin you may + // find in some hosts when a session containing automation data is saved using + // the VST2 or VST3 version, and is later loaded using the other version, the + // automation data will fail to control any of the parameters in the plugin as + // the IDs for these parameters are different. To fix parameter automation for + // the VST3 plugin when a session was saved with the VST2 plugin, implement + // VST3ClientExtensions::getCompatibleParameterIds() and enable + // JUCE_IGNORE_VST3_MISMATCHED_PARAMETER_ID_WARNING. + + #error You may have a conflict with parameter automation between VST2 and VST3 versions of your plugin. See the comment above for more details. +#endif + JUCE_BEGIN_NO_SANITIZE ("vptr") #if JUCE_PLUGINHOST_VST3 @@ -97,22 +129,32 @@ JUCE_BEGIN_NO_SANITIZE ("vptr") namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4310) -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wall") +using VST3InterfaceType = VST3ClientExtensions::InterfaceType; +using VST3InterfaceId = VST3ClientExtensions::InterfaceId; -#if JUCE_VST3_CAN_REPLACE_VST2 - static Steinberg::FUID getFUIDForVST2ID (bool forControllerUID) - { - Steinberg::TUID uuid; - detail::PluginUtilities::getUUIDForVST2ID (forControllerUID, (uint8*) uuid); - return Steinberg::FUID (uuid); - } -#endif +using namespace Steinberg; -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +static FUID toSteinbergUID (const VST3InterfaceId& uid) +{ + return FUID::fromTUID ((const char*) (uid.data())); +} -using namespace Steinberg; +static VST3InterfaceId toVST3InterfaceId (const TUID uid) +{ + VST3InterfaceId iid; + std::memcpy (iid.data(), uid, iid.size()); + return iid; +} + +static VST3InterfaceId getInterfaceId (VST3InterfaceType interfaceType) +{ + #if JUCE_VST3_CAN_REPLACE_VST2 + if (interfaceType == VST3InterfaceType::controller || interfaceType == VST3InterfaceType::component) + return VST3ClientExtensions::convertVST2PluginId (JucePlugin_VSTUniqueID, JucePlugin_Name, interfaceType); + #endif + + return VST3ClientExtensions::convertJucePluginId (JucePlugin_ManufacturerCode, JucePlugin_PluginCode, interfaceType); +} //============================================================================== #if JUCE_WINDOWS && JUCE_WIN_PER_MONITOR_DPI_AWARE @@ -502,13 +544,15 @@ class JuceAudioProcessor final : public Vst::IUnitInfo #if JUCE_FORCE_USE_LEGACY_PARAM_IDS return static_cast (paramIndex); #else + jassert (paramIndex < vstParamIDs.size()); return vstParamIDs.getReference (paramIndex); #endif } AudioProcessorParameter* getParamForVSTParamID (Vst::ParamID paramID) const noexcept { - return paramMap[static_cast (paramID)]; + const auto iter = paramMap.find (paramID); + return iter != paramMap.end() ? iter->second : nullptr; } AudioProcessorParameter* getBypassParameter() const noexcept @@ -561,8 +605,61 @@ class JuceAudioProcessor final : public Vst::IUnitInfo bool isUsingManagedParameters() const noexcept { return juceParameters.isUsingManagedParameters(); } + std::map getParameterMap (const VST3InterfaceId& pluginId) const + { + const auto iter = compatibleParameterIdMap.find (pluginId); + return iter != compatibleParameterIdMap.end() ? iter->second + : std::map{}; + } + + AudioProcessorParameter* getParameter (const String& juceParamId) const + { + const auto iter = juceIdParameterMap.find (juceParamId); + return iter != juceIdParameterMap.end() ? iter->second : nullptr; + } + + void updateParameterMapping() + { + static const auto currentPluginId = getInterfaceId (VST3InterfaceType::component); + + compatibleParameterIdMap = {}; + compatibleParameterIdMap[currentPluginId] = paramMap; + + if (const auto* ext = audioProcessor->getVST3ClientExtensions()) + { + for (auto& compatibleClass : ext->getCompatibleClasses()) + { + auto& parameterIdMap = compatibleParameterIdMap[compatibleClass]; + + for (auto [oldParamId, newParamId] : ext->getCompatibleParameterIds (compatibleClass)) + { + auto* parameter = getParameter (newParamId); + parameterIdMap[oldParamId] = parameter; + + // This means a parameter ID returned by getCompatibleParameterIds() + // does not match any parameters declared in the plugin. All IDs must + // match an existing parameter, or return an empty string to indicate + // there is no parameter to map to. + jassert (parameter != nullptr || newParamId.isEmpty()); + + // This means getCompatibleParameterIds() returned a parameter mapping + // that will hide a parameter in the current plugin! If this is due to + // an ID collision between plugin versions, you may be able to determine + // the mapping to report based on setStateInformation(). If you've + // already done this you can safely ignore this warning. If there is no + // way to determine the difference between the two plugin versions in + // setStateInformation() the best course of action is to remove the + // problematic parameter from the mapping. + jassert (compatibleClass != currentPluginId + || getParamForVSTParamID (oldParamId) == nullptr + || parameter == getParamForVSTParamID (oldParamId)); + } + } + } + } + //============================================================================== - inline static const FUID iid { TUID INLINE_UID (0x0101ABAB, 0xABCDEF01, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::processor)); private: //============================================================================== @@ -570,7 +667,7 @@ class JuceAudioProcessor final : public Vst::IUnitInfo { parameterGroups = audioProcessor->getParameterTree().getSubgroups (true); - #if JUCE_DEBUG + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED auto allGroups = parameterGroups; allGroups.add (&audioProcessor->getParameterTree()); std::unordered_set unitIDs; @@ -633,7 +730,8 @@ class JuceAudioProcessor final : public Vst::IUnitInfo } vstParamIDs.add (vstParamID); - paramMap.set (static_cast (vstParamID), juceParam); + paramMap[vstParamID] = juceParam; + juceIdParameterMap[LegacyAudioParameter::getParamID (juceParam, false)] = juceParam; } auto numPrograms = audioProcessor->getNumPrograms(); @@ -650,7 +748,7 @@ class JuceAudioProcessor final : public Vst::IUnitInfo programParamID = static_cast (i++); vstParamIDs.add (programParamID); - paramMap.set (static_cast (programParamID), ownedProgramParameter.get()); + paramMap[programParamID] = ownedProgramParameter.get(); } cachedParamValues = CachedParamValues { { vstParamIDs.begin(), vstParamIDs.end() } }; @@ -658,20 +756,13 @@ class JuceAudioProcessor final : public Vst::IUnitInfo Vst::ParamID generateVSTParamIDForParam (const AudioProcessorParameter* param) { - auto juceParamID = LegacyAudioParameter::getParamID (param, false); + const auto juceParamID = LegacyAudioParameter::getParamID (param, false); - #if JUCE_FORCE_USE_LEGACY_PARAM_IDS + #if JUCE_FORCE_USE_LEGACY_PARAM_IDS return static_cast (juceParamID.getIntValue()); - #else - auto paramHash = static_cast (juceParamID.hashCode()); - - #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS - // studio one doesn't like negative parameters - paramHash &= ~(((Vst::ParamID) 1) << (sizeof (Vst::ParamID) * 8 - 1)); + #else + return VST3ClientExtensions::convertJuceParameterId (juceParamID, JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS); #endif - - return paramHash; - #endif } //============================================================================== @@ -679,6 +770,8 @@ class JuceAudioProcessor final : public Vst::IUnitInfo CachedParamValues cachedParamValues; Vst::ParamID bypassParamID = 0, programParamID = static_cast (paramPreset); bool bypassIsRegularParameter = false; + std::map> compatibleParameterIdMap; + std::map juceIdParameterMap; //============================================================================== std::atomic refCount { 0 }; @@ -686,7 +779,7 @@ class JuceAudioProcessor final : public Vst::IUnitInfo //============================================================================== LegacyAudioParametersWrapper juceParameters; - HashMap paramMap; + std::map paramMap; std::unique_ptr ownedBypassParameter, ownedProgramParameter; Array parameterGroups; @@ -764,6 +857,7 @@ static void setValueAndNotifyIfChanged (AudioProcessorParameter& param, float ne class JuceVST3EditController final : public Vst::EditController, public Vst::IMidiMapping, public Vst::IUnitInfo, + public Vst::IRemapParamID, public Vst::ChannelContext::IInfoListener, #if JucePlugin_Enable_ARA public Presonus::IPlugInViewEmbedding, @@ -783,12 +877,7 @@ class JuceVST3EditController final : public Vst::EditController, } //============================================================================== - - #if JUCE_VST3_CAN_REPLACE_VST2 - inline static const FUID iid = getFUIDForVST2ID (true); - #else - inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x1234ABCD, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; - #endif + inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::controller)); //============================================================================== JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Winconsistent-missing-override") @@ -1028,6 +1117,30 @@ class JuceVST3EditController final : public Vst::EditController, JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProgramChangeParameter) }; + //============================================================================== + tresult PLUGIN_API getCompatibleParamID (const TUID pluginToReplaceUID, + Vst::ParamID oldParamID, + Vst::ParamID& newParamID) override + { + const auto parameterMap = audioProcessor->getParameterMap (toVST3InterfaceId (pluginToReplaceUID)); + const auto iter = parameterMap.find (oldParamID); + + if (iter == parameterMap.end()) + { + // This suggests a host is trying to load a plugin and parameter ID + // combination that hasn't been accounted for in getCompatibleParameterIds(). + // Override this method in VST3ClientExtensions and return a suitable + // parameter mapping to silence this warning. + jassertfalse; + return kResultFalse; + } + + const auto* parameter = iter->second; + newParamID = parameter != nullptr ? audioProcessor->getVSTParamIDForIndex (parameter->getParameterIndex()) + : 0xffffffff; + return kResultTrue; + } + //============================================================================== tresult PLUGIN_API setChannelContextInfos (Vst::IAttributeList* list) override { @@ -1101,8 +1214,10 @@ class JuceVST3EditController final : public Vst::EditController, } } + audioProcessor->updateParameterMapping(); + if (auto* handler = getComponentHandler()) - handler->restartComponent (Vst::kParamValuesChanged); + handler->restartComponent (Vst::kParamValuesChanged | Vst::kParamIDMappingChanged); return kResultOk; } @@ -1549,6 +1664,7 @@ class JuceVST3EditController final : public Vst::EditController, UniqueBase{}, UniqueBase{}, UniqueBase{}, + UniqueBase{}, UniqueBase{}, SharedBase{}, UniqueBase{}, @@ -2508,7 +2624,7 @@ class JuceVST3EditController final : public Vst::EditController, return createARAFactory(); } - inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xA1B2C3D4, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::ara)); private: //============================================================================== @@ -2581,11 +2697,7 @@ class JuceVST3Component final : public Vst::IComponent, AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; } //============================================================================== - #if JUCE_VST3_CAN_REPLACE_VST2 - inline static const FUID iid = getFUIDForVST2ID (false); - #else - inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0x9182FAEB, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; - #endif + inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::component)); JUCE_DECLARE_VST3_COM_REF_METHODS @@ -3998,15 +4110,7 @@ class JucePluginCompatibility final : public IPluginCompatibility Array oldArray; for (const auto& uid : extensions->getCompatibleClasses()) - { - // All UIDs returned from getCompatibleClasses should be 32 characters long - jassert (uid.length() == 32); - - // All UIDs returned from getCompatibleClasses should be in hex notation - jassert (uid.containsOnly ("ABCDEF0123456789")); - - oldArray.add (uid); - } + oldArray.add (String::toHexString (uid.data(), (int) uid.size(), 0)); return oldArray; }()); @@ -4034,7 +4138,7 @@ class JucePluginCompatibility final : public IPluginCompatibility return kNotImplemented; } - inline static const FUID iid { TUID INLINE_UID (0xABCDEF01, 0xC0DEF00D, JucePlugin_ManufacturerCode, JucePlugin_PluginCode) }; + inline static const FUID iid = toSteinbergUID (getInterfaceId (VST3InterfaceType::compatibility)); private: std::atomic refCount { 1 }; diff --git a/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp b/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp index d55c302fbfe9..ac718f197ea1 100644 --- a/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp +++ b/modules/juce_audio_processors/format_types/juce_LV2SupportLibs.cpp @@ -37,7 +37,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc99-extensions", "-Wcast-align", "-Wconversion", - "-Wdeprecated-declarations", "-Wextra-semi", "-Wfloat-conversion", "-Wfloat-equal", @@ -56,7 +55,8 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wc99-extensions", "-Wswitch-enum", "-Wunused-parameter", "-Wzero-as-null-pointer-constant") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100 4200 4244 4267 4389 4702 4706 4800 4996 6308 28182 28183 6385 6386 6387 6011 6282 6323 6330 6001 6031) +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100 4200 4244 4267 4389 4702 4706 4800 6308 28182 28183 6385 6386 6387 6011 6282 6323 6330 6001 6031) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS extern "C" { @@ -113,5 +113,6 @@ extern "C" } // extern "C" +JUCE_END_IGNORE_DEPRECATION_WARNINGS JUCE_END_IGNORE_WARNINGS_MSVC JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp index 380abce17b35..977206dc0724 100644 --- a/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp +++ b/modules/juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp @@ -35,8 +35,7 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS class LegacyAudioParameter final : public HostedAudioProcessorParameter { @@ -219,7 +218,6 @@ class LegacyAudioParametersWrapper bool legacyParamIDs = false, usingManagedParameters = false; }; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_audio_processors/format_types/juce_VST3Common.h b/modules/juce_audio_processors/format_types/juce_VST3Common.h index 8b1171ebbb4b..6b93925de9de 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3Common.h +++ b/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -1430,7 +1430,7 @@ class MidiEventList : public Steinberg::Vst::IEventList controlEvent->controllerNumber); if (controlParamID != Steinberg::Vst::kNoParamId) - callback (controlParamID, controlEvent->paramValue, msg.getTimeStamp()); + callback (controlParamID, (float) controlEvent->paramValue, msg.getTimeStamp()); return true; } diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 7afd89b9e62f..fb122ff5d068 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -1932,7 +1932,7 @@ struct VST3PluginWindow final : public AudioProcessorEditor, JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) }; -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) // warning about overriding deprecated methods +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS //============================================================================== static bool hasARAExtension (IPluginFactory* pluginFactory, const String& pluginClassName) @@ -2593,6 +2593,11 @@ class VST3PluginInstance final : public AudioPluginInstance return cachedInfo; } + Steinberg::int32 getVstParamIndex() const + { + return vstParamIndex; + } + private: Vst::ParameterInfo fetchParameterInfo() const { @@ -3770,12 +3775,21 @@ class VST3PluginInstance final : public AudioPluginInstance if (acceptsMidi()) { - const auto midiMessageCallback = [&] (auto controlID, auto paramValue, auto time) + const auto midiMessageCallback = [&] (auto controlID, float paramValue, auto time) { Steinberg::int32 queueIndex{}; if (auto* queue = inputParameterChanges->addParameterData (controlID, queueIndex)) - queue->append ({ (Steinberg::int32) time, (float) paramValue }); + queue->append ({ (Steinberg::int32) time, paramValue }); + + if (auto* param = getParameterForID (controlID)) + { + // Send the parameter value to the editor + parameterDispatcher.push (param->getVstParamIndex(), paramValue); + + // Update the host's view of the parameter value + param->setValueWithoutUpdatingProcessor (paramValue); + } }; MidiEventList::hostToPluginEventList (*midiInputs, @@ -3891,7 +3905,7 @@ class VST3PluginInstance final : public AudioPluginInstance JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginInstance) }; -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS //============================================================================== tresult VST3HostContext::beginEdit (Vst::ParamID paramID) diff --git a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index e1c214c3cc56..0a8f4856425c 100644 --- a/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -63,11 +63,11 @@ struct AEffect; #include "juce_VSTCommon.h" -JUCE_END_IGNORE_WARNINGS_GCC_LIKE JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS #include "juce_VSTMidiEventList.h" @@ -81,7 +81,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4355) #endif #ifndef JUCE_VST_WRAPPER_INVOKE_MAIN -#define JUCE_VST_WRAPPER_INVOKE_MAIN effect = module->moduleMain ((Vst2::audioMasterCallback) &audioMaster); +#define JUCE_VST_WRAPPER_INVOKE_MAIN effect = module->moduleMain (audioMaster); #endif #ifndef JUCE_VST_FALLBACK_HOST_NAME @@ -222,8 +222,7 @@ namespace } //============================================================================== -typedef Vst2::AEffect* (VSTCALLBACK *MainCall) (Vst2::audioMasterCallback); -static pointer_sized_int VSTCALLBACK audioMaster (Vst2::AEffect*, int32, int32, pointer_sized_int, void*, float); +using MainCall = Vst2::AEffect* (VSTCALLBACK*) (Vst2::audioMasterCallback); //============================================================================== // Change this to disable logging of various VST activities @@ -829,8 +828,6 @@ struct ModuleHandle final : public ReferenceCountedObject static const int defaultVSTSampleRateValue = 44100; static const int defaultVSTBlockSizeValue = 512; -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - class TempChannelPointers { public: @@ -2156,6 +2153,20 @@ struct VSTPluginInstance final : public AudioPluginInstance, UseResFile (module->resFileId); #endif + constexpr Vst2::audioMasterCallback audioMaster = [] (Vst2::AEffect* eff, + Vst2::VstInt32 opcode, + Vst2::VstInt32 index, + Vst2::VstIntPtr value, + void* ptr, + float opt) -> Vst2::VstIntPtr + { + if (eff != nullptr) + if (auto* instance = (VSTPluginInstance*) (eff->resvd2)) + return instance->handleCallback (opcode, index, value, ptr, opt); + + return VSTPluginInstance::handleGeneralCallback (opcode, index, value, ptr, opt); + }; + { JUCE_VST_WRAPPER_INVOKE_MAIN } @@ -3433,8 +3444,6 @@ struct VSTPluginWindow final : public AudioProcessorEditor, }; #endif -JUCE_END_IGNORE_WARNINGS_MSVC - //============================================================================== AudioProcessorEditor* VSTPluginInstance::createEditor() { @@ -3458,15 +3467,6 @@ bool VSTPluginInstance::updateSizeFromEditor ([[maybe_unused]] int w, [[maybe_un //============================================================================== // entry point for all callbacks from the plugin -static pointer_sized_int VSTCALLBACK audioMaster (Vst2::AEffect* effect, int32 opcode, int32 index, pointer_sized_int value, void* ptr, float opt) -{ - if (effect != nullptr) - if (auto* instance = (VSTPluginInstance*) (effect->resvd2)) - return instance->handleCallback (opcode, index, value, ptr, opt); - - return VSTPluginInstance::handleGeneralCallback (opcode, index, value, ptr, opt); -} - //============================================================================== VSTPluginFormat::VSTPluginFormat() {} VSTPluginFormat::~VSTPluginFormat() {} @@ -3758,7 +3758,7 @@ void VSTPluginFormat::aboutToScanVSTShellPlugin (const PluginDescription&) {} } // namespace juce -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS JUCE_END_IGNORE_WARNINGS_MSVC #endif diff --git a/modules/juce_audio_processors/juce_audio_processors.cpp b/modules/juce_audio_processors/juce_audio_processors.cpp index 873ca3ade0bf..a3d5d7ed9e08 100644 --- a/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/modules/juce_audio_processors/juce_audio_processors.cpp @@ -231,6 +231,7 @@ struct NSViewComponentWithParent : public NSViewComponent, #include "utilities/juce_PluginHostType.cpp" #include "utilities/juce_AAXClientExtensions.cpp" #include "utilities/juce_VST2ClientExtensions.cpp" +#include "utilities/juce_VST3ClientExtensions.cpp" #include "utilities/ARA/juce_ARA_utils.cpp" #include "format_types/juce_LV2PluginFormat.cpp" diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index b9fe2031c107..740eb051cf21 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -1271,8 +1271,7 @@ VST3ClientExtensions* AudioProcessor::getVST3ClientExtensions() } //============================================================================== -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS void AudioProcessor::setParameterNotifyingHost (int parameterIndex, float newValue) { @@ -1510,8 +1509,7 @@ AudioProcessorParameter* AudioProcessor::getParamChecked (int index) const bool AudioProcessor::canAddBus ([[maybe_unused]] bool isInput) const { return false; } bool AudioProcessor::canRemoveBus ([[maybe_unused]] bool isInput) const { return false; } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS //============================================================================== void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h index 74376b509664..103d303c1251 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -1154,7 +1154,11 @@ class JUCE_API AudioProcessor : private AAXClientExtensions See also the helper function getXmlFromBinary() for loading settings as XML. - @see setCurrentProgramStateInformation + VST3ClientExtensions::getCompatibleParameterIds() will always be called after + setStateInformation() therefore you can use information from the plugin state + to determine which parameter mapping to use if necessary. + + @see setCurrentProgramStateInformation, VST3ClientExtensions::getCompatibleParameterIds */ virtual void setStateInformation (const void* data, int sizeInBytes) = 0; diff --git a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 3c748685d9ec..e1484c151877 100644 --- a/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -404,7 +404,7 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, Kno : formatManager (manager), list (listToEdit), deadMansPedalFile (deadMansPedal), - optionsButton ("Options..."), + optionsButton (TRANS ("Options...")), propertiesToUse (props), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation), numThreads (allowAsync ? 1 : 0) @@ -554,7 +554,7 @@ PopupMenu PluginListComponent::createOptionsMenu() for (auto format : formatManager.getFormats()) if (format->canScanForPlugins()) - menu.addItem (PopupMenu::Item ("Remove all " + format->getName() + " plug-ins") + menu.addItem (PopupMenu::Item (TRANS ("Remove all XFMTX plug-ins").replace ("XFMTX", format->getName())) .setEnabled (! list.getTypesForFormat (*format).isEmpty()) .setAction ([this, format] { @@ -583,7 +583,7 @@ PopupMenu PluginListComponent::createOptionsMenu() for (auto format : formatManager.getFormats()) if (format->canScanForPlugins()) - menu.addItem (PopupMenu::Item ("Scan for new or updated " + format->getName() + " plug-ins") + menu.addItem (PopupMenu::Item (TRANS ("Scan for new or updated XFMTX plug-ins").replace ("XFMTX", format->getName())) .setAction ([this, format] { scanFor (*format); })); return menu; diff --git a/modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp b/modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp index 588eb554e1a2..c34061e15378 100644 --- a/modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp +++ b/modules/juce_audio_processors/utilities/ARA/juce_AudioProcessor_ARAExtensions.cpp @@ -134,7 +134,7 @@ void AudioProcessorARAExtension::didBindToARA() noexcept playbackRenderer->araExtension = this; #endif -#if (! JUCE_DISABLE_ASSERTIONS) +#if JUCE_ASSERTIONS_ENABLED_OR_LOGGED // validate proper subclassing of the instance role classes if (auto playbackRenderer = getPlaybackRenderer()) jassert (dynamic_cast (playbackRenderer) != nullptr); diff --git a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp new file mode 100644 index 000000000000..8cb875b114bc --- /dev/null +++ b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.cpp @@ -0,0 +1,193 @@ +/* + ============================================================================== + + This file is part of the JUCE framework. + Copyright (c) Raw Material Software Limited + + JUCE is an open source framework subject to commercial or open source + licensing. + + By downloading, installing, or using the JUCE framework, or combining the + JUCE framework with any other source code, object code, content or any other + copyrightable work, you agree to the terms of the JUCE End User Licence + Agreement, and all incorporated terms including the JUCE Privacy Policy and + the JUCE Website Terms of Service, as applicable, which will bind you. If you + do not agree to the terms of these agreements, we will not license the JUCE + framework to you, and you must discontinue the installation or download + process and cease use of the JUCE framework. + + JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/ + JUCE Privacy Policy: https://juce.com/juce-privacy-policy + JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/ + + Or: + + You may also use this code under the terms of the AGPLv3: + https://www.gnu.org/licenses/agpl-3.0.en.html + + THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL + WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +std::map VST3ClientExtensions::getCompatibleParameterIds (const InterfaceId&) const +{ + return {}; +} + +VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertJucePluginId (uint32_t manufacturerCode, + uint32_t pluginCode, + InterfaceType interfaceType) +{ + const auto word0 = std::invoke ([&]() -> uint32_t + { + switch (interfaceType) + { + case InterfaceType::ara: [[fallthrough]]; + case InterfaceType::controller: [[fallthrough]]; + case InterfaceType::compatibility: [[fallthrough]]; + case InterfaceType::component: return 0xABCDEF01; + case InterfaceType::processor: return 0x0101ABAB; + } + + jassertfalse; + return 0; + }); + + const auto word1 = std::invoke ([&]() -> uint32_t + { + switch (interfaceType) + { + case InterfaceType::ara: return 0xA1B2C3D4; + case InterfaceType::controller: return 0x1234ABCD; + case InterfaceType::compatibility: return 0xC0DEF00D; + case InterfaceType::component: return 0x9182FAEB; + case InterfaceType::processor: return 0xABCDEF01; + } + + jassertfalse; + return 0; + }); + + constexpr auto getByteFromLSB = [] (uint32_t word, int byteIndex) + { + jassert (isPositiveAndNotGreaterThan (byteIndex, 3)); + return (std::byte) ((word >> (byteIndex * 8)) & 0xff); + }; + + #if JUCE_WINDOWS + constexpr auto isWindows = true; + #else + constexpr auto isWindows = false; + #endif + + return { + getByteFromLSB (word0, isWindows ? 0 : 3), + getByteFromLSB (word0, isWindows ? 1 : 2), + getByteFromLSB (word0, isWindows ? 2 : 1), + getByteFromLSB (word0, isWindows ? 3 : 0), + + getByteFromLSB (word1, isWindows ? 2 : 3), + getByteFromLSB (word1, isWindows ? 3 : 2), + getByteFromLSB (word1, isWindows ? 0 : 1), + getByteFromLSB (word1, isWindows ? 1 : 0), + + getByteFromLSB (manufacturerCode, 3), + getByteFromLSB (manufacturerCode, 2), + getByteFromLSB (manufacturerCode, 1), + getByteFromLSB (manufacturerCode, 0), + + getByteFromLSB (pluginCode, 3), + getByteFromLSB (pluginCode, 2), + getByteFromLSB (pluginCode, 1), + getByteFromLSB (pluginCode, 0) + }; +} + +VST3ClientExtensions::InterfaceId VST3ClientExtensions::convertVST2PluginId (uint32_t pluginCode, + const String& pluginName, + InterfaceType interfaceType) +{ + VST3ClientExtensions::InterfaceId iid{}; + + iid[0] = (std::byte) 'V'; + iid[1] = (std::byte) 'S'; + iid[2] = (std::byte) std::invoke ([&] + { + switch (interfaceType) + { + case InterfaceType::controller: return 'E'; + case InterfaceType::component: return 'T'; + case InterfaceType::ara: [[fallthrough]]; + case InterfaceType::compatibility: [[fallthrough]]; + case InterfaceType::processor: break; + } + + // A VST2 plugin only has two interfaces + // - component (the audio effect) + // - controller (the editor/UI) + jassertfalse; + return '\0'; + }); + iid[3] = (std::byte) (pluginCode >> 24); + iid[4] = (std::byte) (pluginCode >> 16); + iid[5] = (std::byte) (pluginCode >> 8); + iid[6] = (std::byte) pluginCode; + + for (const auto [index, character] : enumerate (pluginName, (size_t) 7)) + { + if (index >= iid.size()) + break; + + iid[index] = (std::byte) CharacterFunctions::toLowerCase (character); + } + + #if JUCE_WINDOWS + std::swap (iid[0], iid[3]); + std::swap (iid[1], iid[2]); + std::swap (iid[4], iid[5]); + std::swap (iid[6], iid[7]); + #endif + + return iid; +} + +uint32_t VST3ClientExtensions::convertJuceParameterId (const String& parameterId, bool studioOneCompatible) +{ + auto hash = (uint32_t) (parameterId.hashCode()); + + if (studioOneCompatible) + hash &= 0x7fffffff; + + return hash; +} + +VST3ClientExtensions::InterfaceId VST3ClientExtensions::toInterfaceId (const String& interfaceIdString) +{ + jassert (interfaceIdString.length() == 32); + jassert (interfaceIdString.containsOnly ("0123456789abcdefABCDEF")); + + return { (std::byte) interfaceIdString.substring ( 0, 2).getHexValue32(), + (std::byte) interfaceIdString.substring ( 2, 4).getHexValue32(), + (std::byte) interfaceIdString.substring ( 4, 6).getHexValue32(), + (std::byte) interfaceIdString.substring ( 6, 8).getHexValue32(), + (std::byte) interfaceIdString.substring ( 8, 10).getHexValue32(), + (std::byte) interfaceIdString.substring (10, 12).getHexValue32(), + (std::byte) interfaceIdString.substring (12, 14).getHexValue32(), + (std::byte) interfaceIdString.substring (14, 16).getHexValue32(), + (std::byte) interfaceIdString.substring (16, 18).getHexValue32(), + (std::byte) interfaceIdString.substring (18, 20).getHexValue32(), + (std::byte) interfaceIdString.substring (20, 22).getHexValue32(), + (std::byte) interfaceIdString.substring (22, 24).getHexValue32(), + (std::byte) interfaceIdString.substring (24, 26).getHexValue32(), + (std::byte) interfaceIdString.substring (26, 28).getHexValue32(), + (std::byte) interfaceIdString.substring (28, 30).getHexValue32(), + (std::byte) interfaceIdString.substring (30, 32).getHexValue32() }; +} + +} // namespace juce diff --git a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h index 09c8cdb73aa3..cee4e6a30554 100644 --- a/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h +++ b/modules/juce_audio_processors/utilities/juce_VST3ClientExtensions.h @@ -104,16 +104,156 @@ struct VST3ClientExtensions */ virtual bool getPluginHasMainInput() const { return true; } - /** This function should return the UIDs of any compatible VST2 plug-ins. + using InterfaceId = std::array; - Each item in the vector should be a 32-character string consisting only - of the characters 0-9 and A-F. + /** This function should return the UIDs of any compatible VST2 or VST3 + plug-ins. This information will be used to implement the IPluginCompatibility interface. Hosts can use this interface to determine whether this VST3 is capable of replacing a given VST2. + + Each compatible class is a 16-byte array that corresponds to the VST3 + interface ID for the class implementing the IComponent interface. + For VST2 or JUCE plugins these IDs can be determined in the following + ways: + - Use convertVST2PluginId() for VST2 plugins or JUCE VST3 plugins with + JUCE_VST3_CAN_REPLACE_VST3 enabled + - Use convertJucePluginId() for any other JUCE VST3 plugins + + If JUCE_VST3_CAN_REPLACE_VST2 is enabled the VST3 plugin will have the + same identifier as the VST2 plugin and therefore there will be no need + to implement this function. + + If the parameter IDs between compatible versions differ + getCompatibleParameterIds() should also be overridden. However, unlike + getCompatibleParameterIds() this function should remain constant and + always return the same IDs. + + @see getCompatibleParameterIds() */ - virtual std::vector getCompatibleClasses() const { return {}; } + virtual std::vector getCompatibleClasses() const { return {}; } + + /** This function should return a map of VST3 parameter IDs and the JUCE + parameters they map to. + + This information is used to implement the IRemapParameter interface. + Hosts can use this to preserve automation data when a session was saved + using a compatible plugin that has different parameter IDs. + + Not all hosts will take this information into account. Therefore, + parameter IDs should be maintained between plugin versions. For JUCE + plugins migrating from VST2 to VST3 the best method for achieving this + is enabling JUCE_FORCE_LEGACY_PARAM_IDS. However, if a plugin has + already been released without enabling this flag, this method offers an + alternative approach that won't cause any further compatibility issues. + + The key in the map is a VST3 parameter identifier or Vst::ParamID. For + VST2 or JUCE plugins these IDs can be determined in the following ways + - Use the parameter index for + - VST2 plugins + - JUCE VST3 plugins with JUCE_FORCE_LEGACY_PARAM_IDS enabled + - Any parameter that doesn't inherit from HostedAudioProcessorParameter + - Use convertJuceParameterId() for JUCE VST3 plugins where + JUCE_FORCE_LEGACY_PARAM_IDS is disabled + + The value in the map is the JUCE parameter ID for the parameter to map + to, or an empty string to indicate that there is no parameter to map to. + If a parameter doesn't inherit from HostedAudioProcessorParameter its ID + will be the parameter index as a string, for example "1". Otherwise + always use the actual parameter ID (even if JUCE_FORCE_LEGACY_PARAM_IDS + is enabled). + + In the unlikely event that two plugins share the same plugin ID, and + both have a different parameters that share the same parameter ID, it + may be possible to determine which version of the plugin is being loaded + during setStateInformation(). This method will always be called after + setStateInformation(), so that the map with the correct mapping can be + provided when queried. + + Below is an example of how you might implement this function for a JUCE + VST3 plugin where JUCE_VST3_CAN_REPLACE_VST2 is enabled, but + JUCE_FORCE_LEGACY_PARAM_IDS is disabled. + + @code + std::map getCompatibleParameterIds (const String&) const override + { + return { { 0, "Frequency" }, + { 1, "CutOff" }, + { 2, "Gain" }, + { 3, "Bypass" } }; + } + @endcode + + @param compatibleClass A plugin identifier, either for the current + plugin or one listed in getCompatibleClasses(). + This parameter allows the implementation to + return a different parameter map for each + compatible class. Use convertJucePluginId() and + convertVST2PluginId() to determine the class IDs + used by JUCE plugins. + + @returns A map where each key is a VST3 parameter ID in the compatible + plugin, and the value is the unique JUCE parameter ID in the + current plugin that it should be mapped to. + + @see getCompatibleClasses, convertJucePluginId, convertVST2PluginId, convertJuceParameterId + */ + virtual std::map getCompatibleParameterIds (const InterfaceId& compatibleClass) const; + + /** An enum indicating the various VST3 interface types. + + In most cases users shouldn't need to concern themselves with any + interfaces other than the component, which is used to report the actual + audio effect. + */ + enum class InterfaceType + { + ara, + controller, + compatibility, + component, + processor + }; + + /** Returns a 16-byte array indicating the VST3 interface ID used for a + given JUCE VST3 plugin. + + Internally this is what JUCE will use to assign an ID to each VST3 + interface, unless JUCE_VST3_CAN_REPLACE_VST2 is enabled. + + @see convertVST2PluginId, getCompatibleClasses, getCompatibleParameterIds + */ + static InterfaceId convertJucePluginId (uint32_t manufacturerCode, + uint32_t pluginCode, + InterfaceType interfaceType = InterfaceType::component); + + /** Returns a 16-byte array indicating the VST3 interface ID used for a + given VST2 plugin. + + Internally JUCE will use this method to assign an ID for the component + and controller interfaces when JUCE_VST3_CAN_REPLACE_VST2 is enabled. + + @convertJucePluginId, getCompatibleClasses, getCompatibleParameterIds + */ + static InterfaceId convertVST2PluginId (uint32_t pluginCode, + const String& pluginName, + InterfaceType interfaceType = InterfaceType::component); + + /** Returns the VST3 compatible parameter ID reported for a given JUCE + parameter. + + Internally JUCE will use this method to determine the Vst::ParamID for + a HostedAudioProcessorParameter, unless JUCE_FORCE_LEGACY_PARAM_IDS is + enabled, in which case it will use the parameter index. + + @see getCompatibleParameterIds + */ + static uint32_t convertJuceParameterId (const String& parameterId, + bool studioOneCompatible = true); + + /** Converts a 32-character hex notation string to a VST3 interface ID. */ + static InterfaceId toInterfaceId (const String& interfaceIdString); }; } // namespace juce diff --git a/modules/juce_core/containers/juce_Variant.cpp b/modules/juce_core/containers/juce_Variant.cpp index 0c6b6728d0fd..0b45553bef70 100644 --- a/modules/juce_core/containers/juce_Variant.cpp +++ b/modules/juce_core/containers/juce_Variant.cpp @@ -896,13 +896,11 @@ var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const var var::null; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif diff --git a/modules/juce_core/files/juce_DirectoryIterator.cpp b/modules/juce_core/files/juce_DirectoryIterator.cpp index 4ea33f5377f2..782636472cee 100644 --- a/modules/juce_core/files/juce_DirectoryIterator.cpp +++ b/modules/juce_core/files/juce_DirectoryIterator.cpp @@ -58,8 +58,7 @@ bool DirectoryIterator::next() return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) @@ -144,8 +143,7 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi } } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS const File& DirectoryIterator::getFile() const { diff --git a/modules/juce_core/files/juce_File.cpp b/modules/juce_core/files/juce_File.cpp index 073ee8e92505..4fd9e4288caa 100644 --- a/modules/juce_core/files/juce_File.cpp +++ b/modules/juce_core/files/juce_File.cpp @@ -1029,13 +1029,11 @@ File File::getLinkedTarget() const //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const File File::nonexistent{}; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif @@ -1158,10 +1156,10 @@ class FileTests final : public UnitTest expect (home.getChildFile ("...xyz").getFileName() == "...xyz"); expect (home.getChildFile ("./xyz") == home.getChildFile ("xyz")); expect (home.getChildFile ("././xyz") == home.getChildFile ("xyz")); - expect (home.getChildFile ("../xyz") == home.getParentDirectory().getChildFile ("xyz")); - expect (home.getChildFile (".././xyz") == home.getParentDirectory().getChildFile ("xyz")); - expect (home.getChildFile (".././xyz/./abc") == home.getParentDirectory().getChildFile ("xyz/abc")); - expect (home.getChildFile ("./../xyz") == home.getParentDirectory().getChildFile ("xyz")); + expect (home.getChildFile ("../xyz") == home.getSiblingFile ("xyz")); + expect (home.getChildFile (".././xyz") == home.getSiblingFile ("xyz")); + expect (home.getChildFile (".././xyz/./abc") == home.getSiblingFile ("xyz/abc")); + expect (home.getChildFile ("./../xyz") == home.getSiblingFile ("xyz")); expect (home.getChildFile ("a1/a2/a3/./../../a4") == home.getChildFile ("a1/a4")); expect (! File().hasReadAccess()); diff --git a/modules/juce_core/files/juce_RangedDirectoryIterator.cpp b/modules/juce_core/files/juce_RangedDirectoryIterator.cpp index cc4b7b4a9848..181bd01a774e 100644 --- a/modules/juce_core/files/juce_RangedDirectoryIterator.cpp +++ b/modules/juce_core/files/juce_RangedDirectoryIterator.cpp @@ -35,8 +35,7 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS float DirectoryEntry::getEstimatedProgress() const { @@ -85,7 +84,6 @@ void RangedDirectoryIterator::increment() iterator = nullptr; } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_core/files/juce_RangedDirectoryIterator.h b/modules/juce_core/files/juce_RangedDirectoryIterator.h index 1f6ca5448384..ef9b6d2234d4 100644 --- a/modules/juce_core/files/juce_RangedDirectoryIterator.h +++ b/modules/juce_core/files/juce_RangedDirectoryIterator.h @@ -36,8 +36,7 @@ namespace juce { //============================================================================== -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS /** Describes the attributes of a file or folder. @@ -196,7 +195,6 @@ inline RangedDirectoryIterator begin (const RangedDirectoryIterator& it) { retur inline RangedDirectoryIterator end (const RangedDirectoryIterator&) { return {}; } -JUCE_END_IGNORE_WARNINGS_MSVC -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_core/files/juce_TemporaryFile.cpp b/modules/juce_core/files/juce_TemporaryFile.cpp index 52fbc5883909..91a049661ba6 100644 --- a/modules/juce_core/files/juce_TemporaryFile.cpp +++ b/modules/juce_core/files/juce_TemporaryFile.cpp @@ -35,23 +35,6 @@ namespace juce { -// Using Random::getSystemRandom() can be a bit dangerous in multithreaded contexts! -class LockedRandom -{ -public: - int nextInt() - { - const ScopedLock lock (mutex); - return random.nextInt(); - } - -private: - CriticalSection mutex; - Random random; -}; - -static LockedRandom lockedRandom; - static File createTempFile (const File& parentDirectory, String name, const String& suffix, int optionFlags) { @@ -61,9 +44,15 @@ static File createTempFile (const File& parentDirectory, String name, return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0); } +TemporaryFile::TemporaryFile() : TemporaryFile { String{} } {} + +TemporaryFile::TemporaryFile (const String& suffix) : TemporaryFile { suffix, 0 } {} + +TemporaryFile::TemporaryFile (const File& target) : TemporaryFile { target, 0 } {} + TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) : temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), - "temp_" + String::toHexString (lockedRandom.nextInt()), + "temp_" + String::toHexString (Random::getSystemRandom().nextInt()), suffix, optionFlags)), targetFile() { @@ -72,7 +61,7 @@ TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) TemporaryFile::TemporaryFile (const File& target, const int optionFlags) : temporaryFile (createTempFile (target.getParentDirectory(), target.getFileNameWithoutExtension() - + "_temp" + String::toHexString (lockedRandom.nextInt()), + + "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), target.getFileExtension(), optionFlags)), targetFile (target) { diff --git a/modules/juce_core/files/juce_TemporaryFile.h b/modules/juce_core/files/juce_TemporaryFile.h index 33377f7c129c..dffe03dbd74d 100644 --- a/modules/juce_core/files/juce_TemporaryFile.h +++ b/modules/juce_core/files/juce_TemporaryFile.h @@ -87,6 +87,16 @@ class JUCE_API TemporaryFile }; //============================================================================== + /** Creates a randomly-named temporary file in the default temp directory. + */ + TemporaryFile(); + + /** Creates a randomly-named temporary file in the default temp directory. + + @param suffix a file suffix to use for the file + */ + explicit TemporaryFile (const String& suffix); + /** Creates a randomly-named temporary file in the default temp directory. @param suffix a file suffix to use for the file @@ -94,8 +104,24 @@ class JUCE_API TemporaryFile The file will not be created until you write to it. And remember that when this object is deleted, the file will also be deleted! */ - TemporaryFile (const String& suffix = String(), - int optionFlags = 0); + TemporaryFile (const String& suffix, + int optionFlags); + + /** Creates a temporary file in the same directory as a specified file. + + This is useful if you have a file that you want to overwrite, but don't + want to harm the original file if the write operation fails. You can + use this to create a temporary file next to the target file, then + write to the temporary file, and finally use overwriteTargetFileWithTemporary() + to replace the target file with the one you've just written. + + This class won't create any files until you actually write to them. And remember + that when this object is deleted, the temporary file will also be deleted! + + @param targetFile the file that you intend to overwrite - the temporary + file will be created in the same directory as this + */ + explicit TemporaryFile (const File& targetFile); /** Creates a temporary file in the same directory as a specified file. @@ -113,7 +139,7 @@ class JUCE_API TemporaryFile @param optionFlags a combination of the values listed in the OptionFlags enum */ TemporaryFile (const File& targetFile, - int optionFlags = 0); + int optionFlags); /** Creates a temporary file using an explicit filename. The other constructors are a better choice than this one, unless for some reason diff --git a/modules/juce_core/maths/juce_Random.cpp b/modules/juce_core/maths/juce_Random.cpp index a11bd8693fe6..ac5a82c943db 100644 --- a/modules/juce_core/maths/juce_Random.cpp +++ b/modules/juce_core/maths/juce_Random.cpp @@ -46,26 +46,29 @@ Random::Random() : seed (1) void Random::setSeed (const int64 newSeed) noexcept { - if (this == &getSystemRandom()) - { - // Resetting the system Random risks messing up - // JUCE's internal state. If you need a predictable - // stream of random numbers you should use a local - // Random object. - jassertfalse; - return; - } + // Resetting the system Random risks messing up JUCE's internal state. + // If you need a predictable stream of random numbers you should use a + // local Random object. + jassert (! isSystemRandom); seed = newSeed; } void Random::combineSeed (const int64 seedValue) noexcept { + // Resetting the system Random risks messing up JUCE's internal state. + // Consider using a local Random object instead. + jassert (! isSystemRandom); + seed ^= nextInt64() ^ seedValue; } void Random::setSeedRandomly() { + // Resetting the system Random risks messing up JUCE's internal state. + // Consider using a local Random object instead. + jassert (! isSystemRandom); + static std::atomic globalSeed { 0 }; combineSeed (globalSeed ^ (int64) (pointer_sized_int) this); @@ -78,13 +81,28 @@ void Random::setSeedRandomly() Random& Random::getSystemRandom() noexcept { - static Random sysRand; + thread_local Random sysRand = std::invoke ([] + { + Random r; + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED + r.isSystemRandom = true; + #endif + return r; + }); + return sysRand; } //============================================================================== int Random::nextInt() noexcept { + // If you encounter this assertion you've likely stored a reference to the + // system random object and are accessing it from a thread other than the + // one it was first created on. This may lead to race conditions on the + // random object. To avoid this assertion call Random::getSystemRandom() + // directly instead of storing a reference. + jassert (! isSystemRandom || this == &getSystemRandom()); + seed = (int64) (((((uint64) seed) * 0x5deece66dLL) + 11) & 0xffffffffffffLL); return (int) (seed >> 16); @@ -186,23 +204,93 @@ class RandomTests final : public UnitTest void runTest() override { beginTest ("Random"); + { + Random r = getRandom(); - Random r = getRandom(); + for (int i = 2000; --i >= 0;) + { + expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); + expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); + expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); + expect (r.nextInt (1) == 0); - for (int i = 2000; --i >= 0;) - { - expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); - expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); - expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); - expect (r.nextInt (1) == 0); + int n = r.nextInt (50) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - int n = r.nextInt (50) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); + n = r.nextInt (0x7ffffffe) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); + } + } - n = r.nextInt (0x7ffffffe) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); + beginTest ("System random stress test"); + { + // Run this with thread-sanitizer to detect race conditions + runOnMultipleThreadsConcurrently ([] { Random::getSystemRandom().nextInt(); }); } } + +private: + static void runOnMultipleThreadsConcurrently (std::function functionToInvoke, + int numberOfInvocationsPerThread = 10'000, + int numberOfThreads = 100) + { + class FastWaitableEvent + { + public: + void notify() { notified = true; } + void wait() const { while (! notified){} } + + private: + std::atomic notified = false; + }; + + class InvokerThread final : private Thread + { + public: + InvokerThread (std::function fn, FastWaitableEvent& notificationEvent, int numInvocationsToTrigger) + : Thread ("InvokerThread"), + invokable (fn), + notified (¬ificationEvent), + numInvocations (numInvocationsToTrigger) + { + startThread(); + } + + ~InvokerThread() { stopThread (-1); } + + void waitUntilReady() const { ready.wait(); } + + private: + void run() final + { + ready.notify(); + notified->wait(); + + for (int i = numInvocations; --i >= 0;) + invokable(); + } + + std::function invokable; + FastWaitableEvent* notified; + FastWaitableEvent ready; + int numInvocations; + }; + + std::vector> threads; + threads.reserve ((size_t) numberOfThreads); + FastWaitableEvent start; + + for (int i = numberOfThreads; --i >= 0;) + threads.push_back (std::make_unique (functionToInvoke, start, numberOfInvocationsPerThread)); + + for (auto& thread : threads) + thread->waitUntilReady(); + + // just to increase the odds that all the threads are now at the same point + // ready to be notified + Thread::sleep (1); + start.notify(); + } }; static RandomTests randomTests; diff --git a/modules/juce_core/maths/juce_Random.h b/modules/juce_core/maths/juce_Random.h index d991bea03674..e49592392154 100644 --- a/modules/juce_core/maths/juce_Random.h +++ b/modules/juce_core/maths/juce_Random.h @@ -128,11 +128,12 @@ class JUCE_API Random final */ void setSeedRandomly(); - /** The overhead of creating a new Random object is fairly small, but if you want to avoid - it, you can call this method to get a global shared Random object. + /** The overhead of creating a new Random object is fairly small, but if you want + to avoid it, you can call this method to get a global shared Random object. - It's not thread-safe though, so threads should use their own Random object, otherwise - you run the risk of your random numbers becoming.. erm.. randomly corrupted.. + Note this will return a different object per thread it's accessed from, + making it thread safe. However, it's therefore important not store a reference + to this object that will later be accessed from other threads. */ static Random& getSystemRandom() noexcept; @@ -140,6 +141,10 @@ class JUCE_API Random final //============================================================================== int64 seed; + #if JUCE_ASSERTIONS_ENABLED_OR_LOGGED + bool isSystemRandom = false; + #endif + JUCE_LEAK_DETECTOR (Random) }; diff --git a/modules/juce_core/memory/juce_ScopedPointer.h b/modules/juce_core/memory/juce_ScopedPointer.h index f16afbb522a1..107190bff1b4 100644 --- a/modules/juce_core/memory/juce_ScopedPointer.h +++ b/modules/juce_core/memory/juce_ScopedPointer.h @@ -46,8 +46,7 @@ class [[deprecated]] ScopedPointer { public: //============================================================================== - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS inline ScopedPointer() {} @@ -154,13 +153,11 @@ class [[deprecated]] ScopedPointer ScopedPointer& operator= (const ScopedPointer&) = delete; #endif - JUCE_END_IGNORE_WARNINGS_MSVC - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS }; //============================================================================== -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS template bool operator== (ObjectType1* pointer1, const ScopedPointer& pointer2) noexcept @@ -228,8 +225,7 @@ template void deleteAndZero (ScopedPointer&) { static_assert (sizeof (Type) == 12345, "Attempt to call deleteAndZero() on a ScopedPointer"); } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_core/misc/juce_RuntimePermissions.h b/modules/juce_core/misc/juce_RuntimePermissions.h index 026e55778ef7..acd144f88b4c 100644 --- a/modules/juce_core/misc/juce_RuntimePermissions.h +++ b/modules/juce_core/misc/juce_RuntimePermissions.h @@ -113,7 +113,13 @@ class JUCE_API RuntimePermissions /** Permission to read video files that your app didn't create. Has the same effect as readExternalStorage on iOS and Android versions before 33. */ - readMediaVideo = 8 + readMediaVideo = 8, + + /** Permission to post notifications. + + @see PushNotifications::requestPermissionsWithSettings + */ + postNotification = 9 }; //============================================================================== diff --git a/modules/juce_core/native/juce_AndroidDocument_android.cpp b/modules/juce_core/native/juce_AndroidDocument_android.cpp index 6282ea6a67a5..fd8e7cabb46d 100644 --- a/modules/juce_core/native/juce_AndroidDocument_android.cpp +++ b/modules/juce_core/native/juce_AndroidDocument_android.cpp @@ -231,12 +231,10 @@ struct AndroidDocumentDetail struct DirectoryIteratorEngine { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS DirectoryIteratorEngine (const File& dir, bool recursive) : iterator (dir, recursive, "*", File::findFilesAndDirectories) {} - JUCE_END_IGNORE_WARNINGS_MSVC - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS auto read() const { return AndroidDocument::fromFile (iterator.getFile()); } bool increment() { return iterator.next(); } diff --git a/modules/juce_core/native/juce_Files_mac.mm b/modules/juce_core/native/juce_Files_mac.mm index dc39f8c54e76..3e81d843bf94 100644 --- a/modules/juce_core/native/juce_Files_mac.mm +++ b/modules/juce_core/native/juce_Files_mac.mm @@ -431,7 +431,7 @@ bool next (String& filenameFound, return true; } - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; @@ -443,7 +443,7 @@ bool next (String& filenameFound, configuration: dict error: nil]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS } if (file.exists()) diff --git a/modules/juce_core/native/juce_Files_windows.cpp b/modules/juce_core/native/juce_Files_windows.cpp index bbfe611c291f..8db0a1ff9190 100644 --- a/modules/juce_core/native/juce_Files_windows.cpp +++ b/modules/juce_core/native/juce_Files_windows.cpp @@ -253,12 +253,12 @@ namespace WindowsFileHelpers //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const juce_wchar File::separator = '\\'; const StringRef File::separatorString ("\\"); -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif diff --git a/modules/juce_core/native/juce_RuntimePermissions_android.cpp b/modules/juce_core/native/juce_RuntimePermissions_android.cpp index d66d817b80aa..867bd2129c9e 100644 --- a/modules/juce_core/native/juce_RuntimePermissions_android.cpp +++ b/modules/juce_core/native/juce_RuntimePermissions_android.cpp @@ -81,6 +81,9 @@ static StringArray jucePermissionToAndroidPermissions (RuntimePermissions::Permi case RuntimePermissions::readMediaVideo: return { externalStorageOrMedia ("android.permission.READ_MEDIA_VIDEO") }; + + case RuntimePermissions::postNotification: + return { "android.permission.POST_NOTIFICATIONS" }; } // invalid permission @@ -101,6 +104,7 @@ static RuntimePermissions::PermissionID androidPermissionToJucePermission (const { "android.permission.READ_MEDIA_IMAGES", RuntimePermissions::readMediaImages }, { "android.permission.READ_MEDIA_VIDEO", RuntimePermissions::readMediaVideo }, { "android.permission.BLUETOOTH_SCAN", RuntimePermissions::bluetoothMidi }, + { "android.permission.POST_NOTIFICATIONS", RuntimePermissions::postNotification }, }; const auto iter = map.find (permission); @@ -118,8 +122,7 @@ struct PermissionsRequest //============================================================================== struct PermissionsOverlay final : public FragmentOverlay { - PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {} - ~PermissionsOverlay() override = default; + explicit PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {} struct PermissionResult { diff --git a/modules/juce_core/native/juce_SharedCode_posix.h b/modules/juce_core/native/juce_SharedCode_posix.h index c53ef0fff6f3..3f505f09c037 100644 --- a/modules/juce_core/native/juce_SharedCode_posix.h +++ b/modules/juce_core/native/juce_SharedCode_posix.h @@ -113,12 +113,12 @@ static MaxNumFileHandlesInitialiser maxNumFileHandlesInitialiser; //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const juce_wchar File::separator = '/'; const StringRef File::separatorString ("/"); -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif diff --git a/modules/juce_core/native/juce_Threads_windows.cpp b/modules/juce_core/native/juce_Threads_windows.cpp index be21a44c637a..a459a7800e21 100644 --- a/modules/juce_core/native/juce_Threads_windows.cpp +++ b/modules/juce_core/native/juce_Threads_windows.cpp @@ -325,7 +325,7 @@ void DynamicLibrary::close() void* DynamicLibrary::getFunction (const String& functionName) noexcept { - return handle != nullptr ? (void*) GetProcAddress ((HMODULE) handle, functionName.toUTF8()) // (void* cast is required for mingw) + return handle != nullptr ? (void*) GetProcAddress ((HMODULE) handle, functionName.toUTF8()) : nullptr; } diff --git a/modules/juce_core/system/juce_CompilerWarnings.h b/modules/juce_core/system/juce_CompilerWarnings.h index 9bd8f5abea1b..1f4905479723 100644 --- a/modules/juce_core/system/juce_CompilerWarnings.h +++ b/modules/juce_core/system/juce_CompilerWarnings.h @@ -238,6 +238,14 @@ #define JUCE_SANITIZER_ATTRIBUTE_MINIMUM_CLANG_VERSION 9 #endif +#define JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS \ + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") \ + JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + +#define JUCE_END_IGNORE_DEPRECATION_WARNINGS \ + JUCE_END_IGNORE_WARNINGS_MSVC \ + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + /** Disable sanitizers for a range of functions. This functionality doesn't seem to exist on GCC yet, so at the moment this only works for clang. diff --git a/modules/juce_core/system/juce_StandardHeader.h b/modules/juce_core/system/juce_StandardHeader.h index df6f6553a1ca..ff03eef3ddb9 100644 --- a/modules/juce_core/system/juce_StandardHeader.h +++ b/modules/juce_core/system/juce_StandardHeader.h @@ -81,6 +81,7 @@ #include #include #include +#include #include #include #include diff --git a/modules/juce_core/system/juce_TargetPlatform.h b/modules/juce_core/system/juce_TargetPlatform.h index 0c55ad7c586a..ee433d33873c 100644 --- a/modules/juce_core/system/juce_TargetPlatform.h +++ b/modules/juce_core/system/juce_TargetPlatform.h @@ -110,13 +110,7 @@ #endif #ifdef __MINGW32__ - #define JUCE_MINGW 1 - #warning Support for MinGW has been removed. Please use an alternative compiler. - #ifdef __MINGW64__ - #define JUCE_64BIT 1 - #else - #define JUCE_32BIT 1 - #endif + #error "MinGW is not supported. Please use an alternative compiler." #endif /** If defined, this indicates that the processor is little-endian. */ diff --git a/modules/juce_core/text/juce_String.cpp b/modules/juce_core/text/juce_String.cpp index 47cd5a64a62a..d85cfd3009ef 100644 --- a/modules/juce_core/text/juce_String.cpp +++ b/modules/juce_core/text/juce_String.cpp @@ -1860,7 +1860,7 @@ String String::formattedRaw (const char* pf, ...) va_start (args, pf); #if JUCE_WINDOWS - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS #endif #if JUCE_ANDROID @@ -1881,7 +1881,7 @@ String String::formattedRaw (const char* pf, ...) #endif #if JUCE_WINDOWS - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif va_end (args); @@ -2342,13 +2342,11 @@ static String serialiseDouble (double input, int maxDecimalPlaces = 0) //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const String String::empty; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif @@ -2371,49 +2369,55 @@ class StringTests final : public UnitTest { static void test (UnitTest& test, Random& r) { - String s (createRandomWideCharString (r)); + constexpr auto stringLength = 50; + const String s (createRandomWideCharString (r, stringLength)); using CharType = typename CharPointerType::CharType; - CharType buffer[300]; + constexpr auto bytesPerCodeUnit = sizeof (CharType); + constexpr auto maxCodeUnitsPerCodePoint = 4 / bytesPerCodeUnit; - memset (buffer, 0xff, sizeof (buffer)); - CharPointerType (buffer).writeAll (s.toUTF32()); - test.expectEquals (String (CharPointerType (buffer)), s); + std::array codeUnits{}; + const auto codeUnitsSizeInBytes = codeUnits.size() * bytesPerCodeUnit; - memset (buffer, 0xff, sizeof (buffer)); - CharPointerType (buffer).writeAll (s.toUTF16()); - test.expectEquals (String (CharPointerType (buffer)), s); + std::memset (codeUnits.data(), 0xff, codeUnitsSizeInBytes); + CharPointerType (codeUnits.data()).writeAll (s.toUTF32()); + test.expectEquals (String (CharPointerType (codeUnits.data())), s); - memset (buffer, 0xff, sizeof (buffer)); - CharPointerType (buffer).writeAll (s.toUTF8()); - test.expectEquals (String (CharPointerType (buffer)), s); + std::memset (codeUnits.data(), 0xff, codeUnitsSizeInBytes); + CharPointerType (codeUnits.data()).writeAll (s.toUTF16()); + test.expectEquals (String (CharPointerType (codeUnits.data())), s); - const auto nullTerminator = std::find (buffer, buffer + std::size (buffer), (CharType) 0); - const auto numValidBytes = (int) std::distance (buffer, nullTerminator) * (int) sizeof (CharType); + std::memset (codeUnits.data(), 0xff, codeUnitsSizeInBytes); + CharPointerType (codeUnits.data()).writeAll (s.toUTF8()); + test.expectEquals (String (CharPointerType (codeUnits.data())), s); - test.expect (CharPointerType::isValidString (buffer, numValidBytes)); + test.expect (CharPointerType::isValidString (codeUnits.data(), codeUnitsSizeInBytes)); } }; - static String createRandomWideCharString (Random& r) + static String createRandomWideCharString (Random& r, size_t length) { - juce_wchar buffer[50] = { 0 }; + std::vector characters (length, 0); - for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) + for (auto& character : characters) { if (r.nextBool()) { do { - buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); + character = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); } - while (! CharPointer_UTF16::canRepresent (buffer[i])); + while (! CharPointer_UTF16::canRepresent (character)); } else - buffer[i] = (juce_wchar) (1 + r.nextInt (0xff)); + { + character = (juce_wchar) (1 + r.nextInt (0xff)); + } } - return CharPointer_UTF32 (buffer); + characters.push_back (0); + + return CharPointer_UTF32 (characters.data()); } void runTest() override diff --git a/modules/juce_data_structures/values/juce_ValueTree.cpp b/modules/juce_data_structures/values/juce_ValueTree.cpp index e534747f140e..b0df154664d0 100644 --- a/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -1111,13 +1111,11 @@ void ValueTree::Listener::valueTreeRedirected (ValueTree&) //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const ValueTree ValueTree::invalid; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif diff --git a/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp index a322b739e4e5..533a75e3080a 100644 --- a/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp +++ b/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -149,11 +149,9 @@ void ChildProcessCoordinator::handleConnectionLost() {} void ChildProcessCoordinator::handleMessageFromWorker (const MemoryBlock& mb) { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS handleMessageFromSlave (mb); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_DEPRECATION_WARNINGS } bool ChildProcessCoordinator::sendMessageToWorker (const MemoryBlock& mb) @@ -276,11 +274,9 @@ void ChildProcessWorker::handleConnectionLost() {} void ChildProcessWorker::handleMessageFromCoordinator (const MemoryBlock& mb) { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS handleMessageFromMaster (mb); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_DEPRECATION_WARNINGS } bool ChildProcessWorker::sendMessageToCoordinator (const MemoryBlock& mb) diff --git a/modules/juce_events/native/juce_MessageManager_mac.mm b/modules/juce_events/native/juce_MessageManager_mac.mm index f727616ae974..07779664332b 100644 --- a/modules/juce_events/native/juce_MessageManager_mac.mm +++ b/modules/juce_events/native/juce_MessageManager_mac.mm @@ -142,11 +142,11 @@ { if (notification.userInfo != nil) { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS // NSUserNotification is deprecated from macOS 11, but there doesn't seem to be a // replacement for NSApplicationLaunchUserNotificationKey returning a non-deprecated type NSUserNotification* userNotification = notification.userInfo[NSApplicationLaunchUserNotificationKey]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion") if (userNotification != nil && userNotification.userInfo != nil) diff --git a/modules/juce_graphics/colour/juce_PixelFormats.h b/modules/juce_graphics/colour/juce_PixelFormats.h index 7a17a87aa5d5..cd5bc4d168f2 100644 --- a/modules/juce_graphics/colour/juce_PixelFormats.h +++ b/modules/juce_graphics/colour/juce_PixelFormats.h @@ -556,7 +556,7 @@ class JUCE_API PixelRGB //============================================================================== /** The indexes of the different components in the byte layout of this type of colour. */ - #if JUCE_MAC + #if JUCE_MAC || JUCE_IOS enum { indexR = 0, indexG = 1, indexB = 2 }; #else enum { indexR = 2, indexG = 1, indexB = 0 }; @@ -578,7 +578,7 @@ class JUCE_API PixelRGB } //============================================================================== - #if JUCE_MAC + #if JUCE_MAC || JUCE_IOS uint8 r, g, b; #else uint8 b, g, r; diff --git a/modules/juce_graphics/detail/juce_Ranges.cpp b/modules/juce_graphics/detail/juce_Ranges.cpp index 2c88c9fb1a55..ea05cd2f17dc 100644 --- a/modules/juce_graphics/detail/juce_Ranges.cpp +++ b/modules/juce_graphics/detail/juce_Ranges.cpp @@ -406,6 +406,33 @@ class RangesTests : public RangesTestsBase ranges.drop ({ -1000, 1000 }); expect (ranges.isEmpty()); } + + beginTest ("Ranges::covers()"); + { + Ranges ranges; + + ranges.set ({ 0, 48 }); + ranges.set ({ 48, 49 }); + ranges.set ({ 55, 94 }); + ranges.set ({ 94, 127 }); + ranges.set ({ 127, 150 }); + + expect (ranges.covers ({ 0, 48 })); + expect (ranges.covers ({ 0, 20 })); + expect (ranges.covers ({ 10, 30 })); + expect (ranges.covers ({ 30, 48 })); + expect (ranges.covers ({ 30, 49 })); + expect (ranges.covers ({ 55, 150 })); + expect (ranges.covers ({ 60, 145 })); + + expect (! ranges.covers ({ -1, 10 })); + expect (! ranges.covers ({ 1, 50 })); + expect (! ranges.covers ({ 50, 140 })); + expect (! ranges.covers ({ 149, 151 })); + + expect (ranges.covers ({ 10, 10 })); + expect (! ranges.covers ({ 151, 151 })); + } } }; @@ -663,6 +690,41 @@ class RangedValuesTests : public UnitTest expectRangedValuesItem (rangedValues.getItem (5), { 45, 60 }, 'd'); } + beginTest ("RangedValues::eraseUpTo() - erasing before all ranges has no effect"); + { + auto rangedValues = createRangedValuesObjectForErase(); + + rangedValues.eraseUpTo (rangedValues.getRanges().get (0).getStart()); + + expectRangedValuesItem (rangedValues.getItem (0), { 0, 10 }, 'a'); + expectRangedValuesItem (rangedValues.getItem (1), { 11, 20 }, 'b'); + expectRangedValuesItem (rangedValues.getItem (2), { 23, 30 }, 'c'); + expectRangedValuesItem (rangedValues.getItem (3), { 35, 45 }, 'c'); + expectRangedValuesItem (rangedValues.getItem (4), { 45, 60 }, 'd'); + } + + beginTest ("RangedValues::eraseUpTo() - erasing values up to, not including 15"); + { + auto rangedValues = createRangedValuesObjectForErase(); + + rangedValues.eraseUpTo (15); + + expectRangedValuesItem (rangedValues.getItem (0), { 15, 20 }, 'b'); + expectRangedValuesItem (rangedValues.getItem (1), { 23, 30 }, 'c'); + expectRangedValuesItem (rangedValues.getItem (2), { 35, 45 }, 'c'); + expectRangedValuesItem (rangedValues.getItem (3), { 45, 60 }, 'd'); + } + + beginTest ("RangedValues::eraseUpTo() - erasing up to the end of all ranges clears the container"); + { + auto rangedValues = createRangedValuesObjectForErase(); + const auto ranges = rangedValues.getRanges(); + + rangedValues.eraseUpTo (ranges.get (ranges.size() - 1).getEnd()); + + expect (rangedValues.isEmpty()); + } + beginTest ("RangedValues::drop() - drop shifts ranges downward on the right side"); { auto rangedValues = createRangedValuesObjectForErase(); diff --git a/modules/juce_graphics/detail/juce_Ranges.h b/modules/juce_graphics/detail/juce_Ranges.h index 750724270978..a84987db5a0e 100644 --- a/modules/juce_graphics/detail/juce_Ranges.h +++ b/modules/juce_graphics/detail/juce_Ranges.h @@ -331,19 +331,6 @@ struct Ranges final return result; } - std::optional getIndexForEnclosingRange (int64 positionInTextRange) const - { - auto it = std::lower_bound (ranges.begin(), - ranges.end(), - positionInTextRange, - [] (auto& elem, auto& value) { return elem.getEnd() <= value; }); - - if (it != ranges.end() && it->getStart() <= positionInTextRange) - return getIndex (it); - - return std::nullopt; - } - //============================================================================== size_t size() const { @@ -381,6 +368,42 @@ struct Ranges final return ranges.cend(); } + /* Returns an iterator for the Range element which includes the provided value. */ + auto find (int64 i) const + { + const auto it = std::lower_bound (cbegin(), + cend(), + i, + [] (auto& elem, auto& value) { return elem.getEnd() <= value; }); + + return it != cend() && it->getStart() <= i ? it : cend(); + } + + std::optional getIndexForEnclosingRange (int64 positionInTextRange) const + { + const auto iter = find (positionInTextRange); + return iter != ranges.end() ? std::make_optional (getIndex (iter)) : std::nullopt; + } + + /* Returns true if this object covers each element in the provided range. For empty ranges it + returns true if the start value is covered. + */ + bool covers (Range range) const + { + for (auto curr = find (range.getStart()), prev = curr; curr != cend(); ++curr) + { + if (prev != curr && prev->getEnd() != curr->getStart()) + return false; + + if (range.getEnd() <= curr->getEnd()) + return true; + + prev = curr; + } + + return false; + } + private: size_t getIndex (std::vector>::const_iterator it) const { @@ -638,6 +661,14 @@ class RangedValues return erase ({ i, ranges.get (ranges.size() - 1).getEnd() }); } + Ranges::Operations eraseUpTo (int64 i) + { + if (ranges.isEmpty()) + return {}; + + return erase ({ ranges.get (0).getStart(), i }); + } + /** Create a RangedValues object from non-overlapping ranges. */ template auto setForEach (Iterable begin, Iterable end) @@ -663,6 +694,17 @@ class RangedValues return getItemWithEnclosingRangeImpl (*this, i); } + // Finds the item whose range encloses the provided value + template + static auto findImpl (Self& self, int64 i) + { + return iteratorWithAdvance (self.begin(), + std::distance (self.ranges.cbegin(), self.ranges.find (i))); + } + + auto find (int64 i) { return findImpl (*this, i); } + auto find (int64 i) const { return findImpl (*this, i); } + Item getItem (size_t i) { jassert (i < values.size()); diff --git a/modules/juce_graphics/fonts/juce_Font.cpp b/modules/juce_graphics/fonts/juce_Font.cpp index 5641606f1b79..b6553ed19e97 100644 --- a/modules/juce_graphics/fonts/juce_Font.cpp +++ b/modules/juce_graphics/fonts/juce_Font.cpp @@ -754,11 +754,9 @@ float Font::getDescentInPoints() const { return getDescent() * getHeightToP int Font::getStringWidth (const String& text) const { - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return (int) std::ceil (getStringWidthFloat (text)); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_DEPRECATION_WARNINGS } float Font::getStringWidthFloat (const String& text) const @@ -941,11 +939,9 @@ class FontTests : public UnitTest beginTest ("Old constructor from Typeface"); { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS Font f { face }; - JUCE_END_IGNORE_WARNINGS_MSVC - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS expect (f.getTypefaceName() == face->getName()); expect (f.getTypefaceStyle() == face->getStyle()); diff --git a/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp b/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp index 51bd2a8208d0..b73e22b89409 100644 --- a/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp +++ b/modules/juce_graphics/fonts/juce_SimpleShapedText.cpp @@ -167,6 +167,12 @@ struct ShapedGlyph Point offset; }; +struct GlyphLookupEntry +{ + Range glyphRange; + bool ltr = true; +}; + class SimpleShapedText { public: @@ -190,10 +196,6 @@ class SimpleShapedText juce_wchar getCodepoint (int64 glyphIndex) const; - Range getGlyphRangeForLine (size_t line) const; - - std::vector getResolvedFontsIntersectingGlyphRange (Range glyphRange) const; - Span getGlyphs (Range glyphRange) const; Span getGlyphs() const; @@ -202,12 +204,6 @@ class SimpleShapedText void shape (const String& data, const ShapedTextOptions& options); - struct GlyphLookupEntry - { - Range glyphRange; - bool ltr = true; - }; - const String& string; std::vector glyphsInVisualOrder; detail::RangedValues lineNumbers; @@ -674,178 +670,6 @@ struct ShapingParams Font resolvedFont; }; -struct LineAdvance -{ - float includingTrailingWhitespace; - float maybeIgnoringWhitespace; -}; - -struct ConsumableGlyphs -{ -public: - ConsumableGlyphs (const String& stringIn, - Range rangeIn, - ShapingParams params) - : string (stringIn), - range (rangeIn), - shapingParams (std::move (params)) - { - reshape(); - } - - /* If the break happens at a safe-to-break point as per HB, it will just discard the consumed - range. Otherwise, it reshapes the remaining text. - */ - void breakBeforeAndConsume (int64 codepointIndex) - { - jassert (codepointIndex >= range.getStart()); - - range = range.withStart (codepointIndex); - - if (isSafeToBreakBefore (codepointIndex)) - { - glyphs.erase (glyphs.begin(), iteratorWithAdvance (glyphs.begin(), *getGlyphIndexForCodepoint (codepointIndex))); - recalculateAdvances(); - } - else if (! range.isEmpty()) - { - reshape(); - } - } - - /* Returns the glyphs starting from the first unconsumed glyph, and ending with the one that - covers the requested input codepoint range. - - If the provided range end corresponds to an unsafe break, an empty Span will be returned. - - Should only be called with indices for which isSafeToBreakBefore() returns true. - - There is an exception to this, which is the "beyond end codepoint index", which returns all - glyphs without reshaping. - */ - Span getGlyphs (int64 beyondEndCodepointIndex) const - { - if (beyondEndCodepointIndex == range.getEnd()) - return { glyphs }; - - if (isSafeToBreakBefore (beyondEndCodepointIndex)) - return { glyphs.data(), *getGlyphIndexForCodepoint (beyondEndCodepointIndex) }; - - return {}; - } - - /* Returns false for the beyond end index, because the safety of breaking cannot be determined - at this point. - */ - bool isSafeToBreakBefore (int64 codepointIndex) const - { - if (auto i = getGlyphIndexForCodepoint (codepointIndex)) - return ! glyphs[*i].unsafeToBreak; - - return false; - } - - /* If this function returns a value that also means that it's safe to break before the provided - codepoint. Otherwise, we couldn't meaningfully calculate the requested value. - */ - std::optional getAdvanceXUpToBreakPointIfSafe (int64 breakBefore, - bool whitespaceShouldFitInLine) const - { - const auto breakBeforeGlyphIndex = [&]() -> std::optional - { - if (breakBefore == range.getEnd()) - return cumulativeAdvanceX.size() - 1; - - if (isSafeToBreakBefore (breakBefore)) - return *getGlyphIndexForCodepoint (breakBefore); - - return std::nullopt; - }(); - - if (! breakBeforeGlyphIndex.has_value()) - return std::nullopt; - - const auto includingTrailingWhitespace = cumulativeAdvanceX [*breakBeforeGlyphIndex]; - - if (! whitespaceShouldFitInLine) - { - for (auto i = (int64) *breakBeforeGlyphIndex; --i >= 0;) - { - if (! glyphs[(size_t) i].whitespace) - return LineAdvance { includingTrailingWhitespace, cumulativeAdvanceX[(size_t) i + 1] }; - } - } - - return LineAdvance { includingTrailingWhitespace, includingTrailingWhitespace }; - } - - auto isEmpty() const - { - return range.getLength() == 0; - } - - auto getCodepointRange() const - { - return range; - } - -private: - std::optional getGlyphIndexForCodepoint (int64 codepointIndex) const - { - const auto it = std::lower_bound (glyphs.cbegin(), - glyphs.cend(), - codepointIndex, - [] (auto& elem, auto& value) { return elem.cluster < value; }); - - if (it != glyphs.cend() && it->cluster == codepointIndex) - return (size_t) std::distance (glyphs.cbegin(), it); - - return std::nullopt; - } - - void reshape() - { - glyphs = lowLevelShape (string, - getCodepointRange(), - shapingParams.resolvedFont, - shapingParams.script, - shapingParams.language, - shapingParams.embeddingLevel); - - recalculateAdvances(); - } - - void recalculateAdvances() - { - cumulativeAdvanceX.clear(); - cumulativeAdvanceX.reserve (glyphs.size() + 1); - cumulativeAdvanceX.push_back (0.0f); - - for (const auto& glyph : glyphs) - cumulativeAdvanceX.push_back (glyph.advance.getX() - + (cumulativeAdvanceX.empty() ? 0.0f : cumulativeAdvanceX.back())); - } - - const String& string; - Range range; - ShapingParams shapingParams; - std::vector glyphs; - std::vector cumulativeAdvanceX; -}; - -static bool isLtr (int bidiNestingLevel) -{ - return (bidiNestingLevel & 1) == 0; -} - -struct LineChunkInLogicalOrder -{ - Range textRange; - std::vector glyphs; - Font resolvedFont; - int bidiLevel{}; -}; - // Used to avoid signedness warning for types for which std::size() is int template static auto makeSpan (T& array) @@ -853,16 +677,16 @@ static auto makeSpan (T& array) return Span { array.getRawDataPointer(), (size_t) array.size() }; } -static std::vector, Font>> findSuitableFontsForText (const Font& font, - const String& text, - const String& language = {}) +static std::vector findSuitableFontsForText (const Font& font, + const String& text, + const String& language = {}) { detail::RangedValues> fonts; fonts.set ({ 0, (int64) text.length() }, font); const auto getResult = [&] { - std::vector, Font>> result; + std::vector result; for (const auto [r, v] : fonts) result.emplace_back (r, v.value_or (font)); @@ -899,7 +723,7 @@ static std::vector, Font>> findSuitableFontsForText (cons // can't find any more suitable fonts or all codepoints have one for (auto numMissingGlyphs = markMissingGlyphs(); numMissingGlyphs > 0;) { - std::vector, Font>> changes; + std::vector changes; for (const auto [r, f] : fonts) { @@ -925,430 +749,663 @@ static std::vector, Font>> findSuitableFontsForText (cons return getResult(); } -// TODO(ati) Use glyphNotFoundCharacter -void SimpleShapedText::shape (const String& data, - const ShapedTextOptions& options) +static RangedValues resolveFontsWithFallback (const String& string, const RangedValues& fonts) { - const auto fonts = [&] + RangedValues resolved; + + for (const auto [r, f] : fonts) { - RangedValues result; + auto rf = findSuitableFontsForText (f, string.substring ((int) r.getStart(), + (int) std::min (r.getEnd(), (int64) string.length()))); - for (const auto& [range, font] : options.getFontsForRange()) - result.insert ({ (int64) range.getStart(), (int64) range.getEnd() }, font); + for (auto& item : rf) + item.first += r.getStart(); - return result; - }(); - - std::vector lineChunks; - int64 numGlyphsInLine = 0; + resolved.setForEach (rf.begin(), rf.end()); + } - const auto analysis = Unicode::performAnalysis (data); + return resolved; +} - std::vector data32 ((size_t) data.length()); - data.copyToUTF32 (data32.data(), data32.size() * sizeof (juce_wchar)); - const BidiAlgorithm bidiAlgorithm (data32); +struct GlyphsStorage +{ + std::shared_ptr> data; + bool ltr{}; + Font font; +}; - IntegralCanBreakBeforeIterator softBreakIterator { makeSpan (analysis) }; +struct OwnedGlyphsSpan +{ +public: + OwnedGlyphsSpan (GlyphsStorage subOwnedGlyphsSpanIn, + Span glyphsIn, + Range textRangeIn, + size_t visualOrderIn) + : subOwnedGlyphsSpan { std::move (subOwnedGlyphsSpanIn) }, + glyphs { glyphsIn }, + textRange { textRangeIn }, + visualOrder { visualOrderIn } + {} - const auto spanLookup = makeSubSpanLookup (makeSpan (analysis)); + auto& operator* () { return glyphs; } + auto& operator* () const { return glyphs; } - auto remainingWidth = options.getMaxWidth().has_value() ? (*options.getMaxWidth() - options.getFirstLineIndent()) - : std::optional{}; + auto operator-> () { return &glyphs; } + auto operator-> () const { return &glyphs; } - std::vector visualOrder; + bool operator== (const OwnedGlyphsSpan& other) const + { + return glyphs.data() == other.glyphs.data() + && glyphs.size() == other.glyphs.size(); + } - const auto commitLine = [&] (const BidiParagraph& bidiParagraph) + bool operator!= (const OwnedGlyphsSpan& other) const { - if (lineChunks.empty()) - return; + return ! (*this == other); + } - const auto begin = (size_t) lineChunks.front().textRange.getStart(); - const auto end = (size_t) lineChunks.back().textRange.getEnd(); - const auto bidiLine = bidiParagraph.createLine (begin, end - begin); + auto getVisualOrder() const { return visualOrder; } + auto isLtr() const { return subOwnedGlyphsSpan.ltr; } + auto getTextRange() const { return textRange; } + const auto& getFont() const { return subOwnedGlyphsSpan.font; } - bidiLine.computeVisualOrder (visualOrder); +private: + GlyphsStorage subOwnedGlyphsSpan; + Span glyphs; + Range textRange; + size_t visualOrder; +}; - const auto indicesInVisualOrder = [&] +/* Objects of this type contain a ShapedGlyph range that terminates with a glyph after which + soft-wrapping is possible. There are no soft-break opportunities anywhere else inside the range. +*/ +using WrappedGlyphs = std::vector; + +/* Contains a WrappedGlyphs object and marks a location (a particular glyph) somewhere inside it. + + Allows keeping track of partially consuming such objects to support mid-word breaking where the + line is shorter than a single word. +*/ +struct WrappedGlyphsCursor +{ + WrappedGlyphsCursor (const OwnedGlyphsSpan* dataIn, size_t num) + : data { dataIn, num } + {} + + bool empty() const + { + return data.empty() || data.back()->empty(); + } + + bool isBeyondEnd() const + { + return empty() || data.size() <= index.i; + } + + auto& operator+= (size_t d) + { + while (d > 0 && ! isBeyondEnd()) { - std::vector result; - result.reserve (lineChunks.size()); + const auto delta = std::min (d, data[index.i]->size() - index.j); + index.j += delta; + d -= delta; - for (auto it = visualOrder.begin(); it != visualOrder.end();) + if (index.j == data[index.i]->size()) { - const auto logicalIndex = *it; - const auto chunk = std::lower_bound (lineChunks.begin(), - lineChunks.end(), - logicalIndex, - [] (const LineChunkInLogicalOrder& c, size_t x) - { - return (size_t) c.textRange.getEnd() <= x; - }); - - jassert (chunk != lineChunks.end()); - - result.push_back ((size_t) std::distance (lineChunks.begin(), chunk)); - it += std::min ((ptrdiff_t) std::distance (it, visualOrder.end()), - (ptrdiff_t) chunk->textRange.getLength()); + ++index.i; + index.j = 0; } + } - return result; - }(); + return *this; + } - for (auto chunkIndex : indicesInVisualOrder) - { - auto& chunk = lineChunks[chunkIndex]; + auto& operator++() + { + return *this += 1; + } - const auto glyphRange = Range::withStartAndLength ((int64) glyphsInVisualOrder.size(), - (int64) chunk.glyphs.size()); + auto& operator*() { return (*data[index.i])[index.j]; } + auto& operator*() const { return (*data[index.i])[index.j]; } - if (isLtr (chunk.bidiLevel)) - glyphsInVisualOrder.insert (glyphsInVisualOrder.end(), chunk.glyphs.begin(), chunk.glyphs.end()); - else - glyphsInVisualOrder.insert (glyphsInVisualOrder.end(), chunk.glyphs.rbegin(), chunk.glyphs.rend()); + auto* operator->() { return &(*data[index.i])[index.j]; } + auto* operator->() const { return &(*data[index.i])[index.j]; } - resolvedFonts.insert ({ glyphRange.getStart(), glyphRange.getEnd() }, chunk.resolvedFont); + size_t size() const + { + if (empty() || isBeyondEnd()) + return 0; - glyphLookup.set (chunk.textRange, - { glyphRange, isLtr (chunk.bidiLevel) }); - } + size_t size{}; - lineChunks.clear(); + for (auto copy = *this; ! copy.isBeyondEnd(); ++copy) + ++size; - const auto [lineRange, lineNumber] = [&] - { - const auto lineRangeStart = lineNumbers.isEmpty() ? (int64) 0 : lineNumbers.getRanges().get (lineNumbers.getRanges().size() - 1).getEnd(); - const auto lineRangeEnd = lineRangeStart + numGlyphsInLine; - const auto numLine = lineNumbers.isEmpty() ? (int64) 0 : lineNumbers.getItem (lineNumbers.size() - 1).value + 1; + return size; + } - return std::make_pair (Range { lineRangeStart, lineRangeEnd }, numLine); - }(); + auto getTextRange() const + { + Range textRange; + + for (const auto& chunk : data) + textRange = textRange.getUnionWith (chunk.getTextRange()); + + return textRange; + } + + bool operator== (const WrappedGlyphsCursor& other) const + { + const auto tie = [] (auto& x) { return std::tuple (x.data.data(), x.data.size(), x.index); }; + return tie (*this) == tie (other); + } + + bool operator!= (const WrappedGlyphsCursor& other) const + { + return ! operator== (other); + } + + auto& back() { return data.back()->back(); } + auto& back() const { return data.back()->back(); } - if (const auto numLines = (int64) lineNumbers.size(); - numLines == 0 || numLines < options.getMaxNumLines()) + struct ShapedGlyphSpan + { + const ShapedGlyph* start; + const ShapedGlyph* end; + size_t visualOrder; + Range textRange; + Font font; + }; + + std::vector getShapedGlyphSpansUpTo (const WrappedGlyphsCursor& end) const + { + std::vector spans; + + if (data.data() != end.data.data() || data.size() != end.data.size()) { - lineNumbers.insert (lineRange, lineNumber); + jassertfalse; + return spans; } - else + + for (auto indexCopy = index; indexCopy < end.index;) { - const auto lastLine = lineNumbers.getItem (lineNumbers.size() - 1); + auto& chunk = data[indexCopy.i]; + + const auto glyphsStart = chunk->begin() + indexCopy.j; + const auto glyphsEnd = chunk->end() - (indexCopy.i < end.index.i ? 0 : chunk->size() - end.index.j); + + const auto directionalStart = chunk.isLtr() ? glyphsStart : glyphsEnd - 1; + const auto directionalEnd = chunk.isLtr() ? glyphsEnd : glyphsStart - 1; - jassert (lineRange.getStart() >= lastLine.range.getEnd()); + const auto textStart = glyphsStart->cluster; + const auto textEnd = glyphsEnd < chunk->end() ? glyphsEnd->cluster : chunk.getTextRange().getEnd(); - lineNumbers.set ({ lastLine.range.getStart(), lineRange.getEnd() }, lastLine.value); + spans.push_back ({ directionalStart, + directionalEnd, + chunk.getVisualOrder(), + { textStart, textEnd }, + chunk.getFont() }); + + ++indexCopy.i; + indexCopy.j = 0; } - numGlyphsInLine = 0; - remainingWidth = options.getMaxWidth(); - }; + return spans; + } - enum class CanAddGlyphsBeyondLineLimits +private: + struct Index { - no, - yes - }; + size_t i{}, j{}; - struct ConsumedGlyphs - { - std::vector glyphs; - Range textRange; + auto asTuple() const { return std::make_tuple (i, j); } + bool operator== (const Index& other) const { return asTuple() == other.asTuple(); } + bool operator< (const Index& other) const { return asTuple() < other.asTuple(); } }; - const auto append = [&] (const BidiParagraph& bidiParagraph, Range range, const ShapingParams& shapingParams) - { - jassert (! range.isEmpty()); - - ConsumableGlyphs glyphsToConsume { data, range, shapingParams }; + Span data; + Index index; +}; - const auto appendingToFirstLine = [&] { return lineNumbers.isEmpty(); }; - const auto appendingToBeforeLastLine = [&] { return (int64) lineNumbers.size() < options.getMaxNumLines() - 1; }; +template +static auto createRangedValues (const std::vector, T>>& pairs, int64 offset = 0) +{ + detail::RangedValues result; - while (! glyphsToConsume.isEmpty()) - { - const auto remainingCodepointsToConsume = glyphsToConsume.getCodepointRange(); - softBreakIterator.reset (remainingCodepointsToConsume); + for (const auto& [range, value] : pairs) + result.insert (range.movedToStartAt (range.getStart() - offset), value); - struct BestMatch - { - int64 breakBefore{}; + result.eraseUpTo (0); - // We need to use maybeIgnoringWhitespace in comparisons, but - // includingTrailingWhitespace when using subtraction to calculate the remaining - // space. - LineAdvance advance{}; + return result; +} - bool unsafe{}; - std::vector unsafeGlyphs; - }; +struct Shaper +{ + Shaper (const String& stringIn, Range shapingRange, const ShapedTextOptions& options) + : string { stringIn.substring ((int) shapingRange.getStart(), (int) shapingRange.getEnd()) } + { + const auto analysis = Unicode::performAnalysis (string); + + const auto string32 = std::invoke ([this] + { + std::vector s32 ((size_t) string.length()); + string.copyToUTF32 (s32.data(), s32.size() * sizeof (juce_wchar)); + return s32; + }); + + const BidiAlgorithm bidiAlgorithm { string32 }; + const auto bidiParagraph = bidiAlgorithm.createParagraph (0, options.getReadingDirection()); + const auto bidiLine = bidiParagraph.createLine (0, bidiParagraph.getLength()); + bidiLine.computeVisualOrder (visualOrder); - std::optional bestMatch; + const auto bidiLevels = bidiParagraph.getResolvedLevels(); - static constexpr auto floatMax = std::numeric_limits::max(); + const auto fonts = resolveFontsWithFallback (string, + createRangedValues (options.getFontsForRange(), + shapingRange.getStart())); - for (auto breakBefore = softBreakIterator.next(); - breakBefore.has_value() && (appendingToFirstLine() || appendingToBeforeLastLine()); - breakBefore = softBreakIterator.next()) + for (Unicode::LineBreakIterator lineIter { makeSpan (analysis) }; auto lineRun = lineIter.next();) + { + for (Unicode::ScriptRunIterator scriptIter { *lineRun }; auto scriptRun = scriptIter.next();) { - if (auto safeAdvance = glyphsToConsume.getAdvanceXUpToBreakPointIfSafe (*breakBefore, - options.getTrailingWhitespacesShouldFit())) - { - if (safeAdvance->maybeIgnoringWhitespace < remainingWidth.value_or (floatMax) || ! bestMatch.has_value()) - bestMatch = BestMatch { *breakBefore, *safeAdvance, false, std::vector{} }; - else - break; // We found a safe break that is too large to fit. Anything beyond - // this point would be too large to fit. - } - else + const auto offsetInText = (size_t) std::distance (analysis.getRawDataPointer(), scriptRun->data()); + const auto length = scriptRun->size(); + + const auto begin = bidiLevels.data() + offsetInText; + const auto end = begin + length; + + for (auto it = begin; it != end;) { - auto glyphs = lowLevelShape (data, - remainingCodepointsToConsume.withEnd (*breakBefore), - shapingParams.resolvedFont, - shapingParams.script, - shapingParams.language, - shapingParams.embeddingLevel); - - const auto beyondEnd = [&] + const auto next = std::find_if (it, end, [&] (const auto& l) { return l != *it; }); + const auto bidiStart = (int64) std::distance (bidiLevels.data(), it); + const auto bidiLength = (int64) std::distance (it, next); + const auto bidiRange = Range::withStartAndLength (bidiStart, bidiLength); + + for (const auto& [range, font] : fonts.getIntersectionsWith (bidiRange)) { - if (options.getTrailingWhitespacesShouldFit()) - return glyphs.cend(); + shaperRuns.set (range, + { scriptRun->front().script, + options.getLanguage(), + *it, + font }); + } - auto it = glyphs.cend(); + it = next; + } + } + } - while (--it != glyphs.begin()) - { - if (! it->whitespace) - return it; - } + IntegralCanBreakBeforeIterator softBreakIterator { makeSpan (analysis) }; - return it; - }(); + for (auto breakBefore = softBreakIterator.next(); + breakBefore.has_value(); + breakBefore = softBreakIterator.next()) + { + auto v = *breakBefore; - const auto advance = std::accumulate (glyphs.cbegin(), - beyondEnd, - float{}, - [] (auto acc, const auto& elem) { return acc + elem.advance.getX(); }); + if (softBreakBeforePoints.empty() || softBreakBeforePoints.back() != v) + softBreakBeforePoints.push_back (v); + } + } - if (advance < remainingWidth.value_or (floatMax) || ! bestMatch.has_value()) - bestMatch = BestMatch { *breakBefore, { advance, advance }, true, std::move (glyphs) }; - } - } + WrappedGlyphs getChunksUpToNextSafeBreak (int64 startFrom) + { + const auto nextSoftBreakBefore = std::invoke ([&] + { + const auto it = std::upper_bound (softBreakBeforePoints.begin(), + softBreakBeforePoints.end(), + startFrom); + + if (it == softBreakBeforePoints.end()) + return (int64) visualOrder.size(); - // Failed to break anywhere, we need to consume all that's left - if (! bestMatch.has_value()) + return *it; + }); + + if (! shapedGlyphs.getRanges().covers ({ startFrom, nextSoftBreakBefore })) + { + for (auto it = shaperRuns.find (startFrom); + it != shaperRuns.end() && it->range.getStart() < nextSoftBreakBefore; + ++it) { - bestMatch = BestMatch { glyphsToConsume.getCodepointRange().getEnd(), - *glyphsToConsume.getAdvanceXUpToBreakPointIfSafe (glyphsToConsume.getCodepointRange().getEnd(), - options.getTrailingWhitespacesShouldFit()), - false, - std::vector{} }; + const Range shapingRange { std::max (startFrom, it->range.getStart()), it->range.getEnd() }; + jassert (! shapingRange.isEmpty()); + + auto g = lowLevelShape (string, + shapingRange, + it->value.resolvedFont, + it->value.script, + it->value.language, + it->value.embeddingLevel); + + shapedGlyphs.set (shapingRange, + { + std::make_shared> (std::move (g)), + it->value.embeddingLevel % 2 == 0, + it->value.resolvedFont + }); } + } - jassert (bestMatch.has_value()); + auto glyphsIt = shapedGlyphs.find (startFrom); - const auto consumeGlyphs = [&]() -> ConsumedGlyphs - { - auto glyphs = [&] - { - if (bestMatch->unsafe) - return Span { bestMatch->unsafeGlyphs }; + if (glyphsIt == shapedGlyphs.end()) + return {}; - return glyphsToConsume.getGlyphs (bestMatch->breakBefore); - }(); + WrappedGlyphs result; - const auto textRange = glyphsToConsume.getCodepointRange().withEnd (bestMatch->breakBefore); + while (true) + { + const ShapedGlyph* start = glyphsIt->value.data->data(); + const ShapedGlyph* const endIt = glyphsIt->value.data->data() + glyphsIt->value.data->size(); - std::vector copiedGlyphs { glyphs.begin(), glyphs.end() }; + while (start < endIt && start->cluster < startFrom) + ++start; - glyphsToConsume.breakBeforeAndConsume (bestMatch->breakBefore); + const ShapedGlyph* end = start; - return { copiedGlyphs, textRange }; - }; + while (end < endIt && end->cluster < nextSoftBreakBefore) + ++end; - const auto addGlyphsToLine = [&] (const ConsumedGlyphs& toAdd, - CanAddGlyphsBeyondLineLimits evenIfFull) -> ConsumedGlyphs - { - const auto glyphsEnd = [&] - { - if (evenIfFull == CanAddGlyphsBeyondLineLimits::yes || ! remainingWidth.has_value()) - return toAdd.glyphs.end(); + result.push_back ({ glyphsIt->value, + Span { start, (size_t) std::distance (start, end) }, + { startFrom, nextSoftBreakBefore }, + visualOrder[(size_t) start->cluster] }); - auto it = toAdd.glyphs.begin(); + if (end != endIt && end->cluster >= nextSoftBreakBefore) + break; - for (float advance = 0.0f; it != toAdd.glyphs.end();) - { - const auto clusterEnd = std::find_if (it, - toAdd.glyphs.end(), - [cluster = it->cluster] (const auto& g) - { - return g.cluster != cluster; - }); - - advance = std::accumulate (it, - clusterEnd, - advance, - [] (auto acc, const auto& g) - { - return acc + g.advance.getX(); - }); - - // Consume at least one glyph in each line, even if the line is too short. - if (advance > *remainingWidth - && (numGlyphsInLine == 0 && it != toAdd.glyphs.begin())) - { - break; - } - - it = clusterEnd; - } + ++glyphsIt; - if (options.getTrailingWhitespacesShouldFit() || (numGlyphsInLine == 0 && it == toAdd.glyphs.begin())) - return it; + if (glyphsIt == shapedGlyphs.end()) + break; + } - return std::find_if (it, toAdd.glyphs.end(), [] (const auto& x) { return ! x.whitespace; }); - }(); + return result; + } - const auto numGlyphsAdded = (int64) std::distance (toAdd.glyphs.begin(), glyphsEnd); + String string; + std::vector visualOrder; + RangedValues shaperRuns; + std::vector softBreakBeforePoints; + RangedValues shapedGlyphs; +}; - const auto textRange = [&]() -> Range - { - if (glyphsEnd == toAdd.glyphs.end()) - return toAdd.textRange; +struct LineState +{ + LineState() = default; - return { toAdd.textRange.getStart(), glyphsEnd->cluster }; - }(); + LineState (float w, bool f) + : maxWidth { w }, + trailingWhitespaceCanExtendBeyondMargin { f } + {} - lineChunks.push_back ({ textRange, - { toAdd.glyphs.begin(), glyphsEnd }, - shapingParams.resolvedFont, - shapingParams.embeddingLevel }); + bool isInTrailingPosition (const ShapedGlyph& glyph) const + { + return glyph.cluster >= largestVisualOrderInLine; + } - numGlyphsInLine += numGlyphsAdded; + bool isEmpty() const + { + return largestVisualOrderInLine < 0; + } - if (remainingWidth.has_value()) - { - *remainingWidth -= std::accumulate (toAdd.glyphs.begin(), - glyphsEnd, - 0.0f, - [] (auto acc, auto& g) { return acc + g.advance.getX(); }); - } + int64 largestVisualOrderInLine = -1; + float maxWidth{}; + float width{}; + bool trailingWhitespaceCanExtendBeyondMargin; +}; - return { { glyphsEnd, toAdd.glyphs.end() }, toAdd.textRange.withStart (textRange.getEnd()) }; - }; +struct WrappedGlyphsCursorRange +{ + WrappedGlyphsCursor begin, end; +}; - if (bestMatch->advance.maybeIgnoringWhitespace >= remainingWidth.value_or (floatMax)) - { - // Even an empty line is too short to fit any of the text - if (numGlyphsInLine == 0 && exactlyEqual (remainingWidth, options.getMaxWidth())) - { - auto glyphsToAdd = consumeGlyphs(); +class LineOfWrappedGlyphCursorRanges +{ +public: + LineOfWrappedGlyphCursorRanges() = default; - while (! glyphsToAdd.glyphs.empty()) - { - const auto appendingToLastLine = ! appendingToBeforeLastLine(); + LineOfWrappedGlyphCursorRanges (float maxWidth, bool trailingWhitespaceCanExtendBeyondMargin) + : state { maxWidth, trailingWhitespaceCanExtendBeyondMargin } + {} - glyphsToAdd = addGlyphsToLine (glyphsToAdd, - (appendingToLastLine || ! options.getAllowBreakingInsideWord()) ? CanAddGlyphsBeyondLineLimits::yes - : CanAddGlyphsBeyondLineLimits::no); + /* Consumes as many glyphs from the provided cursor as the line will still fit. Returns the end + cursor i.e. the state of the cursor after the glyphs have been consumed. - if (! glyphsToAdd.glyphs.empty()) - commitLine (bidiParagraph); - } - } - else - { - commitLine (bidiParagraph); - } - } - else - { - [[maybe_unused]] const auto remainder = addGlyphsToLine (consumeGlyphs(), - CanAddGlyphsBeyondLineLimits::yes); - jassert (remainder.glyphs.empty()); + If the line is empty it will partially consume a WrappedGlyphsCursor, otherwise only all of it + or none of it. - if (! glyphsToConsume.isEmpty()) - commitLine (bidiParagraph); - } + Always consumes at least one glyph. If forceConsumeFirstWord is true, it consumes at least + one word. + */ + WrappedGlyphsCursor consume (const WrappedGlyphsCursor& glyphIt, bool forceConsumeFirstWord) + { + if (forceConsumeFirstWord && state.isEmpty()) + { + auto [newState, newIt] = consumeIf (state, glyphIt, [] (auto&, auto&) { return true; }); + consumedChunks.push_back ({ glyphIt, newIt }); + state = std::move (newState); + return newIt; } - }; - const auto fontsWithFallback = [&] + auto [newState, newIt] = consumeIf (state, glyphIt, [] (auto& nextState, auto& glyph) + { + const auto remainingWidth = nextState.maxWidth - nextState.width; + + return nextState.isEmpty() + || glyph.advance.getX() <= remainingWidth + || (nextState.trailingWhitespaceCanExtendBeyondMargin + && glyph.whitespace + && nextState.isInTrailingPosition (glyph)); + }); + + // A OwnedGlyphsSpan always ends in the first valid breakpoint. We can only consume all of it or + // none of it. Unless the line is still empty, which means that it's too short to fit even + // a single word. + if (! state.isEmpty() && ! newIt.isBeyondEnd()) + return glyphIt; + + if (newIt != glyphIt) + consumedChunks.push_back ({ glyphIt, newIt }); + + state = std::move (newState); + + return newIt; + } + + const auto& getConsumedChunks() const { - RangedValues resolved; + return consumedChunks; + } - for (const auto [r, f] : fonts) +private: + static std::pair consumeIf (const LineState& state, + const WrappedGlyphsCursor& it, + std::function predicate) + { + auto newState = state; + auto newIt = it; + + while (! newIt.isBeyondEnd() && predicate (newState, *newIt)) { - auto rf = findSuitableFontsForText (f, data.substring ((int) r.getStart(), - (int) std::min (r.getEnd(), (int64) data.length()))); + newState.width += newIt->advance.getX(); + newState.largestVisualOrderInLine = std::max (newState.largestVisualOrderInLine, newIt->cluster); + ++newIt; + } - for (auto& item : rf) - item.first += r.getStart(); + return { std::move (newState), std::move (newIt) }; + } - resolved.setForEach (rf.begin(), rf.end()); - } + LineState state; + std::vector consumedChunks; +}; - return resolved; - }(); +struct LineDataAndChunkStorage +{ + std::vector chunkStorage; + std::vector> lines; +}; - bidiAlgorithm.forEachParagraph ([&] (const BidiParagraph& bidiParagraph) +struct FillLinesOptions +{ + FillLinesOptions withWidth (float x) const { - const auto bidiLevels = bidiParagraph.getResolvedLevels(); - const Span paragraphSpan { analysis.getRawDataPointer() + bidiParagraph.getOffset(), bidiParagraph.getLength() }; + return withMember (*this, &FillLinesOptions::width, x); + } - for (Unicode::LineBreakIterator lineIter { paragraphSpan }; auto lineRun = lineIter.next();) - { - for (Unicode::ScriptRunIterator scriptIter { *lineRun }; auto scriptRun = scriptIter.next();) - { - const auto offsetInText = (size_t) std::distance (analysis.getRawDataPointer(), scriptRun->data()); - const auto offsetInParagraph = offsetInText - bidiParagraph.getOffset(); - const auto length = scriptRun->size(); + FillLinesOptions withFirstLinePadding (float x) const + { + return withMember (*this, &FillLinesOptions::firstLinePadding, x); + } - const auto begin = bidiLevels.data() + offsetInParagraph; - const auto end = begin + length; + FillLinesOptions withTrailingWhitespaceCanExtendBeyondMargin (bool x = true) const + { + return withMember (*this, &FillLinesOptions::trailingWhitespaceCanExtendBeyondMargin, x); + } - for (auto it = begin; it != end;) - { - const auto next = std::find_if (it, end, [&] (const auto& l) { return l != *it; }); - const auto bidiRunOffset = std::distance (begin, it); - const auto bidiRunLength = std::distance (it, next); - const Span bidiRun { analysis.getRawDataPointer() + bidiRunOffset + offsetInText, (size_t) bidiRunLength }; + FillLinesOptions withForceConsumeFirstWord (bool x = true) const + { + return withMember (*this, &FillLinesOptions::forceConsumeFirstWord, x); + } - for (const auto& [range, font] : fontsWithFallback.getIntersectionsWith (spanLookup.getRange (bidiRun))) - { - append (bidiParagraph, - range, - { scriptRun->front().script, - options.getLanguage(), - *it, - font }); - } + LineDataAndChunkStorage fillLines (Shaper& shaper) const + { + LineDataAndChunkStorage result; + LineOfWrappedGlyphCursorRanges line { width - firstLinePadding, trailingWhitespaceCanExtendBeyondMargin }; - it = next; + for (auto chunks = shaper.getChunksUpToNextSafeBreak (0); + ! chunks.empty();) + { + result.chunkStorage.push_back (std::move (chunks)); + WrappedGlyphsCursor cursor { result.chunkStorage.back().data(), + result.chunkStorage.back().size() }; + + while (! cursor.isBeyondEnd()) + { + cursor = line.consume (cursor, forceConsumeFirstWord); + + if (! cursor.isBeyondEnd()) + { + result.lines.push_back (line.getConsumedChunks()); + line = LineOfWrappedGlyphCursorRanges { width, trailingWhitespaceCanExtendBeyondMargin }; } } - if (! lineChunks.empty()) - commitLine (bidiParagraph); + chunks = shaper.getChunksUpToNextSafeBreak (cursor.getTextRange().getEnd()); } - if (! lineChunks.empty()) - commitLine (bidiParagraph); + result.lines.push_back (line.getConsumedChunks()); + + return result; + } + + float width{}; + float firstLinePadding{}; + bool trailingWhitespaceCanExtendBeyondMargin = false; + bool forceConsumeFirstWord = false; +}; + +static auto getShapedGlyphSpansInVisualOrder (const std::vector& lineData) +{ + std::vector glyphSpans; - }, options.getReadingDirection()); + for (const auto& chunk : lineData) + { + auto spans = chunk.begin.getShapedGlyphSpansUpTo (chunk.end); + glyphSpans.insert (glyphSpans.begin(), spans.begin(), spans.end()); + } + + std::sort (glyphSpans.begin(), + glyphSpans.end(), + [] (const auto& a, const auto& b) + { + return a.visualOrder < b.visualOrder; + }); + + return glyphSpans; } -Range SimpleShapedText::getGlyphRangeForLine (size_t line) const +static auto getLineRanges (const String& data) { - jassert (line <= lineNumbers.size()); - return lineNumbers.getItem (line).range; + std::vector> lineRanges; + + const auto analysis = Unicode::performAnalysis (data); + const auto spanLookup = makeSubSpanLookup (makeSpan (analysis)); + + for (Unicode::LineBreakIterator lineIter { makeSpan (analysis) }; auto lineRun = lineIter.next();) + lineRanges.push_back (spanLookup.getRange (*lineRun)); + + return lineRanges; } -std::vector SimpleShapedText::getResolvedFontsIntersectingGlyphRange (Range glyphRange) const +static void foldLinesBeyondLineLimit (std::vector>& lines, + size_t maxNumLines) { - std::vector result; + if (lines.size() <= maxNumLines || maxNumLines == 0) + return; - for (const auto& item : resolvedFonts.getIntersectionsWith (glyphRange)) - result.push_back ({ item.range, item.value }); + auto& lastLine = lines[maxNumLines - 1]; - return result; + for (auto i = maxNumLines; i < lines.size(); ++i) + lastLine.insert (lastLine.end(), lines[i].begin(), lines[i].end()); + + lines.erase (iteratorWithAdvance (lines.begin(), maxNumLines), lines.end()); +} + +void SimpleShapedText::shape (const String& data, + const ShapedTextOptions& options) +{ + for (const auto& lineRange : getLineRanges (data)) + { + Shaper shaper { data, lineRange, options }; + auto lineDataAndStorage = FillLinesOptions{}.withWidth (options.getMaxWidth().value_or ((float) 1e6)) + .withFirstLinePadding (options.getFirstLineIndent()) + .withTrailingWhitespaceCanExtendBeyondMargin (! options.getTrailingWhitespacesShouldFit()) + .withForceConsumeFirstWord (! options.getAllowBreakingInsideWord()) + .fillLines (shaper); + auto& lineData = lineDataAndStorage.lines; + + foldLinesBeyondLineLimit (lineData, (size_t) options.getMaxNumLines() - lineNumbers.size()); + + if (lineNumbers.size() >= (size_t) options.getMaxNumLines()) + break; + + for (const auto& line : lineData) + { + const auto glyphSpansInLine = getShapedGlyphSpansInVisualOrder (line); + + const auto lineStart = (int64) glyphsInVisualOrder.size(); + + for (const auto& s : glyphSpansInLine) + { + const auto start = (int64) glyphsInVisualOrder.size(); + bool ltr = true; + + if (s.start < s.end) + { + for (auto it = s.start; it < s.end; ++it) + glyphsInVisualOrder.push_back (*it); + } + else + { + ltr = false; + + for (auto it = s.start; it > s.end; --it) + glyphsInVisualOrder.push_back (*it); + } + + const auto end = (int64) glyphsInVisualOrder.size(); + + glyphLookup.set (s.textRange, { { start, end }, ltr }); + resolvedFonts.set ({ start, end }, s.font); + } + + const auto lineEnd = (int64) glyphsInVisualOrder.size(); + lineNumbers.set ({ lineStart, lineEnd}, (int64) lineNumbers.size()); + } + } } Span SimpleShapedText::getGlyphs (Range glyphRange) const diff --git a/modules/juce_graphics/images/juce_Image.cpp b/modules/juce_graphics/images/juce_Image.cpp index 112bcce29e32..277ebd6bf0ed 100644 --- a/modules/juce_graphics/images/juce_Image.cpp +++ b/modules/juce_graphics/images/juce_Image.cpp @@ -841,13 +841,11 @@ void ImageEffects::applySingleChannelBoxBlurEffect (int radius, const Image& inp //============================================================================== #if JUCE_ALLOW_STATIC_NULL_VARIABLES -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const Image Image::null; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif diff --git a/modules/juce_graphics/juce_graphics_Harfbuzz.cpp b/modules/juce_graphics/juce_graphics_Harfbuzz.cpp index bc464f9fb83e..2653786c7e4e 100644 --- a/modules/juce_graphics/juce_graphics_Harfbuzz.cpp +++ b/modules/juce_graphics/juce_graphics_Harfbuzz.cpp @@ -34,10 +34,9 @@ #include -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100 4127 4189 4244 4245 4265 4267 4309 4310 4312 4456 4457 4458 4459 4701 4702 4706 4996 6001 6011 6239 6244 6246 6262 6297 6313 6319 6326 6336 6385 6386 28251) +JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4100 4127 4189 4244 4245 4265 4267 4309 4310 4312 4456 4457 4458 4459 4701 4702 4706 6001 6011 6239 6244 6246 6262 6297 6313 6319 6326 6336 6385 6386 28251) -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations", - "-Wcast-function-type", +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type", "-Wsign-conversion", "-Wzero-as-null-pointer-constant", "-Wformat-pedantic", @@ -56,6 +55,8 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations", "-Woverflow", "-Wimplicit-fallthrough") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS + #define HAVE_ATEXIT 1 #if JUCE_LINUX || JUCE_BSD @@ -95,5 +96,6 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations", #undef HAVE_FREETYPE #undef HAVE_CORETEXT +JUCE_END_IGNORE_DEPRECATION_WARNINGS JUCE_END_IGNORE_WARNINGS_GCC_LIKE JUCE_END_IGNORE_WARNINGS_MSVC diff --git a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm index 1caaf7e7b28e..aa18f90ac156 100644 --- a/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm +++ b/modules/juce_graphics/native/juce_CoreGraphicsContext_mac.mm @@ -867,6 +867,10 @@ void setFill (const FillType& newFill) Path p; auto& f = state->font; f.getTypefacePtr()->getOutlineForGlyph (f.getMetricsKind(), glyph, p); + + if (p.isEmpty()) + continue; + const auto scale = f.getHeight(); fillPath (p, AffineTransform::scale (scale * f.getHorizontalScale(), scale).translated (positions[index]).followedBy (transform)); } diff --git a/modules/juce_graphics/native/juce_DirectX_windows.h b/modules/juce_graphics/native/juce_DirectX_windows.h index 2e911b9c59e6..516d20248ee9 100644 --- a/modules/juce_graphics/native/juce_DirectX_windows.h +++ b/modules/juce_graphics/native/juce_DirectX_windows.h @@ -35,6 +35,8 @@ namespace juce { +constexpr auto enableDirectXDebugLayer = false; + struct DxgiAdapter : public ReferenceCountedObject { using Ptr = ReferenceCountedObjectPtr; @@ -60,7 +62,8 @@ struct DxgiAdapter : public ReferenceCountedObject // This flag adds support for surfaces with a different color channel ordering // than the API default. It is required for compatibility with Direct2D. - const auto creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + const auto creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT + | (enableDirectXDebugLayer ? D3D11_CREATE_DEVICE_DEBUG : 0); if (const auto hr = D3D11CreateDevice (result->dxgiAdapter, D3D_DRIVER_TYPE_UNKNOWN, @@ -266,7 +269,7 @@ class DirectX ComSmartPtr d2dSharedFactory = [&] { D2D1_FACTORY_OPTIONS options; - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; + options.debugLevel = enableDirectXDebugLayer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE; JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") ComSmartPtr result; auto hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_MULTI_THREADED, diff --git a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp index a6ddb49633e9..3e6b1af91b6a 100644 --- a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp +++ b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp @@ -35,8 +35,6 @@ namespace juce { -AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr; - class NativeChildHandler { public: @@ -403,10 +401,24 @@ void AccessibilityHandler::setNativeChildForComponent (Component& component, voi NativeChildHandler::getInstance().setNativeChild (component, nativeChild); } +#if JUCE_MODULE_AVAILABLE_juce_gui_extra +void privatePostSystemNotification (const String&, const String&); +#endif + +void AccessibilityHandler::postSystemNotification ([[maybe_unused]] const String& notificationTitle, + [[maybe_unused]] const String& notificationBody) +{ + #if JUCE_MODULE_AVAILABLE_juce_gui_extra + if (areAnyAccessibilityClientsActive()) + privatePostSystemNotification (notificationTitle, notificationBody); + #endif +} + #if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {} void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {} AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; } + bool AccessibilityHandler::areAnyAccessibilityClientsActive() { return false; } #endif } // namespace juce diff --git a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h index da8b3f3af4f1..540799839b31 100644 --- a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h +++ b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.h @@ -295,6 +295,30 @@ class JUCE_API AccessibilityHandler */ static void postAnnouncement (const String& announcementString, AnnouncementPriority priority); + /** Posts a local system notification. + + In order for this to do anything, the following conditions must be met. + - At build time: + - The juce_gui_extra module must be included in the project. + - Push notifications must be enabled by setting the preprocessor definition + JUCE_PUSH_NOTIFICATIONS=1 + - At run time: + - An accessibility client (narrator, voiceover etc.) must be active. + + Additionally, on Android, an icon is required for notifications. + This must be specified by adding the path to the icon file called + "accessibilitynotificationicon" in the "Extra Android Raw Resources" setting + in the Projucer. + + This will use the push notification client on macOS, iOS and Android. + On Windows this will create a system tray icon to post the notification. + + @param notificationTitle the title of the notification + @param notificationBody the main body text of the notification + */ + static void postSystemNotification (const String& notificationTitle, + const String& notificationBody); + //============================================================================== /** @internal */ AccessibilityNativeHandle* getNativeImplementation() const; @@ -329,8 +353,9 @@ class JUCE_API AccessibilityHandler void grabFocusInternal (bool); void giveAwayFocusInternal() const; void takeFocus(); + static bool areAnyAccessibilityClientsActive(); - static AccessibilityHandler* currentlyFocusedHandler; + static inline AccessibilityHandler* currentlyFocusedHandler = nullptr; //============================================================================== Component& component; diff --git a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 04175c6e5c6e..56ce6b2bbd3f 100644 --- a/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -1299,7 +1299,7 @@ class SVGState } else { - auto linkedFile = originalFile.getParentDirectory().getChildFile (link); + auto linkedFile = originalFile.getSiblingFile (link); if (linkedFile.existsAsFile()) inputStream = linkedFile.createInputStream(); diff --git a/modules/juce_gui_basics/juce_gui_basics.h b/modules/juce_gui_basics/juce_gui_basics.h index d15687204fd1..387927ed0a94 100644 --- a/modules/juce_gui_basics/juce_gui_basics.h +++ b/modules/juce_gui_basics/juce_gui_basics.h @@ -55,7 +55,7 @@ OSXFrameworks: Cocoa QuartzCore WeakOSXFrameworks: Metal MetalKit iOSFrameworks: CoreServices UIKit - WeakiOSFrameworks: Metal MetalKit UniformTypeIdentifiers + WeakiOSFrameworks: Metal MetalKit UniformTypeIdentifiers UserNotifications END_JUCE_MODULE_DECLARATION diff --git a/modules/juce_gui_basics/layout/juce_Grid.cpp b/modules/juce_gui_basics/layout/juce_Grid.cpp index 2268cb11b634..5d78080d7eae 100644 --- a/modules/juce_gui_basics/layout/juce_Grid.cpp +++ b/modules/juce_gui_basics/layout/juce_Grid.cpp @@ -633,6 +633,8 @@ struct Grid::Helpers //============================================================================== struct AutoPlacement { + AutoPlacement() = delete; + using ItemPlacementArray = Array>; //============================================================================== @@ -649,7 +651,7 @@ struct Grid::Helpers { for (int i = 0; i < columnSpan; i++) for (int j = 0; j < rowSpan; j++) - setCell (cell.column + i, cell.row + j); + occupiedCells.insert ({ cell.column + i, cell.row + j }); return { { cell.column, cell.column + columnSpan }, { cell.row, cell.row + rowSpan } }; } @@ -698,36 +700,26 @@ struct Grid::Helpers } private: - struct SortableCell + struct Comparator { - int column, row; - bool columnFirst; + using Tie = std::tuple (*) (const Cell&); - bool operator< (const SortableCell& other) const - { - if (columnFirst) - { - if (row == other.row) - return column < other.column; - - return row < other.row; - } + explicit Comparator (bool columnFirstIn) + : tie (columnFirstIn ? Tie { [] (const Cell& x) { return std::tuple (x.row, x.column); } } + : Tie { [] (const Cell& x) { return std::tuple (x.column, x.row); } }) + {} - if (row == other.row) - return column < other.column; - - return row < other.row; + bool operator() (const Cell& a, const Cell& b) const + { + return tie (a) < tie (b); } - }; - void setCell (int column, int row) - { - occupiedCells.insert ({ column, row, columnFirst }); - } + const Tie tie; + }; bool isOccupied (Cell cell) const { - return occupiedCells.count ({ cell.column, cell.row, columnFirst }) > 0; + return occupiedCells.count (cell) > 0; } bool isOccupied (Cell cell, int columnSpan, int rowSpan) const @@ -750,20 +742,20 @@ struct Grid::Helpers int getHighestCrossDimension() const { - Cell cell { 1, 1 }; - - if (occupiedCells.size() > 0) - cell = { occupiedCells.crbegin()->column, occupiedCells.crbegin()->row }; + const auto cell = occupiedCells.empty() ? Cell { 1, 1 } + : *std::prev (occupiedCells.end()); - return std::max (getCrossDimension (cell), highestCrossDimension); + return std::max (getCrossDimension (cell) + 1, highestCrossDimension); } Cell advance (Cell cell) const { - if ((getCrossDimension (cell) + 1) >= getHighestCrossDimension()) + const auto next = getCrossDimension (cell) + 1; + + if (next >= getHighestCrossDimension()) return fromDimensions (getMainDimension (cell) + 1, 1); - return fromDimensions (getMainDimension (cell), getCrossDimension (cell) + 1); + return fromDimensions (getMainDimension (cell), next); } int getMainDimension (Cell cell) const { return columnFirst ? cell.column : cell.row; } @@ -778,8 +770,11 @@ struct Grid::Helpers } int highestCrossDimension; - bool columnFirst; - std::set occupiedCells; + const bool columnFirst; + std::set occupiedCells { Comparator { columnFirst } }; + + JUCE_DECLARE_NON_COPYABLE (OccupancyPlane) + JUCE_DECLARE_NON_MOVEABLE (OccupancyPlane) }; //============================================================================== @@ -841,7 +836,7 @@ struct Grid::Helpers } //============================================================================== - ItemPlacementArray deduceAllItems (Grid& grid) const + static ItemPlacementArray deduceAllItems (Grid& grid) { const auto namedAreas = PlacementHelpers::deduceNamedAreas (grid.templateAreas); @@ -1112,7 +1107,7 @@ float Grid::TrackInfo::getAbsoluteSize (float relativeFractionalUnit) const //============================================================================== void Grid::performLayout (Rectangle targetArea) { - const auto itemsAndAreas = Helpers::AutoPlacement().deduceAllItems (*this); + const auto itemsAndAreas = Helpers::AutoPlacement::deduceAllItems (*this); auto implicitTracks = Helpers::AutoPlacement::createImplicitTracks (*this, itemsAndAreas); @@ -1641,6 +1636,85 @@ struct GridTests final : public UnitTest evaluateInvariants (randomSolution); } } + + { + beginTest ("Cell orderings for row and columns work correctly"); + + const Rectangle bounds { 0, 0, 200, 200 }; + + Grid grid; + grid.autoColumns = Grid::TrackInfo { Grid::Fr { 1 } }; + grid.autoRows = Grid::TrackInfo { Grid::Fr { 1 } }; + + grid.items = { GridItem{}.withArea (1, 20), + GridItem{}.withArea (2, 10), + GridItem{}.withArea (GridItem::Span { 1 }, GridItem::Span { 15 }), + GridItem{} }; + + grid.autoFlow = Grid::AutoFlow::row; + grid.performLayout (bounds); + expect (grid.items.getLast().currentBounds == Rect { 150, 0, 10, 100 }); + + grid.autoFlow = Grid::AutoFlow::column; + grid.performLayout (bounds); + expect (grid.items.getLast().currentBounds == Rect { 0, 100, 10, 100 }); + + grid.items = { GridItem{}.withArea (20, 1), + GridItem{}.withArea (10, 2), + GridItem{}.withArea (GridItem::Span { 15 }, GridItem::Span { 1 }), + GridItem{} }; + + grid.autoFlow = Grid::AutoFlow::row; + grid.performLayout (bounds); + expect (grid.items.getLast().currentBounds == Rect { 100, 0, 100, 10 }); + + grid.autoFlow = Grid::AutoFlow::column; + grid.performLayout (bounds); + expect (grid.items.getLast().currentBounds == Rect { 0, 150, 100, 10 }); + } + + beginTest ("Complex grid layout"); + { + Grid grid; + + using Track = Grid::TrackInfo; + + grid.templateRows = { Track (1_fr), Track (1_fr), Track (1_fr) }; + grid.templateColumns = { Track (1_fr), Track (1_fr), Track (1_fr) }; + + grid.autoColumns = Track (1_fr); + grid.autoRows = Track (1_fr); + + grid.autoFlow = Grid::AutoFlow::column; + + grid.items.addArray ({ GridItem().withArea (2, 2, 4, 4), + GridItem(), + GridItem().withArea ({}, 3), + GridItem(), + GridItem().withArea (GridItem::Span (2), {}), + GridItem(), + GridItem(), + GridItem(), + GridItem(), + GridItem(), + GridItem(), + GridItem() }); + + grid.performLayout ({ 60, 30 }); + + expect (grid.items[0] .currentBounds == Rect { 10, 10, 20, 20 }); + expect (grid.items[1] .currentBounds == Rect { 0, 0, 10, 10 }); + expect (grid.items[2] .currentBounds == Rect { 20, 0, 10, 10 }); + expect (grid.items[3] .currentBounds == Rect { 0, 10, 10, 10 }); + expect (grid.items[4] .currentBounds == Rect { 30, 0, 10, 20 }); + expect (grid.items[5] .currentBounds == Rect { 30, 20, 10, 10 }); + expect (grid.items[6] .currentBounds == Rect { 40, 0, 10, 10 }); + expect (grid.items[7] .currentBounds == Rect { 40, 10, 10, 10 }); + expect (grid.items[8] .currentBounds == Rect { 40, 20, 10, 10 }); + expect (grid.items[9] .currentBounds == Rect { 50, 0, 10, 10 }); + expect (grid.items[10].currentBounds == Rect { 50, 10, 10, 10 }); + expect (grid.items[11].currentBounds == Rect { 50, 20, 10, 10 }); + } } }; diff --git a/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp b/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp index 88fe008996e7..3ec2a0b18dd3 100644 --- a/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp +++ b/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp @@ -324,12 +324,35 @@ class DragAndDropContainer::DragImageComponent final : public Component, void setNewScreenPos (Point screenPos) { - auto newPos = screenPos - imageOffset; + setTopLeftPosition (std::invoke ([&] + { + if (auto* p = getParentComponent()) + return p->getLocalPoint (nullptr, screenPos - imageOffset); + + #if JUCE_WINDOWS + // On Windows, the mouse position is continuous in physical pixels across screen boundaries. + // i.e. if two screens are set to different scale factors, when the mouse moves horizontally + // between those screens, the mouse's physical y coordinate will be preserved, and if + // the mouse moves vertically between screens its physical x coordinate will be preserved. + + // To avoid the dragged image detaching from the mouse, compute the new top left position + // in physical coords and then convert back to logical. + // If we were to stay in logical coordinates the whole time, the image may detach from the + // mouse because the mouse does not move continuously in logical coordinate space. + + const auto& displays = Desktop::getInstance().getDisplays(); + const auto physicalPos = displays.logicalToPhysical (screenPos); + + float scale = 1.0f; - if (auto* p = getParentComponent()) - newPos = p->getLocalPoint (nullptr, newPos); + if (auto* p = getPeer()) + scale = (float) p->getPlatformScaleFactor(); - setTopLeftPosition (newPos); + return displays.physicalToLogical (physicalPos - (imageOffset * scale)); + #else + return screenPos - imageOffset; + #endif + })); } void sendDragMove (DragAndDropTarget::SourceDetails& details) const diff --git a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_android.cpp b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_android.cpp index 4ab7bd9db89d..418eba3108d6 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_android.cpp +++ b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_android.cpp @@ -1055,4 +1055,9 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString, javaString (announcementString).get()); } +bool AccessibilityHandler::areAnyAccessibilityClientsActive() +{ + return AccessibilityNativeHandle::areAnyAccessibilityClientsActive(); +} + } // namespace juce diff --git a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_ios.mm b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_ios.mm index 21538ae77732..5c4643b49968 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_ios.mm +++ b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_ios.mm @@ -666,4 +666,9 @@ static void sendAccessibilityEvent (UIAccessibilityNotifications notification, i sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString)); } +bool AccessibilityHandler::areAnyAccessibilityClientsActive() +{ + return juce::areAnyAccessibilityClientsActive(); +} + } // namespace juce diff --git a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_mac.mm b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_mac.mm index 48647846ad87..a6001b00e708 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_mac.mm +++ b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_mac.mm @@ -939,4 +939,9 @@ static void sendHandlerNotification (const AccessibilityHandler& handler, NSAccessibilityPriorityKey: @(nsPriority) }); } +bool AccessibilityHandler::areAnyAccessibilityClientsActive() +{ + return juce::areAnyAccessibilityClientsActive(); +} + } // namespace juce diff --git a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_windows.cpp b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_windows.cpp index 0c98af66dd96..9fa451fb4e5a 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_Accessibility_windows.cpp +++ b/modules/juce_gui_basics/native/accessibility/juce_Accessibility_windows.cpp @@ -39,26 +39,83 @@ namespace juce JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token") -static bool isStartingUpOrShuttingDown() +//============================================================================== +struct WindowsAccessibility { - if (auto* app = JUCEApplicationBase::getInstance()) - if (app->isInitialising()) - return true; + WindowsAccessibility() = delete; + + static long getUiaRootObjectId() + { + return static_cast (UiaRootObjectId); + } + + static bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res) + { + if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler))) + return false; + + if (auto* uiaWrapper = WindowsUIAWrapper::getInstance()) + { + ComSmartPtr provider; + handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); + + if (! uiaWrapper->isProviderDisconnecting (provider)) + *res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider); - if (auto* mm = MessageManager::getInstanceWithoutCreating()) - if (mm->hasStopMessageBeenSent()) return true; + } - return false; -} + return false; + } -static bool isHandlerValid (const AccessibilityHandler& handler) -{ - if (auto* provider = handler.getNativeImplementation()) - return provider->isElementValid(); + static void revokeUIAMapEntriesForWindow (HWND hwnd) + { + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr); + } - return false; -} + static bool isStartingUpOrShuttingDown() + { + if (auto* app = JUCEApplicationBase::getInstance()) + if (app->isInitialising()) + return true; + + if (auto* mm = MessageManager::getInstanceWithoutCreating()) + if (mm->hasStopMessageBeenSent()) + return true; + + return false; + } + + static bool isHandlerValid (const AccessibilityHandler& handler) + { + if (auto* provider = handler.getNativeImplementation()) + return provider->isElementValid(); + + return false; + } + + static bool areAnyAccessibilityClientsActive() + { + const auto areClientsListening = [] + { + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + return uiaWrapper->clientsAreListening() != 0; + + return false; + }; + + const auto isScreenReaderRunning = [] + { + BOOL isRunning = FALSE; + SystemParametersInfo (SPI_GETSCREENREADER, 0, (PVOID) &isRunning, 0); + + return isRunning != 0; + }; + + return areClientsListening() || isScreenReaderRunning(); + } +}; //============================================================================== class AccessibilityHandler::AccessibilityNativeImpl @@ -103,31 +160,12 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const return nativeImpl->accessibilityElement; } -static bool areAnyAccessibilityClientsActive() -{ - const auto areClientsListening = [] - { - if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) - return uiaWrapper->clientsAreListening() != 0; - - return false; - }; - - const auto isScreenReaderRunning = [] - { - BOOL isRunning = FALSE; - SystemParametersInfo (SPI_GETSCREENREADER, 0, (PVOID) &isRunning, 0); - - return isRunning != 0; - }; - - return areClientsListening() || isScreenReaderRunning(); -} - template void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback) { - if (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler)) + if (! WindowsAccessibility::areAnyAccessibilityClientsActive() + || WindowsAccessibility::isStartingUpOrShuttingDown() + || ! WindowsAccessibility::isHandlerValid (handler)) return; if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) @@ -292,41 +330,11 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString, A } } -//============================================================================== -namespace WindowsAccessibility +bool AccessibilityHandler::areAnyAccessibilityClientsActive() { - static long getUiaRootObjectId() - { - return static_cast (UiaRootObjectId); - } - - static bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res) - { - if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler))) - return false; - - if (auto* uiaWrapper = WindowsUIAWrapper::getInstance()) - { - ComSmartPtr provider; - handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); - - if (! uiaWrapper->isProviderDisconnecting (provider)) - *res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider); - - return true; - } - - return false; - } - - static void revokeUIAMapEntriesForWindow (HWND hwnd) - { - if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) - uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr); - } + return WindowsAccessibility::areAnyAccessibilityClientsActive(); } - JUCE_END_IGNORE_WARNINGS_GCC_LIKE } // namespace juce diff --git a/modules/juce_gui_basics/native/juce_FileChooser_mac.mm b/modules/juce_gui_basics/native/juce_FileChooser_mac.mm index 4987e5f2bbe2..24c84c812920 100644 --- a/modules/juce_gui_basics/native/juce_FileChooser_mac.mm +++ b/modules/juce_gui_basics/native/juce_FileChooser_mac.mm @@ -103,9 +103,9 @@ [panel setTitle: nsTitle]; [panel setReleasedWhenClosed: YES]; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS [panel setAllowedFileTypes: createAllowedTypesArray (filters)]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS if (! isSave) { diff --git a/modules/juce_gui_basics/native/juce_NSViewComponentPeer_mac.mm b/modules/juce_gui_basics/native/juce_NSViewComponentPeer_mac.mm index 86ea738548bf..58c33abafabc 100644 --- a/modules/juce_gui_basics/native/juce_NSViewComponentPeer_mac.mm +++ b/modules/juce_gui_basics/native/juce_NSViewComponentPeer_mac.mm @@ -129,14 +129,16 @@ static constexpr int translateVirtualToAsciiKeyCode (int keyCode) noexcept constexpr int extendedKeyModifier = 0x30000; //============================================================================== -class JuceCALayerDelegate final : public ObjCClass> +struct JuceCALayerDelegateCallback +{ + virtual ~JuceCALayerDelegateCallback() = default; + virtual void displayLayer (CALayer*) = 0; +}; + +class API_AVAILABLE (macos (10.12)) JuceCALayerDelegate final : public ObjCClass> { public: - struct Callback - { - virtual ~Callback() = default; - virtual void displayLayer (CALayer*) = 0; - }; + using Callback = JuceCALayerDelegateCallback; static NSObject* construct (Callback* owner) { @@ -176,7 +178,7 @@ static void setOwner (id self, Callback* newOwner) //============================================================================== class NSViewComponentPeer final : public ComponentPeer, - private JuceCALayerDelegate::Callback + private JuceCALayerDelegateCallback { public: NSViewComponentPeer (Component& comp, const int windowStyleFlags, NSView* viewToAttachTo) @@ -1460,9 +1462,9 @@ static unsigned int getNSWindowStyleMask (const int flags) noexcept if (@available (macOS 10.13, *)) return NSPasteboardTypeFileURL; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return (NSString*) kUTTypeFileURL; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS }(); return [NSArray arrayWithObjects: type, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil]; @@ -1633,7 +1635,7 @@ bool sendEventToInputContextOrComponent (NSEvent* ev) const auto handled = [&]() -> bool { - if (auto* target = findCurrentTextInputTarget()) + if (findCurrentTextInputTarget() != nullptr) if (const auto* inputContext = [view inputContext]) return [inputContext handleEvent: ev] && ! viewCannotHandleEvent; diff --git a/modules/juce_gui_basics/native/juce_PerScreenDisplayLinks_mac.h b/modules/juce_gui_basics/native/juce_PerScreenDisplayLinks_mac.h index 4e412beb1393..dcea884cf935 100644 --- a/modules/juce_gui_basics/native/juce_PerScreenDisplayLinks_mac.h +++ b/modules/juce_gui_basics/native/juce_PerScreenDisplayLinks_mac.h @@ -109,7 +109,7 @@ class FunctionNotificationCenterObserver // NSScreen.displayLink(target:selector:) all of which were only introduced in macOS 14+ however, // it's not clear how these methods can be used to replace all use cases -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS class ScopedDisplayLink { @@ -188,7 +188,7 @@ class ScopedDisplayLink JUCE_DECLARE_NON_MOVEABLE (ScopedDisplayLink) }; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS //============================================================================== /* diff --git a/modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm b/modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm index e2d229f2a28f..c3f6341ba6d6 100644 --- a/modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm +++ b/modules/juce_gui_basics/native/juce_UIViewComponentPeer_ios.mm @@ -115,9 +115,9 @@ static UIInterfaceOrientation getWindowOrientation() return [(UIWindowScene*) scene interfaceOrientation]; } - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return [sharedApplication statusBarOrientation]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS } struct Orientations @@ -1107,7 +1107,7 @@ - (instancetype) initWithOwner: (UIViewComponentPeer*) ownerIn - (BOOL) canPerformAction: (SEL) action withSender: (id) sender { - if (auto* target = [self getTextInputTarget]) + if ([self getTextInputTarget] != nullptr) { if (action == @selector (paste:)) return [[UIPasteboard generalPasteboard] hasStrings]; @@ -1266,7 +1266,7 @@ - (void) setSelectedTextRange: (JuceUITextRange*) range - (UITextRange*) markedTextRange { if (owner != nullptr && owner->stringBeingComposed.isNotEmpty()) - if (auto* target = owner->findCurrentTextInputTarget()) + if (owner->findCurrentTextInputTarget() != nullptr) return [JuceUITextRange withRange: owner->getMarkedTextRange()]; return nil; diff --git a/modules/juce_gui_basics/native/juce_Windowing_mac.mm b/modules/juce_gui_basics/native/juce_Windowing_mac.mm index 6b18e9de8239..9839e5dd8fe2 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_mac.mm +++ b/modules/juce_gui_basics/native/juce_Windowing_mac.mm @@ -603,12 +603,12 @@ static Image createNSWindowSnapshot (NSWindow* nsWindow) #else - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return createImageFromCGImage ((CGImageRef) CFAutorelease (CGWindowListCreateImage (CGRectNull, kCGWindowListOptionIncludingWindow, (CGWindowID) [nsWindow windowNumber], kCGWindowImageBoundsIgnoreFraming))); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif } diff --git a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp index bf82c01d32c4..1e19b9df8c77 100644 --- a/modules/juce_gui_basics/native/juce_Windowing_windows.cpp +++ b/modules/juce_gui_basics/native/juce_Windowing_windows.cpp @@ -1530,7 +1530,7 @@ class HWNDComponentPeer final : public ComponentPeer return BorderSize { 0, 0, 0, 0 }; } - BorderSize findPhysicalBorderSize() const + std::optional> findPhysicalBorderSize() const { if (const auto custom = getCustomBorderSize()) return *custom; @@ -1543,10 +1543,15 @@ class HWNDComponentPeer final : public ComponentPeer if (! GetWindowInfo (hwnd, &info)) return {}; - return { info.rcClient.top - info.rcWindow.top, - info.rcClient.left - info.rcWindow.left, - info.rcWindow.bottom - info.rcClient.bottom, - info.rcWindow.right - info.rcClient.right }; + // Sometimes GetWindowInfo returns bogus information when called in the middle of restoring + // the window + if (info.rcWindow.left <= -32000 && info.rcWindow.top <= -32000) + return {}; + + return BorderSize { info.rcClient.top - info.rcWindow.top, + info.rcClient.left - info.rcWindow.left, + info.rcWindow.bottom - info.rcClient.bottom, + info.rcWindow.right - info.rcClient.right }; } void setBounds (const Rectangle& bounds, bool isNowFullScreen) override @@ -1563,7 +1568,7 @@ class HWNDComponentPeer final : public ComponentPeer const ScopedValueSetter scope (shouldIgnoreModalDismiss, true); - const auto borderSize = findPhysicalBorderSize(); + const auto borderSize = findPhysicalBorderSize().value_or (BorderSize{}); auto newBounds = borderSize.addedTo ([&] { ScopedThreadDPIAwarenessSetter setter { hwnd }; @@ -1630,7 +1635,7 @@ class HWNDComponentPeer final : public ComponentPeer // This means that we may end up clipping off up to one logical pixel under the physical // window border, but this is preferable to displaying an uninitialised/unpainted // region of the client area. - const auto physicalBorder = findPhysicalBorderSize(); + const auto physicalBorder = findPhysicalBorderSize().value_or (BorderSize{}); const auto physicalBounds = D2DUtilities::toRectangle (getWindowScreenRect (hwnd)); const auto physicalClient = physicalBorder.subtractedFrom (physicalBounds); @@ -1781,7 +1786,7 @@ class HWNDComponentPeer final : public ComponentPeer BorderSize getFrameSize() const override { - return findPhysicalBorderSize().multipliedBy (1.0 / scaleFactor); + return findPhysicalBorderSize().value_or (BorderSize{}).multipliedBy (1.0 / scaleFactor); } bool setAlwaysOnTop (bool alwaysOnTop) override @@ -2027,7 +2032,7 @@ class HWNDComponentPeer final : public ComponentPeer } HWNDComponentPeer& peer; - ComponentPeer::DragInfo dragInfo; + DragInfo dragInfo; bool peerIsDeleted = false; private: @@ -2226,7 +2231,7 @@ class HWNDComponentPeer final : public ComponentPeer uint32 lastPaintTime = 0; ULONGLONG lastMagnifySize = 0; bool isDragging = false, isMouseOver = false, - hasCreatedCaret = false, constrainerIsResizing = false; + hasCreatedCaret = false, constrainerIsResizing = false, sizing = false; IconConverters::IconPtr currentWindowIcon; FileDropTarget* dropTarget = nullptr; UWPUIViewSettings uwpViewSettings; @@ -2390,6 +2395,7 @@ class HWNDComponentPeer final : public ComponentPeer const auto hasMax = (styleFlags & windowHasMaximiseButton) != 0; const auto appearsOnTaskbar = (styleFlags & windowAppearsOnTaskbar) != 0; const auto resizable = (styleFlags & windowIsResizable) != 0; + const auto usesDropShadow = windowUsesNativeShadow(); if (parentToAddTo != nullptr) { @@ -2397,13 +2403,14 @@ class HWNDComponentPeer final : public ComponentPeer } else { - if (titled || windowUsesNativeShadow()) + if (titled || usesDropShadow) { + type |= usesDropShadow ? WS_CAPTION : 0; type |= titled ? (WS_OVERLAPPED | WS_CAPTION) : WS_POPUP; type |= hasClose ? (WS_SYSMENU | WS_CAPTION) : 0; type |= hasMin ? (WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU) : 0; type |= hasMax ? (WS_MAXIMIZEBOX | WS_CAPTION | WS_SYSMENU) : 0; - type |= resizable || windowUsesNativeShadow() ? WS_THICKFRAME : 0; + type |= resizable ? WS_THICKFRAME : 0; } else { @@ -2420,7 +2427,7 @@ class HWNDComponentPeer final : public ComponentPeer L"", type, 0, 0, 0, 0, parentToAddTo, nullptr, (HINSTANCE) Process::getCurrentModuleInstanceHandle(), nullptr); - if (! titled && windowUsesNativeShadow()) + if (! titled && usesDropShadow) { // The choice of margins is very particular. // - Using 0 for all values disables the system decoration (shadow etc.) completely. @@ -2676,22 +2683,15 @@ class HWNDComponentPeer final : public ComponentPeer client, }; - std::optional doMouseMove (LPARAM lParam, bool isMouseDownEvent, WindowArea area) + std::optional doMouseMove (const LPARAM lParam, bool isMouseDownEvent, WindowArea area) { - auto point = getPOINTFromLParam (lParam); - - if (area == WindowArea::client) - ClientToScreen (hwnd, &point); - - const auto adjustedLParam = MAKELPARAM (point.x, point.y); - // Check if the mouse has moved since being pressed in the caption area. // If it has, then we defer to DefWindowProc to handle the mouse movement. // Allowing DefWindowProc to handle WM_NCLBUTTONDOWN directly will pause message // processing (and therefore painting) when the mouse is clicked in the caption area, // which is why we wait until the mouse is *moved* before asking the system to take over. // Letting the system handle the move is important for things like Aero Snap to work. - if (captionMouseDown.has_value() && *captionMouseDown != adjustedLParam) + if (area == WindowArea::nonclient && captionMouseDown.has_value() && *captionMouseDown != lParam) { captionMouseDown.reset(); @@ -2703,20 +2703,21 @@ class HWNDComponentPeer final : public ComponentPeer // ModifierKeys::currentModifiers gets left in the wrong state. As a workaround, we // manually update the modifier keys after DefWindowProc exits, and update the // capture state if necessary. - const auto result = DefWindowProc (hwnd, WM_NCLBUTTONDOWN, HTCAPTION, adjustedLParam); + const auto result = DefWindowProc (hwnd, WM_NCLBUTTONDOWN, HTCAPTION, lParam); getMouseModifiers(); releaseCaptureIfNecessary(); return result; } - const auto position = getLocalPointFromScreenLParam (adjustedLParam); - ModifierKeys modsToSend (ModifierKeys::currentModifiers); // this will be handled by WM_TOUCH if (isTouchEvent() || areOtherTouchSourcesActive()) return {}; + const auto position = area == WindowArea::client ? getPointFromLocalLParam (lParam) + : getLocalPointFromScreenLParam (lParam); + if (! isMouseOver) { isMouseOver = true; @@ -2801,7 +2802,7 @@ class HWNDComponentPeer final : public ComponentPeer isDragging = true; - doMouseEvent (clientLparamToPoint (lParam), MouseInputSource::defaultPressure); + doMouseEvent (getPointFromLocalLParam (lParam), MouseInputSource::defaultPressure); } } @@ -2894,14 +2895,8 @@ class HWNDComponentPeer final : public ComponentPeer // the window with focus, despite what the MSDN docs might say. // This is the behaviour we want; if we're receiving a scroll event, we can assume it // should be processed by the current peer. - const auto currentMousePos = getPOINTFromLParam ((LPARAM) GetMessagePos()); - auto* peer = getOwnerOfWindow (WindowFromPoint (currentMousePos)); - - if (peer == nullptr) - return false; - - const auto localPos = peer->globalToLocal (convertPhysicalScreenPointToLogical (D2DUtilities::toPoint (currentMousePos), hwnd).toFloat()); - peer->handleMouseWheel (getPointerType (wParam), localPos, getMouseEventTime(), wheel); + const auto localPos = getLocalPointFromScreenLParam ((LPARAM) GetMessagePos()); + handleMouseWheel (getPointerType (wParam), localPos, getMouseEventTime(), wheel); return true; } @@ -3361,7 +3356,11 @@ class HWNDComponentPeer final : public ComponentPeer movingLeft, movingBottom, movingRight); - r = D2DUtilities::toRECT (modifiedPhysicalBounds); + + if (! modifiedPhysicalBounds.has_value()) + return TRUE; + + r = D2DUtilities::toRECT (*modifiedPhysicalBounds); } return TRUE; @@ -3376,12 +3375,14 @@ class HWNDComponentPeer final : public ComponentPeer && ! Component::isMouseButtonDownAnywhere()) { const auto requestedPhysicalBounds = D2DUtilities::toRectangle ({ wp.x, wp.y, wp.x + wp.cx, wp.y + wp.cy }); - const auto modifiedPhysicalBounds = getConstrainedBounds (requestedPhysicalBounds, false, false, false, false); - wp.x = modifiedPhysicalBounds.getX(); - wp.y = modifiedPhysicalBounds.getY(); - wp.cx = modifiedPhysicalBounds.getWidth(); - wp.cy = modifiedPhysicalBounds.getHeight(); + if (const auto modifiedPhysicalBounds = getConstrainedBounds (requestedPhysicalBounds, false, false, false, false)) + { + wp.x = modifiedPhysicalBounds->getX(); + wp.y = modifiedPhysicalBounds->getY(); + wp.cx = modifiedPhysicalBounds->getWidth(); + wp.cy = modifiedPhysicalBounds->getHeight(); + } } } @@ -3393,9 +3394,17 @@ class HWNDComponentPeer final : public ComponentPeer return 0; } - Rectangle getConstrainedBounds (Rectangle proposed, bool top, bool left, bool bottom, bool right) const + std::optional> getConstrainedBounds (Rectangle proposed, + bool top, + bool left, + bool bottom, + bool right) const { const auto physicalBorder = findPhysicalBorderSize(); + + if (! physicalBorder.has_value()) + return {}; + const auto logicalBorder = getFrameSize(); // The constrainer expects to operate in logical coordinate space. @@ -3407,7 +3416,7 @@ class HWNDComponentPeer final : public ComponentPeer // After the constrainer returns, we substitute in the other direction, replacing logical // borders with physical. const auto requestedPhysicalBounds = proposed; - const auto requestedPhysicalClient = physicalBorder.subtractedFrom (requestedPhysicalBounds); + const auto requestedPhysicalClient = physicalBorder->subtractedFrom (requestedPhysicalBounds); const auto requestedLogicalClient = detail::ScalingHelpers::unscaledScreenPosToScaled ( component, convertPhysicalScreenRectangleToLogical (requestedPhysicalClient, hwnd)); @@ -3453,7 +3462,7 @@ class HWNDComponentPeer final : public ComponentPeer return modified; }(); - return physicalBorder.addedTo (withSnappedPosition); + return physicalBorder->addedTo (withSnappedPosition); } enum class ForceRefreshDispatcher @@ -3705,11 +3714,21 @@ class HWNDComponentPeer final : public ComponentPeer return globalToLocal (convertPhysicalScreenPointToLogical (globalPos, hwnd).toFloat()); } - Point clientLparamToPoint (LPARAM lParam) + Point getPointFromLocalLParam (LPARAM lParam) noexcept { - auto point = getPOINTFromLParam (lParam); - ClientToScreen (hwnd, &point); - return getLocalPointFromScreenLParam (MAKELPARAM (point.x, point.y)); + const auto p = D2DUtilities::toPoint (getPOINTFromLParam (lParam)); + + if (! isPerMonitorDPIAwareWindow (hwnd)) + return p.toFloat(); + + // LPARAM is relative to this window's top-left but may be on a different monitor so we need to calculate the + // physical screen position and then convert this to local logical coordinates + auto r = getWindowScreenRect (hwnd); + const auto windowBorder = findPhysicalBorderSize().value_or (BorderSize{}); + const auto offset = p + + Point { (int) r.left, (int) r.top } + + Point { windowBorder.getLeft(), windowBorder.getTop() }; + return globalToLocal (Desktop::getInstance().getDisplays().physicalToLogical (offset).toFloat()); } Point getCurrentMousePos() noexcept @@ -3719,12 +3738,12 @@ class HWNDComponentPeer final : public ComponentPeer static ModifierKeys getMouseModifiers() { - HWNDComponentPeer::updateKeyModifiers(); + updateKeyModifiers(); int mouseMods = 0; - if (HWNDComponentPeer::isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier; - if (HWNDComponentPeer::isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier; - if (HWNDComponentPeer::isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier; + if (isKeyDown (VK_LBUTTON)) mouseMods |= ModifierKeys::leftButtonModifier; + if (isKeyDown (VK_RBUTTON)) mouseMods |= ModifierKeys::rightButtonModifier; + if (isKeyDown (VK_MBUTTON)) mouseMods |= ModifierKeys::middleButtonModifier; ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods); @@ -3775,8 +3794,12 @@ class HWNDComponentPeer final : public ComponentPeer if (const auto result = DefWindowProc (h, message, wParam, lParam); HTSIZEFIRST <= result && result <= HTSIZELAST) return result; - const auto localPoint = getLocalPointFromScreenLParam (lParam).toFloat(); - const auto kind = component.findControlAtPoint (localPoint); + const auto physicalPoint = D2DUtilities::toPoint (getPOINTFromLParam (lParam)); + const auto logicalPoint = convertPhysicalScreenPointToLogical (physicalPoint, hwnd); + const auto localPoint = globalToLocal (logicalPoint.toFloat()); + const auto componentPoint = detail::ScalingHelpers::unscaledScreenPosToScaled (component, localPoint); + + const auto kind = component.findControlAtPoint (componentPoint); using Kind = Component::WindowControlKind; switch (kind) @@ -3953,7 +3976,7 @@ class HWNDComponentPeer final : public ComponentPeer case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - doMouseUp (clientLparamToPoint (lParam), wParam); + doMouseUp (getPointFromLocalLParam (lParam), wParam); return 0; case WM_POINTERWHEEL: @@ -3985,13 +4008,25 @@ class HWNDComponentPeer final : public ComponentPeer break; //============================================================================== + case WM_ENTERSIZEMOVE: + sizing = true; + break; + + case WM_EXITSIZEMOVE: + sizing = false; + break; + case WM_SIZING: + sizing = true; return handleSizeConstraining (*(RECT*) lParam, wParam); case WM_MOVING: return handleSizeConstraining (*(RECT*) lParam, 0); case WM_WINDOWPOSCHANGING: + if (hasTitleBar() && sizing) + break; + return handlePositionChanging (*(WINDOWPOS*) lParam); case 0x2e0: /* WM_DPICHANGED */ return handleDPIChanging ((int) HIWORD (wParam), *(RECT*) lParam); @@ -4072,7 +4107,7 @@ class HWNDComponentPeer final : public ComponentPeer if (auto* modal = Component::getCurrentlyModalComponent()) if (auto* peer = modal->getPeer()) - if ((peer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0) + if ((peer->getStyleFlags() & windowIsTemporary) != 0) sendInputAttemptWhenModalMessage(); break; @@ -4287,7 +4322,7 @@ class HWNDComponentPeer final : public ComponentPeer case WM_IME_SETCONTEXT: imeHandler.handleSetContext (h, wParam == TRUE); lParam &= ~(LPARAM) ISC_SHOWUICOMPOSITIONWINDOW; - return ImmIsUIMessage (h, message, wParam, lParam); + break; case WM_IME_STARTCOMPOSITION: imeHandler.handleStartComposition (*this); return 0; case WM_IME_ENDCOMPOSITION: imeHandler.handleEndComposition (*this, h); return 0; @@ -4545,9 +4580,10 @@ class HWNDComponentPeer final : public ComponentPeer { if (auto* targetComp = dynamic_cast (target)) { - auto area = peer.getComponent().getLocalArea (targetComp, target->getCaretRectangle()); + const auto screenPos = targetComp->localPointToGlobal (target->getCaretRectangle().getBottomLeft()); + const auto relativePos = peer.globalToLocal (screenPos) * peer.getPlatformScaleFactor(); - CANDIDATEFORM pos = { 0, CFS_CANDIDATEPOS, { area.getX(), area.getBottom() }, { 0, 0, 0, 0 } }; + CANDIDATEFORM pos { 0, CFS_CANDIDATEPOS, D2DUtilities::toPOINT (relativePos), { 0, 0, 0, 0 } }; ImmSetCandidateWindow (hImc, &pos); } } diff --git a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index 4a83998c5041..f9aa7a9b5644 100644 --- a/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -1697,11 +1697,9 @@ void CodeEditorComponent::setFont (const Font& newFont) { font = newFont; - JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS charWidth = font.getStringWidthFloat ("0"); - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - JUCE_END_IGNORE_WARNINGS_MSVC + JUCE_END_IGNORE_DEPRECATION_WARNINGS lineHeight = roundToInt (font.getHeight()); resized(); diff --git a/modules/juce_gui_extra/misc/juce_PushNotifications.cpp b/modules/juce_gui_extra/misc/juce_PushNotifications.cpp index 4b6bca78be0d..c6d4ee426e15 100644 --- a/modules/juce_gui_extra/misc/juce_PushNotifications.cpp +++ b/modules/juce_gui_extra/misc/juce_PushNotifications.cpp @@ -36,10 +36,50 @@ namespace juce { //============================================================================== -#if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC +#if ! JUCE_PUSH_NOTIFICATIONS_IMPL + +struct PushNotifications::Impl +{ + explicit Impl (PushNotifications& o) : owner (o) {} + + void requestPermissionsWithSettings (const Settings&) const + { + owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); }); + } + + void requestSettingsUsed() const + { + owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); }); + } + + bool areNotificationsEnabled() const { return false; } + void getDeliveredNotifications() const {} + void removeAllDeliveredNotifications() const {} + String getDeviceToken() const { return {}; } + void setupChannels (const Array&, const Array&) const {} + void getPendingLocalNotifications() const {} + void removeAllPendingLocalNotifications() const {} + void subscribeToTopic (const String&) const {} + void unsubscribeFromTopic (const String&) const {} + void sendLocalNotification (const Notification&) const {} + void removeDeliveredNotification (const String&) const {} + void removePendingLocalNotification (const String&) const {} + void sendUpstreamMessage (const String&, + const String&, + const String&, + const String&, + int, + const StringPairArray&) const {} + +private: + PushNotifications& owner; +}; + bool PushNotifications::Notification::isValid() const noexcept { return true; } + #endif +//============================================================================== PushNotifications::Notification::Notification (const Notification& other) : identifier (other.identifier), title (other.title), @@ -82,9 +122,7 @@ PushNotifications::Notification::Notification (const Notification& other) //============================================================================== PushNotifications::PushNotifications() - #if JUCE_PUSH_NOTIFICATIONS - : pimpl (new Pimpl (*this)) - #endif + : pimpl (new Impl (*this)) { } @@ -93,128 +131,90 @@ PushNotifications::~PushNotifications() { clearSingletonInstance(); } void PushNotifications::addListener (Listener* l) { listeners.add (l); } void PushNotifications::removeListener (Listener* l) { listeners.remove (l); } -void PushNotifications::requestPermissionsWithSettings ([[maybe_unused]] const PushNotifications::Settings& settings) +void PushNotifications::requestPermissionsWithSettings (const Settings& settings) { - #if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC) pimpl->requestPermissionsWithSettings (settings); - #else - listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); }); - #endif } void PushNotifications::requestSettingsUsed() { - #if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC) pimpl->requestSettingsUsed(); - #else - listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); }); - #endif } bool PushNotifications::areNotificationsEnabled() const { - #if JUCE_PUSH_NOTIFICATIONS return pimpl->areNotificationsEnabled(); - #else - return false; - #endif } void PushNotifications::getDeliveredNotifications() const { - #if JUCE_PUSH_NOTIFICATIONS pimpl->getDeliveredNotifications(); - #endif } void PushNotifications::removeAllDeliveredNotifications() { - #if JUCE_PUSH_NOTIFICATIONS pimpl->removeAllDeliveredNotifications(); - #endif } String PushNotifications::getDeviceToken() const { - #if JUCE_PUSH_NOTIFICATIONS return pimpl->getDeviceToken(); - #else - return {}; - #endif } -void PushNotifications::setupChannels ([[maybe_unused]] const Array& groups, [[maybe_unused]] const Array& channels) +void PushNotifications::setupChannels (const Array& groups, + const Array& channels) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->setupChannels (groups, channels); - #endif } void PushNotifications::getPendingLocalNotifications() const { - #if JUCE_PUSH_NOTIFICATIONS pimpl->getPendingLocalNotifications(); - #endif } void PushNotifications::removeAllPendingLocalNotifications() { - #if JUCE_PUSH_NOTIFICATIONS pimpl->removeAllPendingLocalNotifications(); - #endif } -void PushNotifications::subscribeToTopic ([[maybe_unused]] const String& topic) +void PushNotifications::subscribeToTopic (const String& topic) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->subscribeToTopic (topic); - #endif } -void PushNotifications::unsubscribeFromTopic ([[maybe_unused]] const String& topic) +void PushNotifications::unsubscribeFromTopic (const String& topic) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->unsubscribeFromTopic (topic); - #endif } - -void PushNotifications::sendLocalNotification ([[maybe_unused]] const Notification& n) +void PushNotifications::sendLocalNotification (const Notification& n) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->sendLocalNotification (n); - #endif } -void PushNotifications::removeDeliveredNotification ([[maybe_unused]] const String& identifier) +void PushNotifications::removeDeliveredNotification (const String& identifier) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->removeDeliveredNotification (identifier); - #endif } -void PushNotifications::removePendingLocalNotification ([[maybe_unused]] const String& identifier) +void PushNotifications::removePendingLocalNotification (const String& identifier) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->removePendingLocalNotification (identifier); - #endif } -void PushNotifications::sendUpstreamMessage ([[maybe_unused]] const String& serverSenderId, - [[maybe_unused]] const String& collapseKey, - [[maybe_unused]] const String& messageId, - [[maybe_unused]] const String& messageType, - [[maybe_unused]] int timeToLive, - [[maybe_unused]] const StringPairArray& additionalData) +void PushNotifications::sendUpstreamMessage (const String& serverSenderId, + const String& collapseKey, + const String& messageId, + const String& messageType, + int timeToLive, + const StringPairArray& additionalData) { - #if JUCE_PUSH_NOTIFICATIONS pimpl->sendUpstreamMessage (serverSenderId, collapseKey, messageId, messageType, timeToLive, additionalData); - #endif } //============================================================================== @@ -234,4 +234,92 @@ void PushNotifications::Listener::upstreamMessageSent ([[maybe_unused]] const St void PushNotifications::Listener::upstreamMessageSendingError ([[maybe_unused]] const String& messageId, [[maybe_unused]] const String& error) {} +//============================================================================== +void privatePostSystemNotification (const String& notificationTitle, const String& notificationBody); +void privatePostSystemNotification ([[maybe_unused]] const String& notificationTitle, + [[maybe_unused]] const String& notificationBody) +{ + #if JUCE_PUSH_NOTIFICATIONS + #if JUCE_ANDROID || JUCE_IOS || JUCE_MAC + auto* notificationsInstance = PushNotifications::getInstance(); + + if (notificationsInstance == nullptr) + return; + + #if JUCE_ANDROID + notificationsInstance->requestPermissionsWithSettings ({}); + + static auto channels = std::invoke ([]() -> Array + { + PushNotifications::Channel chan; + + chan.identifier = "1"; + chan.name = "Notifications"; + chan.description = "Accessibility notifications"; + chan.groupId = "accessibility"; + chan.ledColour = Colours::yellow; + chan.canShowBadge = true; + chan.enableLights = true; + chan.enableVibration = true; + chan.soundToPlay = URL ("default_os_sound"); + chan.vibrationPattern = { 1000, 1000 }; + + return { chan }; + }); + + notificationsInstance->setupChannels ({ PushNotifications::ChannelGroup { "accessibility", "accessibility" } }, + channels); + #else + static auto settings = std::invoke ([] + { + PushNotifications::Settings s; + s.allowAlert = true; + s.allowBadge = true; + s.allowSound = true; + + #if JUCE_IOS + PushNotifications::Settings::Category c; + c.identifier = "Accessibility"; + + s.categories = { c }; + #endif + + return s; + }); + + notificationsInstance->requestPermissionsWithSettings (settings); + #endif + + const auto notification = std::invoke ([¬ificationTitle, ¬ificationBody] + { + PushNotifications::Notification n; + + n.identifier = String (Random::getSystemRandom().nextInt()); + n.title = notificationTitle; + n.body = notificationBody; + + #if JUCE_IOS + n.category = "Accessibility"; + #elif JUCE_ANDROID + n.channelId = "1"; + n.icon = "accessibilitynotificationicon"; + #endif + + return n; + }); + + if (notification.isValid()) + notificationsInstance->sendLocalNotification (notification); + + #else + SystemTrayIconComponent systemTrayIcon; + + Image im (Image::ARGB, 128, 128, true); + systemTrayIcon.setIconImage (im, im); + + systemTrayIcon.showInfoBubble (notificationTitle, notificationBody); + #endif + #endif +} + } // namespace juce diff --git a/modules/juce_gui_extra/misc/juce_PushNotifications.h b/modules/juce_gui_extra/misc/juce_PushNotifications.h index b3d7b550b1b6..b5bafe2511b3 100644 --- a/modules/juce_gui_extra/misc/juce_PushNotifications.h +++ b/modules/juce_gui_extra/misc/juce_PushNotifications.h @@ -403,8 +403,8 @@ class JUCE_API PushNotifications : private DeletedAtShutdown */ struct Category { - juce::String identifier; /**< unique identifier */ - juce::Array actions; /**< optional list of actions within this category */ + String identifier; /**< unique identifier */ + Array actions; /**< optional list of actions within this category */ bool sendDismissAction = false; /**< whether dismiss action will be sent to the app */ }; @@ -703,12 +703,8 @@ class JUCE_API PushNotifications : private DeletedAtShutdown friend struct JuceFirebaseMessagingService; #endif - #if JUCE_PUSH_NOTIFICATIONS - struct Pimpl; - friend struct Pimpl; - - std::unique_ptr pimpl; - #endif + struct Impl; + std::unique_ptr pimpl; }; } // namespace juce diff --git a/modules/juce_gui_extra/native/juce_AppleRemote_mac.mm b/modules/juce_gui_extra/native/juce_AppleRemote_mac.mm index 0b2f5493d79d..77d3f43a7eda 100644 --- a/modules/juce_gui_extra/native/juce_AppleRemote_mac.mm +++ b/modules/juce_gui_extra/native/juce_AppleRemote_mac.mm @@ -63,9 +63,9 @@ io_object_t getAppleRemoteDevice() return kIOMainPortDefault; #endif - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return kIOMasterPortDefault; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS }(); if (IOServiceGetMatchingServices (defaultPort, dict, &iter) == kIOReturnSuccess diff --git a/modules/juce_gui_extra/native/juce_PushNotifications_android.cpp b/modules/juce_gui_extra/native/juce_PushNotifications_android.cpp index 1d77f035541e..25dd16027f2a 100644 --- a/modules/juce_gui_extra/native/juce_PushNotifications_android.cpp +++ b/modules/juce_gui_extra/native/juce_PushNotifications_android.cpp @@ -35,6 +35,8 @@ namespace juce { +#define JUCE_PUSH_NOTIFICATIONS_IMPL 1 + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \ METHOD (constructor, "", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \ METHOD (enableLights, "enableLights", "(Z)V") \ @@ -274,14 +276,17 @@ bool PushNotifications::Notification::isValid() const noexcept } //============================================================================== -struct PushNotifications::Pimpl +struct PushNotifications::Impl { - Pimpl (PushNotifications& p) + explicit Impl (PushNotifications& p) : owner (p) {} bool areNotificationsEnabled() const { + if (getAndroidSDKVersion() >= 33 && ! RuntimePermissions::isGranted (RuntimePermissions::postNotification)) + return false; + if (getAndroidSDKVersion() >= 24) { auto* env = getEnv(); @@ -296,23 +301,47 @@ struct PushNotifications::Pimpl } //============================================================================== - void sendLocalNotification (const PushNotifications::Notification& n) + void requestPermissionsWithSettings (const Settings&) + { + RuntimePermissions::request (RuntimePermissions::postNotification, [&] (bool) + { + const auto notifyListeners = [] + { + if (auto* instance = PushNotifications::getInstance()) + instance->listeners.call ([] (Listener& l) { l.notificationSettingsReceived (makeDefaultSettings()); }); + }; + + if (MessageManager::getInstance()->isThisTheMessageThread()) + notifyListeners(); + else + MessageManager::callAsync (notifyListeners); + }); + } + + void requestSettingsUsed() + { + owner.listeners.call ([] (Listener& l) { l.notificationSettingsReceived (makeDefaultSettings()); }); + } + + void sendLocalNotification (const Notification& n) { // All required fields have to be setup! jassert (n.isValid()); auto* env = getEnv(); - auto notificationManager = getNotificationManager(); - - if (notificationManager.get() != nullptr) + if (auto notificationManager = getNotificationManager()) { - auto notification = juceNotificationToJavaNotification (n); - - auto tag = javaString (n.identifier); - const int id = 0; + if (auto notification = juceNotificationToJavaNotification (n)) + { + auto tag = javaString (n.identifier); + const int id = 0; - env->CallVoidMethod (notificationManager.get(), NotificationManagerBase.notify, tag.get(), id, notification.get()); + env->CallVoidMethod (notificationManager.get(), + NotificationManagerBase.notify, + tag.get(), + id, notification.get()); + } } } @@ -322,7 +351,7 @@ struct PushNotifications::Pimpl { auto* env = getEnv(); - Array notifications; + Array notifications; auto notificationManager = getNotificationManager(); jassert (notificationManager != nullptr); @@ -601,22 +630,23 @@ struct PushNotifications::Pimpl javaString ("notification").get())); } - static LocalRef juceNotificationToJavaNotification (const PushNotifications::Notification& n) + static LocalRef juceNotificationToJavaNotification (const Notification& n) { auto* env = getEnv(); auto notificationBuilder = createNotificationBuilder (n); - setupRequiredFields (n, notificationBuilder); - setupOptionalFields (n, notificationBuilder); + notificationBuilder = setupRequiredFields (n, notificationBuilder); + notificationBuilder = setupOptionalFields (n, notificationBuilder); + notificationBuilder = setupActions (n, notificationBuilder); - if (n.actions.size() > 0) - setupActions (n, notificationBuilder); + if (notificationBuilder == nullptr) + return notificationBuilder; return LocalRef (env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.build)); } - static LocalRef createNotificationBuilder (const PushNotifications::Notification& n) + static LocalRef createNotificationBuilder (const Notification& n) { auto* env = getEnv(); LocalRef context (getMainActivity()); @@ -648,8 +678,13 @@ struct PushNotifications::Pimpl return LocalRef (env->NewObject (builderClass, builderConstructor, context.get())); } - static void setupRequiredFields (const PushNotifications::Notification& n, LocalRef& notificationBuilder) + static constexpr auto FLAG_IMMUTABLE = 0x04000000; + + static LocalRef setupRequiredFields (const Notification& n, LocalRef notificationBuilder) { + if (notificationBuilder == nullptr) + return notificationBuilder; + auto* env = getEnv(); LocalRef context (getMainActivity()); @@ -669,15 +704,23 @@ struct PushNotifications::Pimpl context.get(), 1002, notifyIntent.get(), - 0)); + FLAG_IMMUTABLE)); env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentTitle, javaString (n.title).get()); env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentText, javaString (n.body).get()); env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setContentIntent, notifyPendingIntent.get()); auto resources = LocalRef (env->CallObjectMethod (context.get(), AndroidContext.getResources)); - const int iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (n.icon).get(), - javaString ("raw").get(), packageNameString.get()); + const auto iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (n.icon).get(), + javaString ("raw").get(), packageNameString.get()); + + if (iconId == 0) + { + // If you hit this, the notification icon could not be located, and the notification + // will not be sent. + jassertfalse; + return {}; + } env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setSmallIcon, iconId); @@ -688,15 +731,20 @@ struct PushNotifications::Pimpl auto publicNotificationBuilder = createNotificationBuilder (n); - setupRequiredFields (*n.publicVersion, publicNotificationBuilder); - setupOptionalFields (*n.publicVersion, publicNotificationBuilder); + publicNotificationBuilder = setupRequiredFields (*n.publicVersion, publicNotificationBuilder); + publicNotificationBuilder = setupOptionalFields (*n.publicVersion, publicNotificationBuilder); + + if (publicNotificationBuilder == nullptr) + return {}; auto publicVersion = LocalRef (env->CallObjectMethod (publicNotificationBuilder, NotificationBuilderApi16.build)); env->CallObjectMethod (notificationBuilder, NotificationBuilderApi21.setPublicVersion, publicVersion.get()); } + + return notificationBuilder; } - static LocalRef juceNotificationToBundle (const PushNotifications::Notification& n) + static LocalRef juceNotificationToBundle (const Notification& n) { auto* env = getEnv(); @@ -753,8 +801,11 @@ struct PushNotifications::Pimpl return bundle; } - static void setupOptionalFields (const PushNotifications::Notification& n, LocalRef& notificationBuilder) + static LocalRef setupOptionalFields (const Notification n, LocalRef& notificationBuilder) { + if (notificationBuilder == nullptr) + return notificationBuilder; + auto* env = getEnv(); if (n.subtitle.isNotEmpty()) @@ -818,11 +869,11 @@ struct PushNotifications::Pimpl if (getAndroidSDKVersion() < 24) { - const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer; + const bool useChronometer = n.timestampVisibility == Notification::chronometer; env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer); } - const bool showTimeStamp = n.timestampVisibility != PushNotifications::Notification::off; + const bool showTimeStamp = n.timestampVisibility != Notification::off; env->CallObjectMethod (notificationBuilder, NotificationBuilderApi17.setShowWhen, showTimeStamp); if (n.groupId.isNotEmpty()) @@ -857,8 +908,8 @@ struct PushNotifications::Pimpl if (getAndroidSDKVersion() >= 24) { - const bool useChronometer = n.timestampVisibility == PushNotifications::Notification::chronometer; - const bool useCountDownChronometer = n.timestampVisibility == PushNotifications::Notification::countDownChronometer; + const bool useChronometer = n.timestampVisibility == Notification::chronometer; + const bool useCountDownChronometer = n.timestampVisibility == Notification::countDownChronometer; env->CallObjectMethod (notificationBuilder, NotificationBuilderApi24.setChronometerCountDown, useCountDownChronometer); env->CallObjectMethod (notificationBuilder, NotificationBuilderApi16.setUsesChronometer, useChronometer | useCountDownChronometer); @@ -871,12 +922,15 @@ struct PushNotifications::Pimpl env->CallObjectMethod (notificationBuilder, NotificationBuilderApi26.setTimeoutAfter, (jlong) n.timeoutAfterMs); } - setupNotificationDeletedCallback (n, notificationBuilder); + return setupNotificationDeletedCallback (n, notificationBuilder); } - static void setupNotificationDeletedCallback (const PushNotifications::Notification& n, - LocalRef& notificationBuilder) + static LocalRef setupNotificationDeletedCallback (const Notification& n, + LocalRef notificationBuilder) { + if (notificationBuilder == nullptr) + return notificationBuilder; + auto* env = getEnv(); LocalRef context (getMainActivity()); @@ -895,24 +949,27 @@ struct PushNotifications::Pimpl context.get(), 1002, deleteIntent.get(), - 0)); + FLAG_IMMUTABLE)); env->CallObjectMethod (notificationBuilder, NotificationBuilderBase.setDeleteIntent, deletePendingIntent.get()); + + return notificationBuilder; } - static void setupActions (const PushNotifications::Notification& n, LocalRef& notificationBuilder) + static LocalRef setupActions (const Notification& n, LocalRef notificationBuilder) { + if (notificationBuilder == nullptr || n.actions.isEmpty()) + return notificationBuilder; + auto* env = getEnv(); LocalRef context (getMainActivity()); - int actionIndex = 0; - - for (const auto& action : n.actions) + for (const auto [actionIndex, action] : enumerate (n.actions)) { auto activityClass = LocalRef (env->CallObjectMethod (context.get(), JavaObject.getClass)); auto notifyIntent = LocalRef (env->NewObject (AndroidIntent, AndroidIntent.constructorWithContextAndClass, context.get(), activityClass.get())); - const bool isTextStyle = action.style == PushNotifications::Notification::Action::text; + const bool isTextStyle = action.style == Notification::Action::text; auto packageNameString = LocalRef ((jstring) (env->CallObjectMethod (context.get(), AndroidContext.getPackageName))); const String notificationActionString = isTextStyle ? ".JUCE_NOTIFICATION_TEXT_INPUT_ACTION." : ".JUCE_NOTIFICATION_BUTTON_ACTION."; @@ -928,7 +985,7 @@ struct PushNotifications::Pimpl context.get(), 1002, notifyIntent.get(), - 0)); + FLAG_IMMUTABLE)); auto resources = LocalRef (env->CallObjectMethod (context.get(), AndroidContext.getResources)); int iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (action.icon).get(), @@ -938,6 +995,14 @@ struct PushNotifications::Pimpl iconId = env->CallIntMethod (resources, AndroidResources.getIdentifier, javaString (n.icon).get(), javaString ("raw").get(), packageNameString.get()); + if (iconId == 0) + { + // If this is hit, the notification icon could not be located, so the notification + // cannot be displayed. + jassertfalse; + return {}; + } + auto actionBuilder = LocalRef (env->NewObject (NotificationActionBuilder, NotificationActionBuilder.constructor, iconId, @@ -982,9 +1047,9 @@ struct PushNotifications::Pimpl env->CallObjectMethod (notificationBuilder, NotificationBuilderApi20.addAction, env->CallObjectMethod (actionBuilder, NotificationActionBuilder.build)); - - ++actionIndex; } + + return notificationBuilder; } static LocalRef juceUrlToAndroidUri (const URL& url) @@ -1029,26 +1094,26 @@ struct PushNotifications::Pimpl return bitmap; } - static String typeToCategory (PushNotifications::Notification::Type t) + static String typeToCategory (Notification::Type t) { switch (t) { - case PushNotifications::Notification::unspecified: return {}; - case PushNotifications::Notification::alarm: return "alarm"; - case PushNotifications::Notification::call: return "call"; - case PushNotifications::Notification::email: return "email"; - case PushNotifications::Notification::error: return "err"; - case PushNotifications::Notification::event: return "event"; - case PushNotifications::Notification::message: return "msg"; - case PushNotifications::Notification::taskProgress: return "progress"; - case PushNotifications::Notification::promo: return "promo"; - case PushNotifications::Notification::recommendation: return "recommendation"; - case PushNotifications::Notification::reminder: return "reminder"; - case PushNotifications::Notification::service: return "service"; - case PushNotifications::Notification::social: return "social"; - case PushNotifications::Notification::status: return "status"; - case PushNotifications::Notification::system: return "sys"; - case PushNotifications::Notification::transport: return "transport"; + case Notification::unspecified: return {}; + case Notification::alarm: return "alarm"; + case Notification::call: return "call"; + case Notification::email: return "email"; + case Notification::error: return "err"; + case Notification::event: return "event"; + case Notification::message: return "msg"; + case Notification::taskProgress: return "progress"; + case Notification::promo: return "promo"; + case Notification::recommendation: return "recommendation"; + case Notification::reminder: return "reminder"; + case Notification::service: return "service"; + case Notification::social: return "social"; + case Notification::status: return "status"; + case Notification::system: return "sys"; + case Notification::transport: return "transport"; } return {}; @@ -1081,11 +1146,11 @@ struct PushNotifications::Pimpl } // Reverse of juceNotificationToBundle(). - static PushNotifications::Notification localNotificationBundleToJuceNotification (const LocalRef& bundle) + static Notification localNotificationBundleToJuceNotification (const LocalRef& bundle) { auto* env = getEnv(); - PushNotifications::Notification n; + Notification n; if (bundle.get() != nullptr) { @@ -1100,23 +1165,23 @@ struct PushNotifications::Pimpl n.icon = getStringFromBundle (env, "icon", bundle); n.channelId = getStringFromBundle (env, "channelId", bundle); - PushNotifications::Notification::Progress progress; + Notification::Progress progress; progress.max = getIntFromBundle (env, "progressMax", bundle); progress.current = getIntFromBundle (env, "progressCurrent", bundle); progress.indeterminate = getBoolFromBundle (env, "progressIndeterminate", bundle); n.progress = progress; n.person = getStringFromBundle (env, "person", bundle); - n.type = (PushNotifications::Notification::Type) getIntFromBundle (env, "type", bundle); - n.priority = (PushNotifications::Notification::Priority) getIntFromBundle (env, "priority", bundle); - n.lockScreenAppearance = (PushNotifications::Notification::LockScreenAppearance) getIntFromBundle (env, "lockScreenAppearance", bundle); + n.type = (Notification::Type) getIntFromBundle (env, "type", bundle); + n.priority = (Notification::Priority) getIntFromBundle (env, "priority", bundle); + n.lockScreenAppearance = (Notification::LockScreenAppearance) getIntFromBundle (env, "lockScreenAppearance", bundle); n.groupId = getStringFromBundle (env, "groupId", bundle); n.groupSortKey = getStringFromBundle (env, "groupSortKey", bundle); n.groupSummary = getBoolFromBundle (env, "groupSummary", bundle); n.accentColour = Colour ((uint32) getIntFromBundle (env, "accentColour", bundle)); n.ledColour = Colour ((uint32) getIntFromBundle (env, "ledColour", bundle)); - PushNotifications::Notification::LedBlinkPattern ledBlinkPattern; + Notification::LedBlinkPattern ledBlinkPattern; ledBlinkPattern.msToBeOn = getIntFromBundle (env, "ledBlinkPatternMsToBeOn", bundle); ledBlinkPattern.msToBeOff = getIntFromBundle (env, "ledBlinkPatternMsToBeOff", bundle); n.ledBlinkPattern = ledBlinkPattern; @@ -1127,9 +1192,9 @@ struct PushNotifications::Pimpl n.localOnly = getBoolFromBundle (env, "localOnly", bundle); n.ongoing = getBoolFromBundle (env, "ongoing", bundle); n.alertOnlyOnce = getBoolFromBundle (env, "alertOnlyOnce", bundle); - n.timestampVisibility = (PushNotifications::Notification::TimestampVisibility) getIntFromBundle (env, "timestampVisibility", bundle); - n.badgeIconType = (PushNotifications::Notification::BadgeIconType) getIntFromBundle (env, "badgeIconType", bundle); - n.groupAlertBehaviour = (PushNotifications::Notification::GroupAlertBehaviour) getIntFromBundle (env, "groupAlertBehaviour", bundle); + n.timestampVisibility = (Notification::TimestampVisibility) getIntFromBundle (env, "timestampVisibility", bundle); + n.badgeIconType = (Notification::BadgeIconType) getIntFromBundle (env, "badgeIconType", bundle); + n.groupAlertBehaviour = (Notification::GroupAlertBehaviour) getIntFromBundle (env, "groupAlertBehaviour", bundle); n.timeoutAfterMs = getLongFromBundle (env, "timeoutAfterMs", bundle); } @@ -1216,7 +1281,7 @@ struct PushNotifications::Pimpl return {}; } - static PushNotifications::Notification javaNotificationToJuceNotification (const LocalRef& notification) + static Notification javaNotificationToJuceNotification (const LocalRef& notification) { auto* env = getEnv(); @@ -1230,10 +1295,10 @@ struct PushNotifications::Pimpl return remoteNotificationBundleToJuceNotification (extras); } - static PushNotifications::Notification remoteNotificationBundleToJuceNotification (const LocalRef& bundle) + static Notification remoteNotificationBundleToJuceNotification (const LocalRef& bundle) { // This will probably work only for remote notifications that get delivered to system tray - PushNotifications::Notification n; + Notification n; n.properties = bundleToVar (bundle); return n; @@ -1282,7 +1347,7 @@ struct PushNotifications::Pimpl } #if defined (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) - static PushNotifications::Notification firebaseRemoteNotificationToJuceNotification (jobject remoteNotification) + static Notification firebaseRemoteNotificationToJuceNotification (jobject remoteNotification) { auto* env = getEnv(); @@ -1325,7 +1390,7 @@ struct PushNotifications::Pimpl propertiesDynamicObject->setProperty ("ttl", ttl); propertiesDynamicObject->setProperty ("data", dataVar); - PushNotifications::Notification n; + Notification n; if (notification != 0) { @@ -1510,6 +1575,15 @@ struct PushNotifications::Pimpl && env->CallBooleanMethod (extras, AndroidBundle.containsKey, javaString ("google.message_id").get()); } + static Settings makeDefaultSettings() + { + Settings settings; + settings.allowAlert = true; + settings.allowBadge = true; + settings.allowSound = true; + return settings; + } + PushNotifications& owner; }; @@ -1577,14 +1651,14 @@ bool juce_handleNotificationIntent (void* intent) { auto* instance = PushNotifications::getInstanceWithoutCreating(); - if (PushNotifications::Pimpl::isDeleteNotificationIntent ((jobject) intent)) + if (PushNotifications::Impl::isDeleteNotificationIntent ((jobject) intent)) { if (instance) instance->pimpl->notifyListenersAboutLocalNotificationDeleted (LocalRef ((jobject) intent)); return true; } - else if (PushNotifications::Pimpl::isLocalNotificationIntent ((jobject) intent)) + else if (PushNotifications::Impl::isLocalNotificationIntent ((jobject) intent)) { if (instance) instance->pimpl->notifyListenersAboutLocalNotification (LocalRef ((jobject) intent)); @@ -1592,7 +1666,7 @@ bool juce_handleNotificationIntent (void* intent) return true; } #if defined (JUCE_FIREBASE_MESSAGING_SERVICE_CLASSNAME) - else if (PushNotifications::Pimpl::isRemoteNotificationIntent ((jobject) intent)) + else if (PushNotifications::Impl::isRemoteNotificationIntent ((jobject) intent)) { if (instance) instance->pimpl->notifyListenersAboutRemoteNotificationFromSystemTray (LocalRef ((jobject) intent)); diff --git a/modules/juce_gui_extra/native/juce_PushNotifications_ios.cpp b/modules/juce_gui_extra/native/juce_PushNotifications_ios.cpp index b093a76434f0..a2d44c7c7eaf 100644 --- a/modules/juce_gui_extra/native/juce_PushNotifications_ios.cpp +++ b/modules/juce_gui_extra/native/juce_PushNotifications_ios.cpp @@ -35,6 +35,8 @@ namespace juce { +#define JUCE_PUSH_NOTIFICATIONS_IMPL 1 + struct PushNotificationsDelegateDetails { //============================================================================== @@ -301,9 +303,9 @@ bool PushNotifications::Notification::isValid() const noexcept } //============================================================================== -struct PushNotifications::Pimpl +struct PushNotifications::Impl { - Pimpl (PushNotifications& p) + explicit Impl (PushNotifications& p) : owner (p) { Class::setThis (delegate.get(), this); @@ -555,7 +557,7 @@ struct PushNotifications::Pimpl Class() : ObjCClass ("JucePushNotificationsDelegate_") { - addIvar ("self"); + addIvar ("self"); addMethod (@selector (application:didRegisterForRemoteNotificationsWithDeviceToken:), [] (id self, SEL, UIApplication*, NSData* data) { @@ -596,8 +598,8 @@ struct PushNotifications::Pimpl } //============================================================================== - static Pimpl& getThis (id self) { return *getIvar (self, "self"); } - static void setThis (id self, Pimpl* d) { object_setInstanceVariable (self, "self", d); } + static Impl& getThis (id self) { return *getIvar (self, "self"); } + static void setThis (id self, Impl* d) { object_setInstanceVariable (self, "self", d); } }; //============================================================================== diff --git a/modules/juce_gui_extra/native/juce_PushNotifications_mac.cpp b/modules/juce_gui_extra/native/juce_PushNotifications_mac.cpp index b1a3c81e8328..a06dd82dc929 100644 --- a/modules/juce_gui_extra/native/juce_PushNotifications_mac.cpp +++ b/modules/juce_gui_extra/native/juce_PushNotifications_mac.cpp @@ -35,7 +35,9 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +#define JUCE_PUSH_NOTIFICATIONS_IMPL 1 + +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS namespace PushNotificationsDelegateDetailsOsx { @@ -343,9 +345,9 @@ struct PushNotificationsDelegate bool PushNotifications::Notification::isValid() const noexcept { return true; } //============================================================================== -struct PushNotifications::Pimpl : private PushNotificationsDelegate +struct PushNotifications::Impl : private PushNotificationsDelegate { - Pimpl (PushNotifications& p) + explicit Impl (PushNotifications& p) : owner (p) { } @@ -534,6 +536,6 @@ struct PushNotifications::Pimpl : private PushNotificationsDelegate PushNotifications::Settings settings; }; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_gui_extra/native/juce_SystemTrayIcon_mac.cpp b/modules/juce_gui_extra/native/juce_SystemTrayIcon_mac.cpp index cad7a62fa1e2..624282de74a7 100644 --- a/modules/juce_gui_extra/native/juce_SystemTrayIcon_mac.cpp +++ b/modules/juce_gui_extra/native/juce_SystemTrayIcon_mac.cpp @@ -35,7 +35,7 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId, int topLevelIndex, bool addDelegate); @@ -446,6 +446,6 @@ void SystemTrayIconComponent::showDropdownMenu (const PopupMenu& menu) pimpl->statusItemHolder->showMenu (menu); } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp b/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp index f92dfbe4e683..ffea19caa4d3 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp @@ -1137,11 +1137,11 @@ class GtkChildProcess final : private CommandReceiver::Responder // Using the non-deprecated webkit_javascript_result_get_js_value() functions seems easier // but returned values fail the JS_IS_VALUE() internal assertion. The example code from the // documentation doesn't seem to work either. - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS WebKitJavascriptResultUniquePtr jsResult { wk.juce_webkit_web_view_run_javascript_finish (owner->webview, result, &error) }; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS if (jsResult == nullptr) { diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm index e00ebde53482..fcff49f51bcd 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_mac.mm @@ -197,9 +197,9 @@ explicit WebViewKeyEquivalentResponder (bool acceptsFirstMouse) if (@available (macOS 10.12, *)) return (modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return (modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS }(); if (isCommandDown) @@ -270,7 +270,7 @@ explicit WebViewKeyEquivalentResponder (bool acceptsFirstMouse) } }; -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS struct DownloadClickDetectorClass final : public ObjCClass { DownloadClickDetectorClass() : ObjCClass ("JUCEWebClickDetector_") @@ -372,7 +372,7 @@ static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, } } }; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif // Connects the delegate to the rest of the implementation without making WebViewDelegateClass @@ -652,7 +652,7 @@ static void displayError (WebBrowserComponent* owner, NSError* error) //============================================================================== #if JUCE_MAC -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS class WebBrowserComponent::Impl::Platform::WebViewImpl : public WebBrowserComponent::Impl::PlatformInterface, #if JUCE_MAC public NSViewComponent @@ -783,7 +783,7 @@ void evaluateJavascript (const String&, WebBrowserComponent::EvaluationCallback) ObjCObjectHandle webView; ObjCObjectHandle clickListener; }; -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS #endif class WebBrowserComponent::Impl::Platform::WKWebViewImpl : public WebBrowserComponent::Impl::PlatformInterface, diff --git a/modules/juce_midi_ci/ci/juce_CIDevice.cpp b/modules/juce_midi_ci/ci/juce_CIDevice.cpp index 6c2d55a64216..ae43eabc6a5c 100644 --- a/modules/juce_midi_ci/ci/juce_CIDevice.cpp +++ b/modules/juce_midi_ci/ci/juce_CIDevice.cpp @@ -1168,7 +1168,6 @@ class Device::Impl : private SubscriptionManagerDelegate static MUID getReallyRandomMuid() { Random random; - random.setSeedRandomly(); return MUID::makeRandom (random); } diff --git a/modules/juce_opengl/native/juce_OpenGL_mac.h b/modules/juce_opengl/native/juce_OpenGL_mac.h index badc06f207ec..c5efcc0e878c 100644 --- a/modules/juce_opengl/native/juce_OpenGL_mac.h +++ b/modules/juce_opengl/native/juce_OpenGL_mac.h @@ -35,7 +35,7 @@ namespace juce { -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS class OpenGLContext::NativeContext { @@ -326,6 +326,6 @@ bool OpenGLHelpers::isContextActive() return CGLGetCurrentContext() != CGLContextObj(); } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE +JUCE_END_IGNORE_DEPRECATION_WARNINGS } // namespace juce diff --git a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp index 76f44dcd5043..478db67fb28a 100644 --- a/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp +++ b/modules/juce_product_unlocking/marketplace/juce_OnlineUnlockStatus.cpp @@ -328,8 +328,7 @@ String OnlineUnlockStatus::MachineIDUtilities::getUniqueMachineID() return getEncodedIDString (SystemStats::getUniqueDeviceID()); } -JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") -JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996) +JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS StringArray OnlineUnlockStatus::MachineIDUtilities::getLocalMachineIDs() { @@ -350,8 +349,7 @@ StringArray OnlineUnlockStatus::getLocalMachineIDs() return MachineIDUtilities::getLocalMachineIDs(); } -JUCE_END_IGNORE_WARNINGS_GCC_LIKE -JUCE_END_IGNORE_WARNINGS_MSVC +JUCE_END_IGNORE_DEPRECATION_WARNINGS void OnlineUnlockStatus::userCancelled() { diff --git a/modules/juce_video/native/juce_CameraDevice_ios.h b/modules/juce_video/native/juce_CameraDevice_ios.h index 395385fa7074..d0812c71d8bd 100644 --- a/modules/juce_video/native/juce_CameraDevice_ios.h +++ b/modules/juce_video/native/juce_CameraDevice_ios.h @@ -646,7 +646,7 @@ struct CameraDevice::Pimpl printImageOutputDebugInfo (captureOutput); - if (auto* connection = findVideoConnection (captureOutput)) + if (findVideoConnection (captureOutput) != nullptr) { auto* photoOutput = (AVCapturePhotoOutput*) captureOutput; auto outputConnection = [photoOutput connectionWithMediaType: AVMediaTypeVideo]; @@ -1176,7 +1176,7 @@ struct CameraDevice::ViewerComponent : public UIViewComponent { if ([keyPath isEqualToString: @"videoRotationAngleForHorizonLevelPreview"]) { - if (auto* previewLayer = getPreviewLayer (self)) + if (getPreviewLayer (self) != nullptr) { auto* viewer = static_cast (context); auto& session = viewer->cameraDevice.pimpl->captureSession; diff --git a/modules/juce_video/native/juce_CameraDevice_mac.h b/modules/juce_video/native/juce_CameraDevice_mac.h index 4a5dc509656e..f87dcd689364 100644 --- a/modules/juce_video/native/juce_CameraDevice_mac.h +++ b/modules/juce_video/native/juce_CameraDevice_mac.h @@ -148,9 +148,9 @@ struct CameraDevice::Pimpl { if (@available (macOS 10.15, *)) { - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS const auto deviceType = AVCaptureDeviceTypeExternalUnknown; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS auto* discovery = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes: @[AVCaptureDeviceTypeBuiltInWideAngleCamera, deviceType] mediaType: AVMediaTypeVideo @@ -159,9 +159,9 @@ struct CameraDevice::Pimpl return [discovery devices]; } - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS return [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS } static StringArray getAvailableDevices() @@ -331,7 +331,7 @@ struct CameraDevice::Pimpl NSUniquePtr> delegate; }; - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") + JUCE_BEGIN_IGNORE_DEPRECATION_WARNINGS class PreCatalinaStillImageOutput : public ImageOutputBase { public: @@ -397,7 +397,7 @@ struct CameraDevice::Pimpl private: AVCaptureStillImageOutput* imageOutput = nil; }; - JUCE_END_IGNORE_WARNINGS_GCC_LIKE + JUCE_END_IGNORE_DEPRECATION_WARNINGS //============================================================================== void addImageCapture() @@ -542,7 +542,7 @@ struct CameraDevice::Pimpl startSession(); - if (auto* videoConnection = getVideoConnection()) + if (getVideoConnection() != nullptr) imageOutput->triggerImageCapture (*this); }