| @@ -722,11 +722,20 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead, | |||||
| &dataSize); | &dataSize); | ||||
| if (err == noErr) | if (err == noErr) | ||||
| { | { | ||||
| #if (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) | |||||
| [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl]; | |||||
| #else | |||||
| [[UIApplication sharedApplication] openURL: (NSURL*)hostUrl options: @{} completionHandler: nil]; | |||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| if (@available (iOS 10.0, *)) | |||||
| { | |||||
| [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl | |||||
| options: @{} | |||||
| completionHandler: nil]; | |||||
| return; | |||||
| } | |||||
| #endif | #endif | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| [[UIApplication sharedApplication] openURL: (NSURL*) hostUrl]; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| } | } | ||||
| @@ -82,7 +82,7 @@ namespace CoreMidiHelpers | |||||
| struct Sender; | struct Sender; | ||||
| #if JUCE_HAS_NEW_COREMIDI_API | #if JUCE_HAS_NEW_COREMIDI_API | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| template <> | template <> | ||||
| struct Sender<ImplementationStrategy::onlyNew> : public SenderBase | struct Sender<ImplementationStrategy::onlyNew> : public SenderBase | ||||
| @@ -829,7 +829,7 @@ namespace CoreMidiHelpers | |||||
| struct CreatorFunctions; | struct CreatorFunctions; | ||||
| #if JUCE_HAS_NEW_COREMIDI_API | #if JUCE_HAS_NEW_COREMIDI_API | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| template <> | template <> | ||||
| struct CreatorFunctions<ImplementationStrategy::onlyNew> | struct CreatorFunctions<ImplementationStrategy::onlyNew> | ||||
| @@ -28,27 +28,19 @@ | |||||
| #if JucePlugin_Build_AUv3 | #if JucePlugin_Build_AUv3 | ||||
| #if JUCE_MAC | |||||
| #if (! defined MAC_OS_X_VERSION_MIN_REQUIRED) || (! defined MAC_OS_X_VERSION_10_11) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11) | |||||
| #error AUv3 needs Deployment Target OS X 10.11 or higher to compile | |||||
| #endif | |||||
| #if (defined MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13) | |||||
| #define JUCE_AUV3_MIDI_OUTPUT_SUPPORTED 1 | |||||
| #define JUCE_AUV3_VIEW_CONFIG_SUPPORTED 1 | |||||
| #endif | |||||
| #if defined (MAC_OS_VERSION_12_0) | |||||
| #define JUCE_AUV3_MIDI_EVENT_LIST_SUPPORTED 1 | |||||
| #endif | |||||
| #if JUCE_MAC && ! (defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11) | |||||
| #error AUv3 needs Deployment Target OS X 10.11 or higher to compile | |||||
| #endif | #endif | ||||
| #if JUCE_IOS | |||||
| #if (defined __IPHONE_11_0) && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) | |||||
| #define JUCE_AUV3_MIDI_OUTPUT_SUPPORTED 1 | |||||
| #define JUCE_AUV3_VIEW_CONFIG_SUPPORTED 1 | |||||
| #endif | |||||
| #if (defined __IPHONE_15_0) | |||||
| #define JUCE_AUV3_MIDI_EVENT_LIST_SUPPORTED 1 | |||||
| #endif | |||||
| #if (JUCE_IOS && defined (__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) \ | |||||
| || (JUCE_MAC && defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) | |||||
| #define JUCE_AUV3_MIDI_EVENT_LIST_SUPPORTED 1 | |||||
| #endif | |||||
| #if (JUCE_IOS && defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0) \ | |||||
| || (JUCE_MAC && defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13) | |||||
| #define JUCE_AUV3_MIDI_OUTPUT_SUPPORTED 1 | |||||
| #define JUCE_AUV3_VIEW_CONFIG_SUPPORTED 1 | |||||
| #endif | #endif | ||||
| #ifndef __OBJC2__ | #ifndef __OBJC2__ | ||||
| @@ -210,8 +202,10 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| virtual NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>*) = 0; | virtual NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>*) = 0; | ||||
| virtual void selectViewConfiguration (AUAudioUnitViewConfiguration*) = 0; | |||||
| virtual void selectViewConfiguration (AUAudioUnitViewConfiguration*) = 0; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | #endif | ||||
| private: | private: | ||||
| @@ -265,7 +259,8 @@ private: | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||||
| #if JUCE_AUV3_MIDI_OUTPUT_SUPPORTED | #if JUCE_AUV3_MIDI_OUTPUT_SUPPORTED | ||||
| addMethod (@selector (MIDIOutputNames), getMIDIOutputNames, "@@:"); | |||||
| if (@available (macOS 10.13, iOS 11.0, *)) | |||||
| addMethod (@selector (MIDIOutputNames), getMIDIOutputNames, "@@:"); | |||||
| #endif | #endif | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -284,8 +279,11 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | ||||
| addMethod (@selector (supportedViewConfigurations:), getSupportedViewConfigurations, "@@:@"); | |||||
| addMethod (@selector (selectViewConfiguration:), selectViewConfiguration, "v@:@"); | |||||
| if (@available (macOS 10.13, iOS 11.0, *)) | |||||
| { | |||||
| addMethod (@selector (supportedViewConfigurations:), getSupportedViewConfigurations, "@@:@"); | |||||
| addMethod (@selector (selectViewConfiguration:), selectViewConfiguration, "v@:@"); | |||||
| } | |||||
| #endif | #endif | ||||
| registerClass(); | registerClass(); | ||||
| @@ -396,8 +394,10 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| static NSIndexSet* getSupportedViewConfigurations (id self, SEL, NSArray<AUAudioUnitViewConfiguration*>* configs) { return _this (self)->getSupportedViewConfigurations (configs); } | static NSIndexSet* getSupportedViewConfigurations (id self, SEL, NSArray<AUAudioUnitViewConfiguration*>* configs) { return _this (self)->getSupportedViewConfigurations (configs); } | ||||
| static void selectViewConfiguration (id self, SEL, AUAudioUnitViewConfiguration* config) { _this (self)->selectViewConfiguration (config); } | |||||
| static void selectViewConfiguration (id self, SEL, AUAudioUnitViewConfiguration* config) { _this (self)->selectViewConfiguration (config); } | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| @@ -896,6 +896,7 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | #if JUCE_AUV3_VIEW_CONFIG_SUPPORTED | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>* configs) override | NSIndexSet* getSupportedViewConfigurations (NSArray<AUAudioUnitViewConfiguration*>* configs) override | ||||
| { | { | ||||
| auto supportedViewIndecies = [[NSMutableIndexSet alloc] init]; | auto supportedViewIndecies = [[NSMutableIndexSet alloc] init]; | ||||
| @@ -932,6 +933,7 @@ public: | |||||
| { | { | ||||
| processorHolder->viewConfiguration.reset (new AudioProcessorHolder::ViewConfig { [config width], [config height], [config hostHasController] == YES }); | processorHolder->viewConfiguration.reset (new AudioProcessorHolder::ViewConfig { [config width], [config height], [config hostHasController] == YES }); | ||||
| } | } | ||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | #endif | ||||
| struct ScopedKeyChange | struct ScopedKeyChange | ||||
| @@ -1598,10 +1600,11 @@ private: | |||||
| // send MIDI | // send MIDI | ||||
| #if JucePlugin_ProducesMidiOutput && JUCE_AUV3_MIDI_OUTPUT_SUPPORTED | #if JucePlugin_ProducesMidiOutput && JUCE_AUV3_MIDI_OUTPUT_SUPPORTED | ||||
| if (auto midiOut = [au MIDIOutputEventBlock]) | |||||
| if (@available (macOS 10.13, iOS 11.0, *)) | |||||
| { | { | ||||
| for (const auto metadata : midiMessages) | |||||
| midiOut (metadata.samplePosition, 0, metadata.numBytes, metadata.data); | |||||
| if (auto midiOut = [au MIDIOutputEventBlock]) | |||||
| for (const auto metadata : midiMessages) | |||||
| midiOut (metadata.samplePosition, 0, metadata.numBytes, metadata.data); | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -39,18 +39,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| #include <AudioUnit/AudioUnitCarbonView.h> | #include <AudioUnit/AudioUnitCarbonView.h> | ||||
| #endif | #endif | ||||
| #ifndef JUCE_SUPPORTS_AUv3 | |||||
| #if __OBJC2__ \ | |||||
| && (JUCE_IOS || (defined (MAC_OS_X_VERSION_10_11) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11))) | |||||
| #define JUCE_SUPPORTS_AUv3 1 | |||||
| #else | |||||
| #define JUCE_SUPPORTS_AUv3 0 | |||||
| #endif | |||||
| #endif | |||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| #include <CoreAudioKit/AUViewController.h> | |||||
| #endif | |||||
| #include <CoreAudioKit/AUViewController.h> | |||||
| #include <juce_audio_basics/native/juce_mac_CoreAudioLayouts.h> | #include <juce_audio_basics/native/juce_mac_CoreAudioLayouts.h> | ||||
| #include <juce_audio_basics/midi/juce_MidiDataConcatenator.h> | #include <juce_audio_basics/midi/juce_MidiDataConcatenator.h> | ||||
| @@ -547,9 +536,7 @@ public: | |||||
| AudioComponentGetDescription (auComponent, &componentDesc); | AudioComponentGetDescription (auComponent, &componentDesc); | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| isAUv3 = ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | isAUv3 = ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | ||||
| #endif | |||||
| wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice | wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice | ||||
| || componentDesc.componentType == kAudioUnitType_MusicEffect | || componentDesc.componentType == kAudioUnitType_MusicEffect | ||||
| @@ -1136,18 +1123,12 @@ public: | |||||
| bool hasEditor() const override | bool hasEditor() const override | ||||
| { | { | ||||
| #if JUCE_MAC | |||||
| return true; | |||||
| #elif JUCE_SUPPORTS_AUv3 | |||||
| UInt32 dataSize; | UInt32 dataSize; | ||||
| Boolean isWritable; | Boolean isWritable; | ||||
| return (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_RequestViewController, | return (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_RequestViewController, | ||||
| kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr | kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr | ||||
| && dataSize == sizeof (uintptr_t) && isWritable != 0); | && dataSize == sizeof (uintptr_t) && isWritable != 0); | ||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | } | ||||
| AudioProcessorEditor* createEditor() override; | AudioProcessorEditor* createEditor() override; | ||||
| @@ -2261,10 +2242,8 @@ public: | |||||
| { | { | ||||
| addAndMakeVisible (wrapper); | addAndMakeVisible (wrapper); | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| viewControllerCallback = | viewControllerCallback = | ||||
| CreateObjCBlock (this, &AudioUnitPluginWindowCocoa::requestViewControllerCallback); | CreateObjCBlock (this, &AudioUnitPluginWindowCocoa::requestViewControllerCallback); | ||||
| #endif | |||||
| setOpaque (true); | setOpaque (true); | ||||
| setVisible (true); | setVisible (true); | ||||
| @@ -2284,7 +2263,6 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| void embedViewController (JUCE_IOS_MAC_VIEW* pluginView, const CGSize& size) | void embedViewController (JUCE_IOS_MAC_VIEW* pluginView, const CGSize& size) | ||||
| { | { | ||||
| wrapper.setView (pluginView); | wrapper.setView (pluginView); | ||||
| @@ -2299,7 +2277,6 @@ public: | |||||
| wrapper.setSize (static_cast<int> (size.width), static_cast<int> (size.height)); | wrapper.setSize (static_cast<int> (size.width), static_cast<int> (size.height)); | ||||
| #endif | #endif | ||||
| } | } | ||||
| #endif | |||||
| bool isValid() const { return wrapper.getView() != nil || waitingForViewCallback; } | bool isValid() const { return wrapper.getView() != nil || waitingForViewCallback; } | ||||
| @@ -2323,10 +2300,8 @@ private: | |||||
| AudioUnitPluginInstance& plugin; | AudioUnitPluginInstance& plugin; | ||||
| AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper; | AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper; | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| typedef void (^ViewControllerCallbackBlock)(AUViewControllerBase *); | typedef void (^ViewControllerCallbackBlock)(AUViewControllerBase *); | ||||
| ObjCBlock<ViewControllerCallbackBlock> viewControllerCallback; | ObjCBlock<ViewControllerCallbackBlock> viewControllerCallback; | ||||
| #endif | |||||
| bool waitingForViewCallback = false; | bool waitingForViewCallback = false; | ||||
| @@ -2375,7 +2350,6 @@ private: | |||||
| dataSize = 0; | dataSize = 0; | ||||
| isWritable = false; | isWritable = false; | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, | if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, | ||||
| 0, &dataSize, &isWritable) == noErr | 0, &dataSize, &isWritable) == noErr | ||||
| && dataSize == sizeof (ViewControllerCallbackBlock)) | && dataSize == sizeof (ViewControllerCallbackBlock)) | ||||
| @@ -2391,7 +2365,6 @@ private: | |||||
| waitingForViewCallback = false; | waitingForViewCallback = false; | ||||
| } | } | ||||
| #endif | |||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| if (createGenericViewIfNeeded && (pluginView == nil)) | if (createGenericViewIfNeeded && (pluginView == nil)) | ||||
| @@ -2418,7 +2391,6 @@ private: | |||||
| return pluginView != nil; | return pluginView != nil; | ||||
| } | } | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| void requestViewControllerCallback (AUViewControllerBase* controller) | void requestViewControllerCallback (AUViewControllerBase* controller) | ||||
| { | { | ||||
| const auto viewSize = [&controller] | const auto viewSize = [&controller] | ||||
| @@ -2459,7 +2431,6 @@ private: | |||||
| embedViewController ([controller view], viewSize); | embedViewController ([controller view], viewSize); | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| }; | }; | ||||
| #if JUCE_SUPPORT_CARBON | #if JUCE_SUPPORT_CARBON | ||||
| @@ -2693,23 +2664,17 @@ void AudioUnitPluginFormat::createPluginInstance (const PluginDescription& desc, | |||||
| struct AUAsyncInitializationCallback | struct AUAsyncInitializationCallback | ||||
| { | { | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| typedef void (^AUCompletionCallbackBlock)(AudioComponentInstance, OSStatus); | typedef void (^AUCompletionCallbackBlock)(AudioComponentInstance, OSStatus); | ||||
| #endif | |||||
| AUAsyncInitializationCallback (double inSampleRate, int inFramesPerBuffer, | AUAsyncInitializationCallback (double inSampleRate, int inFramesPerBuffer, | ||||
| PluginCreationCallback inOriginalCallback) | PluginCreationCallback inOriginalCallback) | ||||
| : sampleRate (inSampleRate), framesPerBuffer (inFramesPerBuffer), | : sampleRate (inSampleRate), framesPerBuffer (inFramesPerBuffer), | ||||
| originalCallback (std::move (inOriginalCallback)) | originalCallback (std::move (inOriginalCallback)) | ||||
| { | { | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| block = CreateObjCBlock (this, &AUAsyncInitializationCallback::completion); | block = CreateObjCBlock (this, &AUAsyncInitializationCallback::completion); | ||||
| #endif | |||||
| } | } | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| AUCompletionCallbackBlock getBlock() noexcept { return block; } | |||||
| #endif | |||||
| AUCompletionCallbackBlock getBlock() noexcept { return block; } | |||||
| void completion (AudioComponentInstance audioUnit, OSStatus err) | void completion (AudioComponentInstance audioUnit, OSStatus err) | ||||
| { | { | ||||
| @@ -2734,26 +2699,18 @@ void AudioUnitPluginFormat::createPluginInstance (const PluginDescription& desc, | |||||
| double sampleRate; | double sampleRate; | ||||
| int framesPerBuffer; | int framesPerBuffer; | ||||
| PluginCreationCallback originalCallback; | PluginCreationCallback originalCallback; | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| ObjCBlock<AUCompletionCallbackBlock> block; | ObjCBlock<AUCompletionCallbackBlock> block; | ||||
| #endif | |||||
| }; | }; | ||||
| auto callbackBlock = new AUAsyncInitializationCallback (rate, blockSize, std::move (callback)); | auto callbackBlock = new AUAsyncInitializationCallback (rate, blockSize, std::move (callback)); | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| //============================================================================== | |||||
| bool isAUv3 = ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | |||||
| if (isAUv3) | |||||
| if ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0) | |||||
| { | { | ||||
| AudioComponentInstantiate (auComponent, kAudioComponentInstantiation_LoadOutOfProcess, | AudioComponentInstantiate (auComponent, kAudioComponentInstantiation_LoadOutOfProcess, | ||||
| callbackBlock->getBlock()); | callbackBlock->getBlock()); | ||||
| return; | return; | ||||
| } | } | ||||
| #endif // JUCE_SUPPORTS_AUv3 | |||||
| AudioComponentInstance audioUnit; | AudioComponentInstance audioUnit; | ||||
| auto err = AudioComponentInstanceNew(auComponent, &audioUnit); | auto err = AudioComponentInstanceNew(auComponent, &audioUnit); | ||||
| @@ -2767,7 +2724,6 @@ void AudioUnitPluginFormat::createPluginInstance (const PluginDescription& desc, | |||||
| bool AudioUnitPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription& desc) const | bool AudioUnitPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription& desc) const | ||||
| { | { | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| String pluginName, version, manufacturer; | String pluginName, version, manufacturer; | ||||
| AudioComponentDescription componentDesc; | AudioComponentDescription componentDesc; | ||||
| @@ -2780,9 +2736,6 @@ bool AudioUnitPluginFormat::requiresUnblockedMessageThreadDuringCreation (const | |||||
| if (AudioComponentGetDescription (auComp, &componentDesc) == noErr) | if (AudioComponentGetDescription (auComp, &componentDesc) == noErr) | ||||
| return ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | return ((componentDesc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | ||||
| } | } | ||||
| #else | |||||
| ignoreUnused (desc); | |||||
| #endif | |||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -2815,11 +2768,9 @@ StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath&, | |||||
| { | { | ||||
| ignoreUnused (allowPluginsWhichRequireAsynchronousInstantiation); | ignoreUnused (allowPluginsWhichRequireAsynchronousInstantiation); | ||||
| #if JUCE_SUPPORTS_AUv3 | |||||
| bool isAUv3 = ((desc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | |||||
| const auto isAUv3 = ((desc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0); | |||||
| if (allowPluginsWhichRequireAsynchronousInstantiation || ! isAUv3) | if (allowPluginsWhichRequireAsynchronousInstantiation || ! isAUv3) | ||||
| #endif | |||||
| result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc)); | result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc)); | ||||
| } | } | ||||
| } | } | ||||
| @@ -26,8 +26,6 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| #if defined (MAC_OS_X_VERSION_10_11) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 | |||||
| //============================================================================== | //============================================================================== | ||||
| class BluetoothMidiPairingWindowClass : public ObjCClass<NSObject> | class BluetoothMidiPairingWindowClass : public ObjCClass<NSObject> | ||||
| { | { | ||||
| @@ -159,20 +157,12 @@ private: | |||||
| bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback, | bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback, | ||||
| Rectangle<int>* bounds) | Rectangle<int>* bounds) | ||||
| { | { | ||||
| new BluetoothMidiSelectorWindowHelper (exitCallback, bounds); | |||||
| return true; | |||||
| } | |||||
| bool BluetoothMidiDevicePairingDialogue::isAvailable() | |||||
| { | |||||
| return true; | |||||
| } | |||||
| #else | |||||
| if (@available (macOS 10.11, *)) | |||||
| { | |||||
| new BluetoothMidiSelectorWindowHelper (exitCallback, bounds); | |||||
| return true; | |||||
| } | |||||
| bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* exitCallback, | |||||
| Rectangle<int>*) | |||||
| { | |||||
| std::unique_ptr<ModalComponentManager::Callback> cb (exitCallback); | std::unique_ptr<ModalComponentManager::Callback> cb (exitCallback); | ||||
| // This functionality is unavailable when targetting OSX < 10.11. Instead, | // This functionality is unavailable when targetting OSX < 10.11. Instead, | ||||
| // you should pair Bluetooth MIDI devices using the "Audio MIDI Setup" app | // you should pair Bluetooth MIDI devices using the "Audio MIDI Setup" app | ||||
| @@ -183,9 +173,10 @@ bool BluetoothMidiDevicePairingDialogue::open (ModalComponentManager::Callback* | |||||
| bool BluetoothMidiDevicePairingDialogue::isAvailable() | bool BluetoothMidiDevicePairingDialogue::isAvailable() | ||||
| { | { | ||||
| if (@available (macOS 10.11, *)) | |||||
| return true; | |||||
| return false; | return false; | ||||
| } | } | ||||
| #endif | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -28,7 +28,7 @@ | |||||
| #if JUCE_MAC || JUCE_IOS | #if JUCE_MAC || JUCE_IOS | ||||
| #if JUCE_IOS | #if JUCE_IOS | ||||
| #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_12_0 | |||||
| #if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0 | |||||
| #define GLES_SILENCE_DEPRECATION 1 | #define GLES_SILENCE_DEPRECATION 1 | ||||
| #endif | #endif | ||||
| @@ -410,12 +410,20 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& | |||||
| #if JUCE_IOS | #if JUCE_IOS | ||||
| ignoreUnused (parameters); | ignoreUnused (parameters); | ||||
| #if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0) | |||||
| return [[UIApplication sharedApplication] openURL: filenameAsURL]; | |||||
| #else | |||||
| [[UIApplication sharedApplication] openURL: filenameAsURL options: @{} completionHandler: nil]; | |||||
| return true; | |||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| if (@available (iOS 10.0, *)) | |||||
| { | |||||
| [[UIApplication sharedApplication] openURL: filenameAsURL | |||||
| options: @{} | |||||
| completionHandler: nil]; | |||||
| return true; | |||||
| } | |||||
| #endif | #endif | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| return [[UIApplication sharedApplication] openURL: filenameAsURL]; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #else | #else | ||||
| NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; | NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; | ||||
| @@ -434,16 +442,21 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& | |||||
| for (int i = 0; i < params.size(); ++i) | for (int i = 0; i < params.size(); ++i) | ||||
| [paramArray addObject: juceStringToNS (params[i])]; | [paramArray addObject: juceStringToNS (params[i])]; | ||||
| #if (defined MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 | |||||
| auto config = [NSWorkspaceOpenConfiguration configuration]; | |||||
| [config setCreatesNewApplicationInstance: YES]; | |||||
| config.arguments = paramArray; | |||||
| #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 | |||||
| if (@available (macOS 10.15, *)) | |||||
| { | |||||
| auto config = [NSWorkspaceOpenConfiguration configuration]; | |||||
| [config setCreatesNewApplicationInstance: YES]; | |||||
| config.arguments = paramArray; | |||||
| [workspace openApplicationAtURL: filenameAsURL | |||||
| configuration: config | |||||
| completionHandler: nil]; | |||||
| return true; | |||||
| } | |||||
| #endif | |||||
| [workspace openApplicationAtURL: filenameAsURL | |||||
| configuration: config | |||||
| completionHandler: nil]; | |||||
| return true; | |||||
| #else | |||||
| NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; | NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease]; | ||||
| [dict setObject: paramArray | [dict setObject: paramArray | ||||
| @@ -453,7 +466,6 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& | |||||
| options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance | options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance | ||||
| configuration: dict | configuration: dict | ||||
| error: nil]; | error: nil]; | ||||
| #endif | |||||
| } | } | ||||
| if (file.exists()) | if (file.exists()) | ||||
| @@ -89,15 +89,21 @@ static String getOSXVersion() | |||||
| { | { | ||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| { | { | ||||
| const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist"); | |||||
| #if (defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13) | |||||
| NSError* error = nullptr; | |||||
| NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist) | |||||
| error: &error]; | |||||
| #else | |||||
| NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)]; | |||||
| #endif | |||||
| const auto* dict = [] | |||||
| { | |||||
| const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist"); | |||||
| #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 | |||||
| if (@available (macOS 10.13, *)) | |||||
| { | |||||
| NSError* error = nullptr; | |||||
| return [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist) | |||||
| error: &error]; | |||||
| } | |||||
| #endif | |||||
| return [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)]; | |||||
| }(); | |||||
| if (dict != nullptr) | if (dict != nullptr) | ||||
| return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); | return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); | ||||
| @@ -37,10 +37,12 @@ static void juceFreeAccessibilityPlatformSpecificData (UIAccessibilityElement* e | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| #if (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) | |||||
| #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 | |||||
| #define JUCE_IOS_CONTAINER_API_AVAILABLE 1 | #define JUCE_IOS_CONTAINER_API_AVAILABLE 1 | ||||
| #endif | #endif | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| constexpr auto juceUIAccessibilityContainerTypeNone = | constexpr auto juceUIAccessibilityContainerTypeNone = | ||||
| #if JUCE_IOS_CONTAINER_API_AVAILABLE | #if JUCE_IOS_CONTAINER_API_AVAILABLE | ||||
| UIAccessibilityContainerTypeNone; | UIAccessibilityContainerTypeNone; | ||||
| @@ -62,6 +64,8 @@ constexpr auto juceUIAccessibilityContainerTypeList = | |||||
| 2; | 2; | ||||
| #endif | #endif | ||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1 | #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1 | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -26,7 +26,7 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| #if ! (defined (__IPHONE_16_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_16_0) | |||||
| #if ! (defined (__IPHONE_16_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_0) | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | ||||
| #define JUCE_DEPRECATION_IGNORED 1 | #define JUCE_DEPRECATION_IGNORED 1 | ||||
| #endif | #endif | ||||
| @@ -382,6 +382,4 @@ std::shared_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||||
| #endif | #endif | ||||
| #undef JUCE_DEPRECATION_IGNORED | |||||
| } // namespace juce | } // namespace juce | ||||
| @@ -23,7 +23,7 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| #if defined (__IPHONE_13_0) | |||||
| #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 | |||||
| #define JUCE_HAS_IOS_POINTER_SUPPORT 1 | #define JUCE_HAS_IOS_POINTER_SUPPORT 1 | ||||
| #else | #else | ||||
| #define JUCE_HAS_IOS_POINTER_SUPPORT 0 | #define JUCE_HAS_IOS_POINTER_SUPPORT 0 | ||||
| @@ -38,15 +38,18 @@ static UIInterfaceOrientation getWindowOrientation() | |||||
| { | { | ||||
| UIApplication* sharedApplication = [UIApplication sharedApplication]; | UIApplication* sharedApplication = [UIApplication sharedApplication]; | ||||
| #if (defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13_0) | |||||
| for (UIScene* scene in [sharedApplication connectedScenes]) | |||||
| if ([scene isKindOfClass: [UIWindowScene class]]) | |||||
| return [(UIWindowScene*) scene interfaceOrientation]; | |||||
| #if defined (__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0 | |||||
| if (@available (iOS 13.0, *)) | |||||
| { | |||||
| for (UIScene* scene in [sharedApplication connectedScenes]) | |||||
| if ([scene isKindOfClass: [UIWindowScene class]]) | |||||
| return [(UIWindowScene*) scene interfaceOrientation]; | |||||
| } | |||||
| #endif | |||||
| return UIInterfaceOrientationPortrait; | |||||
| #else | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| return [sharedApplication statusBarOrientation]; | return [sharedApplication statusBarOrientation]; | ||||
| #endif | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| namespace Orientations | namespace Orientations | ||||
| @@ -787,7 +787,7 @@ static Rectangle<int> getRecommendedWindowBounds() | |||||
| static BorderSize<int> getSafeAreaInsets (float masterScale) | static BorderSize<int> getSafeAreaInsets (float masterScale) | ||||
| { | { | ||||
| #if defined (__IPHONE_11_0) | |||||
| #if defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0 | |||||
| if (@available (iOS 11.0, *)) | if (@available (iOS 11.0, *)) | ||||
| { | { | ||||
| UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; | UIEdgeInsets safeInsets = TemporaryWindow().window.safeAreaInsets; | ||||
| @@ -799,7 +799,10 @@ static BorderSize<int> getSafeAreaInsets (float masterScale) | |||||
| } | } | ||||
| #endif | #endif | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size; | auto statusBarSize = [UIApplication sharedApplication].statusBarFrame.size; | ||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height); | auto statusBarHeight = jmin (statusBarSize.width, statusBarSize.height); | ||||
| return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 }; | return { roundToInt (statusBarHeight / masterScale), 0, 0, 0 }; | ||||
| @@ -1344,14 +1344,17 @@ public: | |||||
| static NSArray* getSupportedDragTypes() | static NSArray* getSupportedDragTypes() | ||||
| { | { | ||||
| const auto type = | |||||
| #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 | |||||
| NSPasteboardTypeFileURL; | |||||
| #else | |||||
| kUTTypeFileURL; | |||||
| #endif | |||||
| return [NSArray arrayWithObjects: (NSString*) type, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil]; | |||||
| const auto type = [] | |||||
| { | |||||
| #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 | |||||
| if (@available (macOS 10.13, *)) | |||||
| return NSPasteboardTypeFileURL; | |||||
| #endif | |||||
| return (NSString*) kUTTypeFileURL; | |||||
| }(); | |||||
| return [NSArray arrayWithObjects: type, (NSString*) kPasteboardTypeFileURLPromise, NSPasteboardTypeString, nil]; | |||||
| } | } | ||||
| BOOL sendDragCallback (const int type, id <NSDraggingInfo> sender) | BOOL sendDragCallback (const int type, id <NSDraggingInfo> sender) | ||||
| @@ -47,12 +47,15 @@ namespace | |||||
| io_iterator_t iter = 0; | io_iterator_t iter = 0; | ||||
| io_object_t iod = 0; | io_object_t iod = 0; | ||||
| const auto defaultPort = | |||||
| #if defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 | |||||
| kIOMainPortDefault; | |||||
| #else | |||||
| kIOMasterPortDefault; | |||||
| #endif | |||||
| const auto defaultPort = [] | |||||
| { | |||||
| #if defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0 | |||||
| if (@available (macOS 12.0, *)) | |||||
| return kIOMainPortDefault; | |||||
| #endif | |||||
| return kIOMasterPortDefault; | |||||
| }(); | |||||
| if (IOServiceGetMatchingServices (defaultPort, dict, &iter) == kIOReturnSuccess | if (IOServiceGetMatchingServices (defaultPort, dict, &iter) == kIOReturnSuccess | ||||
| && iter != 0) | && iter != 0) | ||||
| @@ -26,7 +26,7 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wdeprecated-declarations") | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new", "-Wdeprecated-declarations") | |||||
| extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId, | extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId, | ||||
| int topLevelIndex, bool addDelegate); | int topLevelIndex, bool addDelegate); | ||||
| @@ -26,11 +26,7 @@ | |||||
| namespace juce | namespace juce | ||||
| { | { | ||||
| #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10) | |||||
| #define JUCE_USE_WKWEBVIEW 1 | |||||
| #endif | |||||
| #if (defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) | |||||
| #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 | |||||
| #define WKWEBVIEW_OPENPANEL_SUPPORTED 1 | #define WKWEBVIEW_OPENPANEL_SUPPORTED 1 | ||||
| #endif | #endif | ||||
| @@ -115,41 +111,39 @@ static NSMutableURLRequest* getRequestForURL (const String& url, const StringArr | |||||
| } | } | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| #if JUCE_USE_WKWEBVIEW | |||||
| using WebViewBase = ObjCClass<WKWebView>; | |||||
| #else | |||||
| using WebViewBase = ObjCClass<WebView>; | |||||
| #endif | |||||
| struct WebViewKeyEquivalentResponder : public WebViewBase | |||||
| template <class WebViewClass> | |||||
| struct WebViewKeyEquivalentResponder : public ObjCClass<WebViewClass> | |||||
| { | { | ||||
| WebViewKeyEquivalentResponder() | WebViewKeyEquivalentResponder() | ||||
| : WebViewBase ("WebViewKeyEquivalentResponder_") | |||||
| : ObjCClass<WebViewClass> ("WebViewKeyEquivalentResponder_") | |||||
| { | { | ||||
| addMethod (@selector (performKeyEquivalent:), performKeyEquivalent, @encode (BOOL), "@:@"); | |||||
| registerClass(); | |||||
| ObjCClass<WebViewClass>::addMethod (@selector (performKeyEquivalent:), performKeyEquivalent, @encode (BOOL), "@:@"); | |||||
| ObjCClass<WebViewClass>::registerClass(); | |||||
| } | } | ||||
| private: | private: | ||||
| static BOOL performKeyEquivalent (id self, SEL selector, NSEvent* event) | static BOOL performKeyEquivalent (id self, SEL selector, NSEvent* event) | ||||
| { | { | ||||
| NSResponder* first = [[self window] firstResponder]; | |||||
| const auto isCommandDown = [event] | |||||
| { | |||||
| const auto modifierFlags = [event modifierFlags]; | |||||
| #if (defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) | |||||
| constexpr auto mask = NSEventModifierFlagDeviceIndependentFlagsMask; | |||||
| constexpr auto key = NSEventModifierFlagCommand; | |||||
| #else | |||||
| constexpr auto mask = NSDeviceIndependentModifierFlagsMask; | |||||
| constexpr auto key = NSCommandKeyMask; | |||||
| #endif | |||||
| #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 | |||||
| if (@available (macOS 10.12, *)) | |||||
| return (modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand; | |||||
| #endif | |||||
| if (([event modifierFlags] & mask) == key) | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| return (modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| }(); | |||||
| if (isCommandDown) | |||||
| { | { | ||||
| auto sendAction = [&] (SEL actionSelector) -> BOOL | auto sendAction = [&] (SEL actionSelector) -> BOOL | ||||
| { | { | ||||
| return [NSApp sendAction: actionSelector | return [NSApp sendAction: actionSelector | ||||
| to: first | |||||
| to: [[self window] firstResponder] | |||||
| from: self]; | from: self]; | ||||
| }; | }; | ||||
| @@ -159,13 +153,122 @@ private: | |||||
| if ([[event charactersIgnoringModifiers] isEqualToString: @"a"]) return sendAction (@selector (selectAll:)); | if ([[event charactersIgnoringModifiers] isEqualToString: @"a"]) return sendAction (@selector (selectAll:)); | ||||
| } | } | ||||
| return sendSuperclassMessage<BOOL> (self, selector, event); | |||||
| return ObjCClass<WebViewClass>::template sendSuperclassMessage<BOOL> (self, selector, event); | |||||
| } | } | ||||
| }; | }; | ||||
| #endif | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| struct DownloadClickDetectorClass : public ObjCClass<NSObject> | |||||
| { | |||||
| DownloadClickDetectorClass() : ObjCClass<NSObject> ("JUCEWebClickDetector_") | |||||
| { | |||||
| addIvar<WebBrowserComponent*> ("owner"); | |||||
| addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:), | |||||
| decidePolicyForNavigationAction, "v@:@@@@@"); | |||||
| addMethod (@selector (webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:), | |||||
| decidePolicyForNewWindowAction, "v@:@@@@@"); | |||||
| addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@"); | |||||
| addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); | |||||
| addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); | |||||
| addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@"); | |||||
| addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), runOpenPanel, "v@:@@", @encode (BOOL)); | |||||
| registerClass(); | |||||
| } | |||||
| static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); } | |||||
| static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); } | |||||
| private: | |||||
| static String getOriginalURL (NSDictionary* actionInformation) | |||||
| { | |||||
| if (NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")]) | |||||
| return nsStringToJuce ([url absoluteString]); | |||||
| return {}; | |||||
| } | |||||
| static void decidePolicyForNavigationAction (id self, SEL, WebView*, NSDictionary* actionInformation, | |||||
| NSURLRequest*, WebFrame*, id<WebPolicyDecisionListener> listener) | |||||
| { | |||||
| if (getOwner (self)->pageAboutToLoad (getOriginalURL (actionInformation))) | |||||
| [listener use]; | |||||
| else | |||||
| [listener ignore]; | |||||
| } | |||||
| static void decidePolicyForNewWindowAction (id self, SEL, WebView*, NSDictionary* actionInformation, | |||||
| NSURLRequest*, NSString*, id<WebPolicyDecisionListener> listener) | |||||
| { | |||||
| getOwner (self)->newWindowAttemptingToLoad (getOriginalURL (actionInformation)); | |||||
| [listener ignore]; | |||||
| } | |||||
| static void didFinishLoadForFrame (id self, SEL, WebView* sender, WebFrame* frame) | |||||
| { | |||||
| if ([frame isEqual: [sender mainFrame]]) | |||||
| { | |||||
| NSURL* url = [[[frame dataSource] request] URL]; | |||||
| getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString])); | |||||
| } | |||||
| } | |||||
| #if JUCE_USE_WKWEBVIEW | |||||
| static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, WebFrame* frame) | |||||
| { | |||||
| if ([frame isEqual: [sender mainFrame]] && error != nullptr && [error code] != NSURLErrorCancelled) | |||||
| { | |||||
| auto errorString = nsStringToJuce ([error localizedDescription]); | |||||
| bool proceedToErrorPage = getOwner (self)->pageLoadHadNetworkError (errorString); | |||||
| // WebKit doesn't have an internal error page, so make a really simple one ourselves | |||||
| if (proceedToErrorPage) | |||||
| getOwner (self)->goToURL ("data:text/plain;charset=UTF-8," + errorString); | |||||
| } | |||||
| } | |||||
| static void willCloseFrame (id self, SEL, WebView*, WebFrame*) | |||||
| { | |||||
| getOwner (self)->windowCloseRequest(); | |||||
| } | |||||
| static void runOpenPanel (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles) | |||||
| { | |||||
| struct DeletedFileChooserWrapper : private DeletedAtShutdown | |||||
| { | |||||
| DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, id<WebOpenPanelResultListener> rl) | |||||
| : chooser (std::move (fc)), listener (rl) | |||||
| { | |||||
| [listener retain]; | |||||
| } | |||||
| ~DeletedFileChooserWrapper() | |||||
| { | |||||
| [listener release]; | |||||
| } | |||||
| std::unique_ptr<FileChooser> chooser; | |||||
| id<WebOpenPanelResultListener> listener; | |||||
| }; | |||||
| auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."), | |||||
| File::getSpecialLocation (File::userHomeDirectory), "*"); | |||||
| auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener); | |||||
| auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | |||||
| | (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0); | |||||
| wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&) | |||||
| { | |||||
| for (auto& f : wrapper->chooser->getResults()) | |||||
| [wrapper->listener chooseFilename: juceStringToNS (f.getFullPathName())]; | |||||
| delete wrapper; | |||||
| }); | |||||
| } | |||||
| }; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | |||||
| struct WebViewDelegateClass : public ObjCClass<NSObject> | struct WebViewDelegateClass : public ObjCClass<NSObject> | ||||
| { | { | ||||
| @@ -182,7 +285,8 @@ struct WebViewDelegateClass : public ObjCClass<NSObject> | |||||
| windowFeatures:), createWebView, "@@:@@@@"); | windowFeatures:), createWebView, "@@:@@@@"); | ||||
| #if WKWEBVIEW_OPENPANEL_SUPPORTED | #if WKWEBVIEW_OPENPANEL_SUPPORTED | ||||
| addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:), runOpenPanel, "v@:@@@@"); | |||||
| if (@available (macOS 10.12, *)) | |||||
| addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:), runOpenPanel, "v@:@@@@"); | |||||
| #endif | #endif | ||||
| registerClass(); | registerClass(); | ||||
| @@ -242,6 +346,7 @@ private: | |||||
| } | } | ||||
| #if WKWEBVIEW_OPENPANEL_SUPPORTED | #if WKWEBVIEW_OPENPANEL_SUPPORTED | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| static void runOpenPanel (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*, | static void runOpenPanel (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*, | ||||
| void (^completionHandler)(NSArray<NSURL*>*)) | void (^completionHandler)(NSArray<NSURL*>*)) | ||||
| { | { | ||||
| @@ -285,10 +390,13 @@ private: | |||||
| auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | ||||
| | ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0); | | ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0); | ||||
| #if (defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14) | |||||
| if ([parameters allowsDirectories]) | |||||
| flags |= FileBrowserComponent::canSelectDirectories; | |||||
| #endif | |||||
| #if (defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14) | |||||
| if (@available (macOS 10.14, *)) | |||||
| { | |||||
| if ([parameters allowsDirectories]) | |||||
| flags |= FileBrowserComponent::canSelectDirectories; | |||||
| } | |||||
| #endif | |||||
| wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&) | wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&) | ||||
| { | { | ||||
| @@ -302,242 +410,186 @@ private: | |||||
| delete wrapper; | delete wrapper; | ||||
| }); | }); | ||||
| } | } | ||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||
| class WebBrowserComponent::Pimpl | |||||
| #if JUCE_MAC | |||||
| : public NSViewComponent | |||||
| #else | |||||
| : public UIViewComponent | |||||
| #endif | |||||
| struct WebViewBase | |||||
| { | |||||
| virtual ~WebViewBase() = default; | |||||
| virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0; | |||||
| virtual void goBack() = 0; | |||||
| virtual void goForward() = 0; | |||||
| virtual void stop() = 0; | |||||
| virtual void refresh() = 0; | |||||
| virtual id getWebView() = 0; | |||||
| }; | |||||
| #if JUCE_MAC | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| class WebViewImpl : public WebViewBase | |||||
| { | { | ||||
| public: | public: | ||||
| Pimpl (WebBrowserComponent* owner) | |||||
| WebViewImpl (WebBrowserComponent* owner) | |||||
| { | { | ||||
| #if JUCE_MAC | |||||
| static WebViewKeyEquivalentResponder webviewClass; | |||||
| webView = (WKWebView*) webviewClass.createInstance(); | |||||
| webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]; | |||||
| #else | |||||
| webView = [[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)]; | |||||
| #endif | |||||
| static WebViewKeyEquivalentResponder<WebView> webviewClass; | |||||
| webView = (WebView*) webviewClass.createInstance(); | |||||
| static WebViewDelegateClass cls; | |||||
| webViewDelegate = [cls.createInstance() init]; | |||||
| WebViewDelegateClass::setOwner (webViewDelegate, owner); | |||||
| webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) | |||||
| frameName: nsEmptyString() | |||||
| groupName: nsEmptyString()]; | |||||
| [webView setNavigationDelegate: webViewDelegate]; | |||||
| [webView setUIDelegate: webViewDelegate]; | |||||
| static DownloadClickDetectorClass cls; | |||||
| clickListener = [cls.createInstance() init]; | |||||
| DownloadClickDetectorClass::setOwner (clickListener, owner); | |||||
| setView (webView); | |||||
| [webView setPolicyDelegate: clickListener]; | |||||
| [webView setFrameLoadDelegate: clickListener]; | |||||
| [webView setUIDelegate: clickListener]; | |||||
| } | } | ||||
| ~Pimpl() | |||||
| ~WebViewImpl() override | |||||
| { | { | ||||
| [webView setNavigationDelegate: nil]; | |||||
| [webView setUIDelegate: nil]; | |||||
| [webViewDelegate release]; | |||||
| [webView setPolicyDelegate: nil]; | |||||
| [webView setFrameLoadDelegate: nil]; | |||||
| [webView setUIDelegate: nil]; | |||||
| setView (nil); | |||||
| [clickListener release]; | |||||
| } | } | ||||
| void goToURL (const String& url, | void goToURL (const String& url, | ||||
| const StringArray* headers, | const StringArray* headers, | ||||
| const MemoryBlock* postData) | |||||
| const MemoryBlock* postData) override | |||||
| { | { | ||||
| auto trimmed = url.trimStart(); | |||||
| if (trimmed.startsWithIgnoreCase ("javascript:")) | |||||
| if (url.trimStart().startsWithIgnoreCase ("javascript:")) | |||||
| { | { | ||||
| [webView evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false)) | |||||
| completionHandler: nil]; | |||||
| [webView stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))]; | |||||
| return; | return; | ||||
| } | } | ||||
| stop(); | stop(); | ||||
| if (trimmed.startsWithIgnoreCase ("file:")) | |||||
| auto getRequest = [&]() -> NSMutableURLRequest* | |||||
| { | { | ||||
| auto file = URL (url).getLocalFile(); | |||||
| if (url.trimStart().startsWithIgnoreCase ("file:")) | |||||
| { | |||||
| auto file = URL (url).getLocalFile(); | |||||
| if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]) | |||||
| [webView loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl]; | |||||
| } | |||||
| else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData)) | |||||
| { | |||||
| [webView loadRequest: request]; | |||||
| } | |||||
| } | |||||
| if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]) | |||||
| return [NSMutableURLRequest requestWithURL: appendParametersToFileURL (url, nsUrl) | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: 30.0]; | |||||
| void goBack() { [webView goBack]; } | |||||
| void goForward() { [webView goForward]; } | |||||
| return nullptr; | |||||
| } | |||||
| void stop() { [webView stopLoading]; } | |||||
| void refresh() { [webView reload]; } | |||||
| return getRequestForURL (url, headers, postData); | |||||
| }; | |||||
| private: | |||||
| WKWebView* webView = nil; | |||||
| id webViewDelegate; | |||||
| }; | |||||
| if (NSMutableURLRequest* request = getRequest()) | |||||
| [[webView mainFrame] loadRequest: request]; | |||||
| } | |||||
| #else | |||||
| void goBack() override { [webView goBack]; } | |||||
| void goForward() override { [webView goForward]; } | |||||
| #if JUCE_MAC | |||||
| void stop() override { [webView stopLoading: nil]; } | |||||
| void refresh() override { [webView reload: nil]; } | |||||
| struct DownloadClickDetectorClass : public ObjCClass<NSObject> | |||||
| { | |||||
| DownloadClickDetectorClass() : ObjCClass<NSObject> ("JUCEWebClickDetector_") | |||||
| { | |||||
| addIvar<WebBrowserComponent*> ("owner"); | |||||
| id getWebView() override { return webView; } | |||||
| addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:), | |||||
| decidePolicyForNavigationAction, "v@:@@@@@"); | |||||
| addMethod (@selector (webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:), | |||||
| decidePolicyForNewWindowAction, "v@:@@@@@"); | |||||
| addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@"); | |||||
| addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); | |||||
| addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); | |||||
| addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@"); | |||||
| addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), runOpenPanel, "v@:@@", @encode (BOOL)); | |||||
| registerClass(); | |||||
| void mouseMove (const MouseEvent&) | |||||
| { | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||||
| // WebKit doesn't capture mouse-moves itself, so it seems the only way to make | |||||
| // them work is to push them via this non-public method.. | |||||
| if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)]) | |||||
| [webView performSelector: @selector (_updateMouseoverWithFakeEvent)]; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | } | ||||
| static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); } | |||||
| static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); } | |||||
| private: | private: | ||||
| static String getOriginalURL (NSDictionary* actionInformation) | |||||
| { | |||||
| if (NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")]) | |||||
| return nsStringToJuce ([url absoluteString]); | |||||
| return {}; | |||||
| } | |||||
| WebView* webView = nil; | |||||
| id clickListener; | |||||
| }; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | |||||
| static void decidePolicyForNavigationAction (id self, SEL, WebView*, NSDictionary* actionInformation, | |||||
| NSURLRequest*, WebFrame*, id<WebPolicyDecisionListener> listener) | |||||
| class WKWebViewImpl : public WebViewBase | |||||
| { | |||||
| public: | |||||
| WKWebViewImpl (WebBrowserComponent* owner) | |||||
| { | { | ||||
| if (getOwner (self)->pageAboutToLoad (getOriginalURL (actionInformation))) | |||||
| [listener use]; | |||||
| else | |||||
| [listener ignore]; | |||||
| } | |||||
| #if JUCE_MAC | |||||
| static WebViewKeyEquivalentResponder<WKWebView> webviewClass; | |||||
| webView = (WKWebView*) webviewClass.createInstance(); | |||||
| static void decidePolicyForNewWindowAction (id self, SEL, WebView*, NSDictionary* actionInformation, | |||||
| NSURLRequest*, NSString*, id<WebPolicyDecisionListener> listener) | |||||
| { | |||||
| getOwner (self)->newWindowAttemptingToLoad (getOriginalURL (actionInformation)); | |||||
| [listener ignore]; | |||||
| } | |||||
| webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]; | |||||
| #else | |||||
| webView = [[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)]; | |||||
| #endif | |||||
| static void didFinishLoadForFrame (id self, SEL, WebView* sender, WebFrame* frame) | |||||
| { | |||||
| if ([frame isEqual: [sender mainFrame]]) | |||||
| { | |||||
| NSURL* url = [[[frame dataSource] request] URL]; | |||||
| getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString])); | |||||
| } | |||||
| static WebViewDelegateClass cls; | |||||
| webViewDelegate = [cls.createInstance() init]; | |||||
| WebViewDelegateClass::setOwner (webViewDelegate, owner); | |||||
| [webView setNavigationDelegate: webViewDelegate]; | |||||
| [webView setUIDelegate: webViewDelegate]; | |||||
| } | } | ||||
| static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, WebFrame* frame) | |||||
| ~WKWebViewImpl() override | |||||
| { | { | ||||
| if ([frame isEqual: [sender mainFrame]] && error != nullptr && [error code] != NSURLErrorCancelled) | |||||
| { | |||||
| auto errorString = nsStringToJuce ([error localizedDescription]); | |||||
| bool proceedToErrorPage = getOwner (self)->pageLoadHadNetworkError (errorString); | |||||
| [webView setNavigationDelegate: nil]; | |||||
| [webView setUIDelegate: nil]; | |||||
| // WebKit doesn't have an internal error page, so make a really simple one ourselves | |||||
| if (proceedToErrorPage) | |||||
| getOwner (self)->goToURL ("data:text/plain;charset=UTF-8," + errorString); | |||||
| } | |||||
| [webViewDelegate release]; | |||||
| } | } | ||||
| static void willCloseFrame (id self, SEL, WebView*, WebFrame*) | |||||
| void goToURL (const String& url, | |||||
| const StringArray* headers, | |||||
| const MemoryBlock* postData) override | |||||
| { | { | ||||
| getOwner (self)->windowCloseRequest(); | |||||
| } | |||||
| auto trimmed = url.trimStart(); | |||||
| static void runOpenPanel (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles) | |||||
| { | |||||
| struct DeletedFileChooserWrapper : private DeletedAtShutdown | |||||
| if (trimmed.startsWithIgnoreCase ("javascript:")) | |||||
| { | { | ||||
| DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, id<WebOpenPanelResultListener> rl) | |||||
| : chooser (std::move (fc)), listener (rl) | |||||
| { | |||||
| [listener retain]; | |||||
| } | |||||
| ~DeletedFileChooserWrapper() | |||||
| { | |||||
| [listener release]; | |||||
| } | |||||
| std::unique_ptr<FileChooser> chooser; | |||||
| id<WebOpenPanelResultListener> listener; | |||||
| }; | |||||
| [webView evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false)) | |||||
| completionHandler: nil]; | |||||
| auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."), | |||||
| File::getSpecialLocation (File::userHomeDirectory), "*"); | |||||
| auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener); | |||||
| return; | |||||
| } | |||||
| auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles | |||||
| | (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0); | |||||
| stop(); | |||||
| wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&) | |||||
| if (trimmed.startsWithIgnoreCase ("file:")) | |||||
| { | { | ||||
| for (auto& f : wrapper->chooser->getResults()) | |||||
| [wrapper->listener chooseFilename: juceStringToNS (f.getFullPathName())]; | |||||
| auto file = URL (url).getLocalFile(); | |||||
| delete wrapper; | |||||
| }); | |||||
| if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]) | |||||
| [webView loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl]; | |||||
| } | |||||
| else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData)) | |||||
| { | |||||
| [webView loadRequest: request]; | |||||
| } | |||||
| } | } | ||||
| }; | |||||
| #else | |||||
| struct WebViewDelegateClass : public ObjCClass<NSObject> | |||||
| { | |||||
| WebViewDelegateClass() : ObjCClass<NSObject> ("JUCEWebViewDelegate_") | |||||
| { | |||||
| addIvar<WebBrowserComponent*> ("owner"); | |||||
| void goBack() override { [webView goBack]; } | |||||
| void goForward() override { [webView goForward]; } | |||||
| addMethod (@selector (gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:), | |||||
| shouldRecognizeSimultaneouslyWithGestureRecognizer, "c@:@@"); | |||||
| void stop() override { [webView stopLoading]; } | |||||
| void refresh() override { [webView reload]; } | |||||
| addMethod (@selector (webView:shouldStartLoadWithRequest:navigationType:), shouldStartLoadWithRequest, "c@:@@@"); | |||||
| addMethod (@selector (webViewDidFinishLoad:), webViewDidFinishLoad, "v@:@"); | |||||
| registerClass(); | |||||
| } | |||||
| static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); } | |||||
| static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); } | |||||
| id getWebView() override { return webView; } | |||||
| private: | private: | ||||
| static BOOL shouldRecognizeSimultaneouslyWithGestureRecognizer (id, SEL, UIGestureRecognizer*, UIGestureRecognizer*) | |||||
| { | |||||
| return YES; | |||||
| } | |||||
| static BOOL shouldStartLoadWithRequest (id self, SEL, UIWebView*, NSURLRequest* request, UIWebViewNavigationType) | |||||
| { | |||||
| return getOwner (self)->pageAboutToLoad (nsStringToJuce ([[request URL] absoluteString])); | |||||
| } | |||||
| static void webViewDidFinishLoad (id self, SEL, UIWebView* webView) | |||||
| { | |||||
| getOwner (self)->pageFinishedLoading (nsStringToJuce ([[[webView request] URL] absoluteString])); | |||||
| } | |||||
| WKWebView* webView = nil; | |||||
| id webViewDelegate; | |||||
| }; | }; | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| class WebBrowserComponent::Pimpl | class WebBrowserComponent::Pimpl | ||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| @@ -549,47 +601,19 @@ class WebBrowserComponent::Pimpl | |||||
| public: | public: | ||||
| Pimpl (WebBrowserComponent* owner) | Pimpl (WebBrowserComponent* owner) | ||||
| { | { | ||||
| if (@available (macOS 10.10, *)) | |||||
| webView = std::make_unique<WKWebViewImpl> (owner); | |||||
| #if JUCE_MAC | #if JUCE_MAC | ||||
| static WebViewKeyEquivalentResponder webviewClass; | |||||
| webView = (WebView*) webviewClass.createInstance(); | |||||
| webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f) | |||||
| frameName: nsEmptyString() | |||||
| groupName: nsEmptyString()]; | |||||
| static DownloadClickDetectorClass cls; | |||||
| clickListener = [cls.createInstance() init]; | |||||
| DownloadClickDetectorClass::setOwner (clickListener, owner); | |||||
| [webView setPolicyDelegate: clickListener]; | |||||
| [webView setFrameLoadDelegate: clickListener]; | |||||
| [webView setUIDelegate: clickListener]; | |||||
| #else | |||||
| webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)]; | |||||
| static WebViewDelegateClass cls; | |||||
| webViewDelegate = [cls.createInstance() init]; | |||||
| WebViewDelegateClass::setOwner (webViewDelegate, owner); | |||||
| [webView setDelegate: webViewDelegate]; | |||||
| else | |||||
| webView = std::make_unique<WebViewImpl> (owner); | |||||
| #endif | #endif | ||||
| setView (webView); | |||||
| setView (webView->getWebView()); | |||||
| } | } | ||||
| ~Pimpl() | ~Pimpl() | ||||
| { | { | ||||
| #if JUCE_MAC | |||||
| [webView setPolicyDelegate: nil]; | |||||
| [webView setFrameLoadDelegate: nil]; | |||||
| [webView setUIDelegate: nil]; | |||||
| [clickListener release]; | |||||
| #else | |||||
| [webView setDelegate: nil]; | |||||
| [webViewDelegate release]; | |||||
| #endif | |||||
| webView = nullptr; | |||||
| setView (nil); | setView (nil); | ||||
| } | } | ||||
| @@ -597,78 +621,19 @@ public: | |||||
| const StringArray* headers, | const StringArray* headers, | ||||
| const MemoryBlock* postData) | const MemoryBlock* postData) | ||||
| { | { | ||||
| if (url.trimStart().startsWithIgnoreCase ("javascript:")) | |||||
| { | |||||
| [webView stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))]; | |||||
| return; | |||||
| } | |||||
| stop(); | |||||
| auto getRequest = [&]() -> NSMutableURLRequest* | |||||
| { | |||||
| if (url.trimStart().startsWithIgnoreCase ("file:")) | |||||
| { | |||||
| auto file = URL (url).getLocalFile(); | |||||
| if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())]) | |||||
| return [NSMutableURLRequest requestWithURL: appendParametersToFileURL (url, nsUrl) | |||||
| cachePolicy: NSURLRequestUseProtocolCachePolicy | |||||
| timeoutInterval: 30.0]; | |||||
| return nullptr; | |||||
| } | |||||
| return getRequestForURL (url, headers, postData); | |||||
| }; | |||||
| if (NSMutableURLRequest* request = getRequest()) | |||||
| { | |||||
| #if JUCE_MAC | |||||
| [[webView mainFrame] loadRequest: request]; | |||||
| #else | |||||
| [webView loadRequest: request]; | |||||
| #endif | |||||
| #if JUCE_IOS | |||||
| [webView setScalesPageToFit: YES]; | |||||
| #endif | |||||
| } | |||||
| webView->goToURL (url, headers, postData); | |||||
| } | } | ||||
| void goBack() { [webView goBack]; } | |||||
| void goForward() { [webView goForward]; } | |||||
| #if JUCE_MAC | |||||
| void stop() { [webView stopLoading: nil]; } | |||||
| void refresh() { [webView reload: nil]; } | |||||
| #else | |||||
| void stop() { [webView stopLoading]; } | |||||
| void refresh() { [webView reload]; } | |||||
| #endif | |||||
| void goBack() { webView->goBack(); } | |||||
| void goForward() { webView->goForward(); } | |||||
| void mouseMove (const MouseEvent&) | |||||
| { | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector") | |||||
| // WebKit doesn't capture mouse-moves itself, so it seems the only way to make | |||||
| // them work is to push them via this non-public method.. | |||||
| if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)]) | |||||
| [webView performSelector: @selector (_updateMouseoverWithFakeEvent)]; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | |||||
| void stop() { webView->stop(); } | |||||
| void refresh() { webView->refresh(); } | |||||
| private: | private: | ||||
| #if JUCE_MAC | |||||
| WebView* webView = nil; | |||||
| id clickListener; | |||||
| #else | |||||
| UIWebView* webView = nil; | |||||
| id webViewDelegate; | |||||
| #endif | |||||
| std::unique_ptr<WebViewBase> webView; | |||||
| }; | }; | ||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden) | WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden) | ||||
| : unloadPageWhenHidden (unloadWhenHidden) | : unloadPageWhenHidden (unloadWhenHidden) | ||||
| @@ -206,11 +206,7 @@ public: | |||||
| jassert (isPositiveAndBelow (numFramesPerSwap, 2)); | jassert (isPositiveAndBelow (numFramesPerSwap, 2)); | ||||
| [renderContext setValues: (const GLint*) &numFramesPerSwap | [renderContext setValues: (const GLint*) &numFramesPerSwap | ||||
| #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 | |||||
| forParameter: NSOpenGLContextParameterSwapInterval]; | |||||
| #else | |||||
| forParameter: NSOpenGLCPSwapInterval]; | |||||
| #endif | |||||
| forParameter: getSwapIntervalParameter()]; | |||||
| updateMinSwapTime(); | updateMinSwapTime(); | ||||
| @@ -221,11 +217,7 @@ public: | |||||
| { | { | ||||
| GLint numFrames = 0; | GLint numFrames = 0; | ||||
| [renderContext getValues: &numFrames | [renderContext getValues: &numFrames | ||||
| #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 | |||||
| forParameter: NSOpenGLContextParameterSwapInterval]; | |||||
| #else | |||||
| forParameter: NSOpenGLCPSwapInterval]; | |||||
| #endif | |||||
| forParameter: getSwapIntervalParameter()]; | |||||
| return numFrames; | return numFrames; | ||||
| } | } | ||||
| @@ -242,6 +234,16 @@ public: | |||||
| minSwapTimeMs = static_cast<int> (numFramesPerSwap * 1000 * videoRefreshPeriodS); | minSwapTimeMs = static_cast<int> (numFramesPerSwap * 1000 * videoRefreshPeriodS); | ||||
| } | } | ||||
| static NSOpenGLContextParameter getSwapIntervalParameter() | |||||
| { | |||||
| #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 | |||||
| if (@available (macOS 10.12, *)) | |||||
| return NSOpenGLContextParameterSwapInterval; | |||||
| #endif | |||||
| return NSOpenGLCPSwapInterval; | |||||
| } | |||||
| NSOpenGLContext* renderContext = nil; | NSOpenGLContext* renderContext = nil; | ||||
| NSOpenGLView* view = nil; | NSOpenGLView* view = nil; | ||||
| ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment; | ReferenceCountedObjectPtr<ReferenceCountedObject> viewAttachment; | ||||
| @@ -31,7 +31,7 @@ namespace juce | |||||
| #elif JUCE_WINDOWS | #elif JUCE_WINDOWS | ||||
| #include "../native/juce_win32_CameraDevice.h" | #include "../native/juce_win32_CameraDevice.h" | ||||
| #elif JUCE_IOS | #elif JUCE_IOS | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability-new") | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| #include "../native/juce_ios_CameraDevice.h" | #include "../native/juce_ios_CameraDevice.h" | ||||
| @@ -23,9 +23,9 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| #if (defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) | |||||
| #if (defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0) | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | ||||
| #define JUCE_DEPRECATION_IGNORED 1 | |||||
| #define JUCE_USE_NEW_CAMERA_API 1 | |||||
| #endif | #endif | ||||
| struct CameraDevice::Pimpl | struct CameraDevice::Pimpl | ||||
| @@ -141,7 +141,7 @@ struct CameraDevice::Pimpl | |||||
| private: | private: | ||||
| static NSArray<AVCaptureDevice*>* getDevices() | static NSArray<AVCaptureDevice*>* getDevices() | ||||
| { | { | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | { | ||||
| std::unique_ptr<NSMutableArray<AVCaptureDeviceType>, NSObjectDeleter> deviceTypes ([[NSMutableArray alloc] initWithCapacity: 2]); | std::unique_ptr<NSMutableArray<AVCaptureDeviceType>, NSObjectDeleter> deviceTypes ([[NSMutableArray alloc] initWithCapacity: 2]); | ||||
| @@ -207,7 +207,7 @@ private: | |||||
| JUCE_CAMERA_LOG ("Supports custom exposure: " + String ((int)[device isExposureModeSupported: AVCaptureExposureModeCustom])); | JUCE_CAMERA_LOG ("Supports custom exposure: " + String ((int)[device isExposureModeSupported: AVCaptureExposureModeCustom])); | ||||
| JUCE_CAMERA_LOG ("Supports point of interest exposure: " + String ((int)device.exposurePointOfInterestSupported)); | JUCE_CAMERA_LOG ("Supports point of interest exposure: " + String ((int)device.exposurePointOfInterestSupported)); | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | { | ||||
| JUCE_CAMERA_LOG ("Device type: " + nsStringToJuce (device.deviceType)); | JUCE_CAMERA_LOG ("Device type: " + nsStringToJuce (device.deviceType)); | ||||
| @@ -238,7 +238,7 @@ private: | |||||
| { | { | ||||
| JUCE_CAMERA_LOG ("Media type: " + nsStringToJuce (format.mediaType)); | JUCE_CAMERA_LOG ("Media type: " + nsStringToJuce (format.mediaType)); | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | { | ||||
| String colourSpaces; | String colourSpaces; | ||||
| @@ -592,12 +592,14 @@ private: | |||||
| captureOutput (createCaptureOutput()), | captureOutput (createCaptureOutput()), | ||||
| photoOutputDelegate (nullptr) | photoOutputDelegate (nullptr) | ||||
| { | { | ||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | { | ||||
| static PhotoOutputDelegateClass cls; | static PhotoOutputDelegateClass cls; | ||||
| photoOutputDelegate.reset ([cls.createInstance() init]); | photoOutputDelegate.reset ([cls.createInstance() init]); | ||||
| PhotoOutputDelegateClass::setOwner (photoOutputDelegate.get(), this); | PhotoOutputDelegateClass::setOwner (photoOutputDelegate.get(), this); | ||||
| } | } | ||||
| #endif | |||||
| captureSession.addOutputIfPossible (captureOutput); | captureSession.addOutputIfPossible (captureOutput); | ||||
| } | } | ||||
| @@ -617,9 +619,9 @@ private: | |||||
| if (auto* connection = findVideoConnection (captureOutput)) | if (auto* connection = findVideoConnection (captureOutput)) | ||||
| { | { | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | |||||
| { | |||||
| if ([captureOutput isKindOfClass: [AVCapturePhotoOutput class]]) | if ([captureOutput isKindOfClass: [AVCapturePhotoOutput class]]) | ||||
| { | { | ||||
| auto* photoOutput = (AVCapturePhotoOutput*) captureOutput; | auto* photoOutput = (AVCapturePhotoOutput*) captureOutput; | ||||
| @@ -669,7 +671,7 @@ private: | |||||
| private: | private: | ||||
| static AVCaptureOutput* createCaptureOutput() | static AVCaptureOutput* createCaptureOutput() | ||||
| { | { | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| return [AVCapturePhotoOutput new]; | return [AVCapturePhotoOutput new]; | ||||
| #endif | #endif | ||||
| @@ -679,7 +681,7 @@ private: | |||||
| static void printImageOutputDebugInfo (AVCaptureOutput* captureOutput) | static void printImageOutputDebugInfo (AVCaptureOutput* captureOutput) | ||||
| { | { | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| { | { | ||||
| if ([captureOutput isKindOfClass: [AVCapturePhotoOutput class]]) | if ([captureOutput isKindOfClass: [AVCapturePhotoOutput class]]) | ||||
| @@ -688,7 +690,7 @@ private: | |||||
| String typesString; | String typesString; | ||||
| for (AVVideoCodecType type in photoOutput.availablePhotoCodecTypes) | |||||
| for (id type in photoOutput.availablePhotoCodecTypes) | |||||
| typesString << nsStringToJuce (type) << " "; | typesString << nsStringToJuce (type) << " "; | ||||
| JUCE_CAMERA_LOG ("Available image codec types: " + typesString); | JUCE_CAMERA_LOG ("Available image codec types: " + typesString); | ||||
| @@ -741,7 +743,7 @@ private: | |||||
| String typesString; | String typesString; | ||||
| for (AVVideoCodecType type in stillImageOutput.availableImageDataCodecTypes) | |||||
| for (id type in stillImageOutput.availableImageDataCodecTypes) | |||||
| typesString << nsStringToJuce (type) << " "; | typesString << nsStringToJuce (type) << " "; | ||||
| JUCE_CAMERA_LOG ("Available image codec types: " + typesString); | JUCE_CAMERA_LOG ("Available image codec types: " + typesString); | ||||
| @@ -763,8 +765,7 @@ private: | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| class PhotoOutputDelegateClass : public ObjCClass<NSObject> | class PhotoOutputDelegateClass : public ObjCClass<NSObject> | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -974,8 +975,7 @@ private: | |||||
| } | } | ||||
| } | } | ||||
| }; | }; | ||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | |||||
| //============================================================================== | //============================================================================== | ||||
| void callListeners (const Image& image) | void callListeners (const Image& image) | ||||
| @@ -1026,7 +1026,7 @@ private: | |||||
| void startRecording (const File& file, AVCaptureVideoOrientation orientationToUse) | void startRecording (const File& file, AVCaptureVideoOrientation orientationToUse) | ||||
| { | { | ||||
| #if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (iOS 10.0, *)) | if (@available (iOS 10.0, *)) | ||||
| printVideoOutputDebugInfo (movieFileOutput); | printVideoOutputDebugInfo (movieFileOutput); | ||||
| #endif | #endif | ||||
| @@ -1058,7 +1058,7 @@ private: | |||||
| JUCE_CAMERA_LOG ("Available video codec types:"); | JUCE_CAMERA_LOG ("Available video codec types:"); | ||||
| #if JUCE_CAMERA_LOG_ENABLED | #if JUCE_CAMERA_LOG_ENABLED | ||||
| for (AVVideoCodecType type in output.availableVideoCodecTypes) | |||||
| for (id type in output.availableVideoCodecTypes) | |||||
| JUCE_CAMERA_LOG (nsStringToJuce (type)); | JUCE_CAMERA_LOG (nsStringToJuce (type)); | ||||
| #endif | #endif | ||||
| @@ -1335,6 +1335,6 @@ String CameraDevice::getFileExtension() | |||||
| return ".mov"; | return ".mov"; | ||||
| } | } | ||||
| #if JUCE_DEPRECATION_IGNORED | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||||
| #endif | #endif | ||||
| @@ -23,180 +23,12 @@ | |||||
| ============================================================================== | ============================================================================== | ||||
| */ | */ | ||||
| #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15 | |||||
| #define JUCE_USE_NEW_CAMERA_API 1 | |||||
| #endif | |||||
| struct CameraDevice::Pimpl | struct CameraDevice::Pimpl | ||||
| { | { | ||||
| #if defined (MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15 | |||||
| #define JUCE_USE_NEW_APPLE_CAMERA_API 1 | |||||
| #else | |||||
| #define JUCE_USE_NEW_APPLE_CAMERA_API 0 | |||||
| #endif | |||||
| #if JUCE_USE_NEW_APPLE_CAMERA_API | |||||
| class PostCatalinaPhotoOutput | |||||
| { | |||||
| public: | |||||
| PostCatalinaPhotoOutput() | |||||
| { | |||||
| static PhotoOutputDelegateClass cls; | |||||
| delegate.reset ([cls.createInstance() init]); | |||||
| } | |||||
| void addImageCapture (AVCaptureSession* s) | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return; | |||||
| imageOutput = [[AVCapturePhotoOutput alloc] init]; | |||||
| [s addOutput: imageOutput]; | |||||
| } | |||||
| void removeImageCapture (AVCaptureSession* s) | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| [s removeOutput: imageOutput]; | |||||
| [imageOutput release]; | |||||
| imageOutput = nil; | |||||
| } | |||||
| NSArray<AVCaptureConnection*>* getConnections() const | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return imageOutput.connections; | |||||
| return nil; | |||||
| } | |||||
| void triggerImageCapture (Pimpl& p) | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| PhotoOutputDelegateClass::setOwner (delegate.get(), &p); | |||||
| [imageOutput capturePhotoWithSettings: [AVCapturePhotoSettings photoSettings] | |||||
| delegate: id<AVCapturePhotoCaptureDelegate> (delegate.get())]; | |||||
| } | |||||
| static NSArray* getAvailableDevices() | |||||
| { | |||||
| auto* discovery = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes: @[AVCaptureDeviceTypeBuiltInWideAngleCamera, | |||||
| AVCaptureDeviceTypeExternalUnknown] | |||||
| mediaType: AVMediaTypeVideo | |||||
| position: AVCaptureDevicePositionUnspecified]; | |||||
| return [discovery devices]; | |||||
| } | |||||
| private: | |||||
| class PhotoOutputDelegateClass : public ObjCClass<NSObject> | |||||
| { | |||||
| public: | |||||
| PhotoOutputDelegateClass() : ObjCClass<NSObject> ("PhotoOutputDelegateClass_") | |||||
| { | |||||
| addMethod (@selector (captureOutput:didFinishProcessingPhoto:error:), didFinishProcessingPhoto, "v@:@@@"); | |||||
| addIvar<Pimpl*> ("owner"); | |||||
| registerClass(); | |||||
| } | |||||
| static void didFinishProcessingPhoto (id self, SEL, AVCapturePhotoOutput*, AVCapturePhoto* photo, NSError* error) | |||||
| { | |||||
| if (error != nil) | |||||
| { | |||||
| String errorString = error != nil ? nsStringToJuce (error.localizedDescription) : String(); | |||||
| ignoreUnused (errorString); | |||||
| JUCE_CAMERA_LOG ("Still picture capture failed, error: " + errorString); | |||||
| jassertfalse; | |||||
| return; | |||||
| } | |||||
| auto* imageData = [photo fileDataRepresentation]; | |||||
| auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length); | |||||
| getOwner (self).imageCaptureFinished (image); | |||||
| } | |||||
| static Pimpl& getOwner (id self) { return *getIvar<Pimpl*> (self, "owner"); } | |||||
| static void setOwner (id self, Pimpl* t) { object_setInstanceVariable (self, "owner", t); } | |||||
| }; | |||||
| AVCapturePhotoOutput* imageOutput = nil; | |||||
| std::unique_ptr<NSObject, NSObjectDeleter> delegate; | |||||
| }; | |||||
| #else | |||||
| struct PreCatalinaStillImageOutput | |||||
| { | |||||
| public: | |||||
| void addImageCapture (AVCaptureSession* s) | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return; | |||||
| const auto codecType = | |||||
| #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13 | |||||
| AVVideoCodecTypeJPEG; | |||||
| #else | |||||
| AVVideoCodecJPEG; | |||||
| #endif | |||||
| imageOutput = [[AVCaptureStillImageOutput alloc] init]; | |||||
| auto imageSettings = [[NSDictionary alloc] initWithObjectsAndKeys: codecType, AVVideoCodecKey, nil]; | |||||
| [imageOutput setOutputSettings: imageSettings]; | |||||
| [imageSettings release]; | |||||
| [s addOutput: imageOutput]; | |||||
| } | |||||
| void removeImageCapture (AVCaptureSession* s) | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| [s removeOutput: imageOutput]; | |||||
| [imageOutput release]; | |||||
| imageOutput = nil; | |||||
| } | |||||
| NSArray<AVCaptureConnection*>* getConnections() const | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return imageOutput.connections; | |||||
| return nil; | |||||
| } | |||||
| void triggerImageCapture (Pimpl& p) | |||||
| { | |||||
| if (auto* videoConnection = p.getVideoConnection()) | |||||
| { | |||||
| [imageOutput captureStillImageAsynchronouslyFromConnection: videoConnection | |||||
| completionHandler: ^(CMSampleBufferRef sampleBuffer, NSError* error) | |||||
| { | |||||
| if (error != nil) | |||||
| { | |||||
| JUCE_CAMERA_LOG ("Still picture capture failed, error: " + nsStringToJuce (error.localizedDescription)); | |||||
| jassertfalse; | |||||
| return; | |||||
| } | |||||
| auto* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: sampleBuffer]; | |||||
| auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length); | |||||
| p.imageCaptureFinished (image); | |||||
| }]; | |||||
| } | |||||
| } | |||||
| static NSArray* getAvailableDevices() | |||||
| { | |||||
| return [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | |||||
| } | |||||
| private: | |||||
| AVCaptureStillImageOutput* imageOutput = nil; | |||||
| }; | |||||
| #endif | |||||
| Pimpl (CameraDevice& ownerToUse, const String& deviceNameToUse, int /*index*/, | Pimpl (CameraDevice& ownerToUse, const String& deviceNameToUse, int /*index*/, | ||||
| int /*minWidth*/, int /*minHeight*/, | int /*minWidth*/, int /*minHeight*/, | ||||
| int /*maxWidth*/, int /*maxHeight*/, | int /*maxWidth*/, int /*maxHeight*/, | ||||
| @@ -204,6 +36,16 @@ struct CameraDevice::Pimpl | |||||
| : owner (ownerToUse), | : owner (ownerToUse), | ||||
| deviceName (deviceNameToUse) | deviceName (deviceNameToUse) | ||||
| { | { | ||||
| imageOutput = []() -> std::unique_ptr<ImageOutputBase> | |||||
| { | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (macOS 10.15, *)) | |||||
| return std::make_unique<PostCatalinaPhotoOutput>(); | |||||
| #endif | |||||
| return std::make_unique<PreCatalinaStillImageOutput>(); | |||||
| }(); | |||||
| session = [[AVCaptureSession alloc] init]; | session = [[AVCaptureSession alloc] init]; | ||||
| session.sessionPreset = useHighQuality ? AVCaptureSessionPresetHigh | session.sessionPreset = useHighQuality ? AVCaptureSessionPresetHigh | ||||
| @@ -299,13 +141,30 @@ struct CameraDevice::Pimpl | |||||
| listeners.remove (listenerToRemove); | listeners.remove (listenerToRemove); | ||||
| } | } | ||||
| static StringArray getAvailableDevices() | |||||
| static NSArray* getCaptureDevices() | |||||
| { | { | ||||
| auto* devices = decltype (imageOutput)::getAvailableDevices(); | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| if (@available (macOS 10.15, *)) | |||||
| { | |||||
| auto* discovery = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes: @[AVCaptureDeviceTypeBuiltInWideAngleCamera, | |||||
| AVCaptureDeviceTypeExternalUnknown] | |||||
| mediaType: AVMediaTypeVideo | |||||
| position: AVCaptureDevicePositionUnspecified]; | |||||
| return [discovery devices]; | |||||
| } | |||||
| #endif | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| return [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| } | |||||
| static StringArray getAvailableDevices() | |||||
| { | |||||
| StringArray results; | StringArray results; | ||||
| for (AVCaptureDevice* device : devices) | |||||
| for (AVCaptureDevice* device : getCaptureDevices()) | |||||
| results.add (nsStringToJuce ([device localizedName])); | results.add (nsStringToJuce ([device localizedName])); | ||||
| return results; | return results; | ||||
| @@ -373,10 +232,180 @@ private: | |||||
| } | } | ||||
| }; | }; | ||||
| //============================================================================== | |||||
| struct ImageOutputBase | |||||
| { | |||||
| virtual ~ImageOutputBase() = default; | |||||
| virtual void addImageCapture (AVCaptureSession*) = 0; | |||||
| virtual void removeImageCapture (AVCaptureSession*) = 0; | |||||
| virtual NSArray<AVCaptureConnection*>* getConnections() const = 0; | |||||
| virtual void triggerImageCapture (Pimpl& p) = 0; | |||||
| }; | |||||
| #if JUCE_USE_NEW_CAMERA_API | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new") | |||||
| class PostCatalinaPhotoOutput : public ImageOutputBase | |||||
| { | |||||
| public: | |||||
| PostCatalinaPhotoOutput() | |||||
| { | |||||
| static PhotoOutputDelegateClass cls; | |||||
| delegate.reset ([cls.createInstance() init]); | |||||
| } | |||||
| void addImageCapture (AVCaptureSession* s) override | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return; | |||||
| imageOutput = [[AVCapturePhotoOutput alloc] init]; | |||||
| [s addOutput: imageOutput]; | |||||
| } | |||||
| void removeImageCapture (AVCaptureSession* s) override | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| [s removeOutput: imageOutput]; | |||||
| [imageOutput release]; | |||||
| imageOutput = nil; | |||||
| } | |||||
| NSArray<AVCaptureConnection*>* getConnections() const override | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return imageOutput.connections; | |||||
| return nil; | |||||
| } | |||||
| void triggerImageCapture (Pimpl& p) override | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| PhotoOutputDelegateClass::setOwner (delegate.get(), &p); | |||||
| [imageOutput capturePhotoWithSettings: [AVCapturePhotoSettings photoSettings] | |||||
| delegate: id<AVCapturePhotoCaptureDelegate> (delegate.get())]; | |||||
| } | |||||
| private: | |||||
| class PhotoOutputDelegateClass : public ObjCClass<NSObject> | |||||
| { | |||||
| public: | |||||
| PhotoOutputDelegateClass() : ObjCClass<NSObject> ("PhotoOutputDelegateClass_") | |||||
| { | |||||
| addMethod (@selector (captureOutput:didFinishProcessingPhoto:error:), didFinishProcessingPhoto, "v@:@@@"); | |||||
| addIvar<Pimpl*> ("owner"); | |||||
| registerClass(); | |||||
| } | |||||
| static void didFinishProcessingPhoto (id self, SEL, AVCapturePhotoOutput*, AVCapturePhoto* photo, NSError* error) | |||||
| { | |||||
| if (error != nil) | |||||
| { | |||||
| String errorString = error != nil ? nsStringToJuce (error.localizedDescription) : String(); | |||||
| ignoreUnused (errorString); | |||||
| JUCE_CAMERA_LOG ("Still picture capture failed, error: " + errorString); | |||||
| jassertfalse; | |||||
| return; | |||||
| } | |||||
| auto* imageData = [photo fileDataRepresentation]; | |||||
| auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length); | |||||
| getOwner (self).imageCaptureFinished (image); | |||||
| } | |||||
| static Pimpl& getOwner (id self) { return *getIvar<Pimpl*> (self, "owner"); } | |||||
| static void setOwner (id self, Pimpl* t) { object_setInstanceVariable (self, "owner", t); } | |||||
| }; | |||||
| AVCapturePhotoOutput* imageOutput = nil; | |||||
| std::unique_ptr<NSObject, NSObjectDeleter> delegate; | |||||
| }; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| #endif | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations") | |||||
| class PreCatalinaStillImageOutput : public ImageOutputBase | |||||
| { | |||||
| public: | |||||
| void addImageCapture (AVCaptureSession* s) override | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return; | |||||
| const auto codecType = [] | |||||
| { | |||||
| #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 | |||||
| if (@available (macOS 10.13, *)) | |||||
| return AVVideoCodecTypeJPEG; | |||||
| #endif | |||||
| return AVVideoCodecJPEG; | |||||
| }(); | |||||
| imageOutput = [[AVCaptureStillImageOutput alloc] init]; | |||||
| auto imageSettings = [[NSDictionary alloc] initWithObjectsAndKeys: codecType, AVVideoCodecKey, nil]; | |||||
| [imageOutput setOutputSettings: imageSettings]; | |||||
| [imageSettings release]; | |||||
| [s addOutput: imageOutput]; | |||||
| } | |||||
| void removeImageCapture (AVCaptureSession* s) override | |||||
| { | |||||
| if (imageOutput == nil) | |||||
| return; | |||||
| [s removeOutput: imageOutput]; | |||||
| [imageOutput release]; | |||||
| imageOutput = nil; | |||||
| } | |||||
| NSArray<AVCaptureConnection*>* getConnections() const override | |||||
| { | |||||
| if (imageOutput != nil) | |||||
| return imageOutput.connections; | |||||
| return nil; | |||||
| } | |||||
| void triggerImageCapture (Pimpl& p) override | |||||
| { | |||||
| if (auto* videoConnection = p.getVideoConnection()) | |||||
| { | |||||
| [imageOutput captureStillImageAsynchronouslyFromConnection: videoConnection | |||||
| completionHandler: ^(CMSampleBufferRef sampleBuffer, NSError* error) | |||||
| { | |||||
| if (error != nil) | |||||
| { | |||||
| JUCE_CAMERA_LOG ("Still picture capture failed, error: " + nsStringToJuce (error.localizedDescription)); | |||||
| jassertfalse; | |||||
| return; | |||||
| } | |||||
| auto* imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation: sampleBuffer]; | |||||
| auto image = ImageFileFormat::loadFrom (imageData.bytes, (size_t) imageData.length); | |||||
| p.imageCaptureFinished (image); | |||||
| }]; | |||||
| } | |||||
| } | |||||
| private: | |||||
| AVCaptureStillImageOutput* imageOutput = nil; | |||||
| }; | |||||
| JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||||
| //============================================================================== | //============================================================================== | ||||
| void addImageCapture() | void addImageCapture() | ||||
| { | { | ||||
| imageOutput.addImageCapture (session); | |||||
| imageOutput->addImageCapture (session); | |||||
| } | } | ||||
| void addMovieCapture() | void addMovieCapture() | ||||
| @@ -390,7 +419,7 @@ private: | |||||
| void removeImageCapture() | void removeImageCapture() | ||||
| { | { | ||||
| imageOutput.removeImageCapture (session); | |||||
| imageOutput->removeImageCapture (session); | |||||
| } | } | ||||
| void removeMovieCapture() | void removeMovieCapture() | ||||
| @@ -419,9 +448,7 @@ private: | |||||
| { | { | ||||
| if (currentInput == nil) | if (currentInput == nil) | ||||
| { | { | ||||
| auto* availableDevices = decltype (imageOutput)::getAvailableDevices(); | |||||
| for (AVCaptureDevice* device : availableDevices) | |||||
| for (AVCaptureDevice* device : getCaptureDevices()) | |||||
| { | { | ||||
| if (deviceName == nsStringToJuce ([device localizedName])) | if (deviceName == nsStringToJuce ([device localizedName])) | ||||
| { | { | ||||
| @@ -480,7 +507,7 @@ private: | |||||
| AVCaptureConnection* getVideoConnection() const | AVCaptureConnection* getVideoConnection() const | ||||
| { | { | ||||
| auto* connections = imageOutput.getConnections(); | |||||
| auto* connections = imageOutput->getConnections(); | |||||
| if (connections != nil) | if (connections != nil) | ||||
| for (AVCaptureConnection* connection in connections) | for (AVCaptureConnection* connection in connections) | ||||
| @@ -519,7 +546,7 @@ private: | |||||
| startSession(); | startSession(); | ||||
| if (auto* videoConnection = getVideoConnection()) | if (auto* videoConnection = getVideoConnection()) | ||||
| imageOutput.triggerImageCapture (*this); | |||||
| imageOutput->triggerImageCapture (*this); | |||||
| } | } | ||||
| void cameraSessionRuntimeError (const String& error) | void cameraSessionRuntimeError (const String& error) | ||||
| @@ -536,11 +563,7 @@ private: | |||||
| AVCaptureSession* session = nil; | AVCaptureSession* session = nil; | ||||
| AVCaptureMovieFileOutput* fileOutput = nil; | AVCaptureMovieFileOutput* fileOutput = nil; | ||||
| #if JUCE_USE_NEW_APPLE_CAMERA_API | |||||
| PostCatalinaPhotoOutput imageOutput; | |||||
| #else | |||||
| PreCatalinaStillImageOutput imageOutput; | |||||
| #endif | |||||
| std::unique_ptr<ImageOutputBase> imageOutput; | |||||
| AVCaptureDeviceInput* currentInput = nil; | AVCaptureDeviceInput* currentInput = nil; | ||||
| id<AVCaptureFileOutputRecordingDelegate> callbackDelegate = nil; | id<AVCaptureFileOutputRecordingDelegate> callbackDelegate = nil; | ||||
| @@ -578,5 +601,3 @@ String CameraDevice::getFileExtension() | |||||
| { | { | ||||
| return ".mov"; | return ".mov"; | ||||
| } | } | ||||
| #undef JUCE_USE_NEW_APPLE_CAMERA_API | |||||