diff --git a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp index 3504709dce..a6809ee92f 100644 --- a/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp +++ b/modules/juce_gui_basics/accessibility/juce_AccessibilityHandler.cpp @@ -28,6 +28,8 @@ namespace juce AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr; +bool areAnyAccessibilityClientsActive(); + enum class InternalAccessibilityEvent { elementCreated, @@ -37,7 +39,7 @@ enum class InternalAccessibilityEvent windowClosed }; -void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent event); +void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent); inline String getAccessibleApplicationOrPluginName() { diff --git a/modules/juce_gui_basics/juce_gui_basics.cpp b/modules/juce_gui_basics/juce_gui_basics.cpp index fcd4cbfbf3..40b4ffdec3 100644 --- a/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/modules/juce_gui_basics/juce_gui_basics.cpp @@ -347,6 +347,7 @@ namespace juce AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; } AccessibilityHandler::AccessibilityNativeImpl* AccessibilityHandler::createNativeImpl (AccessibilityHandler&) { return nullptr; } void AccessibilityHandler::DestroyNativeImpl::operator() (AccessibilityHandler::AccessibilityNativeImpl*) const noexcept {} + bool areAnyAccessibilityClientsActive() { return false; } void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent) {} } #endif diff --git a/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm b/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm index 506f46573f..2ab9237f1c 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm +++ b/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm @@ -1025,6 +1025,20 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement(); } +bool areAnyAccessibilityClientsActive() +{ + const String voiceOverKeyString ("voiceOverOnOffKey"); + const String applicationIDString ("com.apple.universalaccess"); + + CFUniquePtr value (CFPreferencesCopyAppValue (voiceOverKeyString.toCFString(), + applicationIDString.toCFString())); + + if (value != nullptr) + return CFBooleanGetValue ((CFBooleanRef) value.get()); + + return false; +} + static void sendAccessibilityEvent (id accessibilityElement, NSAccessibilityNotificationName notification, NSDictionary* userInfo) @@ -1034,6 +1048,21 @@ static void sendAccessibilityEvent (id accessibilityElement, NSAccessibilityPostNotificationWithUserInfo (accessibilityElement, notification, userInfo); } +static void sendHandlerNotification (const AccessibilityHandler& handler, + NSAccessibilityNotificationName notification) +{ + if (! areAnyAccessibilityClientsActive() || notification == NSAccessibilityNotificationName{}) + return; + + if (id accessibilityElement = (id) handler.getNativeImplementation()) + { + sendAccessibilityEvent (accessibilityElement, notification, + (notification == NSAccessibilityLayoutChangedNotification + ? @{ NSAccessibilityUIElementsKey: @[ accessibilityElement ] } + : nil)); + } +} + void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType) { auto notification = [eventType] @@ -1050,9 +1079,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte return NSAccessibilityNotificationName{}; }(); - if (notification != NSAccessibilityNotificationName{}) - if (id accessibilityElement = (id) handler.getNativeImplementation()) - sendAccessibilityEvent (accessibilityElement, notification, nil); + sendHandlerNotification (handler, notification); } void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const @@ -1073,20 +1100,14 @@ void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventTyp return NSAccessibilityNotificationName{}; }(); - if (notification != NSAccessibilityNotificationName{}) - { - if (id accessibilityElement = (id) getNativeImplementation()) - { - sendAccessibilityEvent (accessibilityElement, notification, - (notification == NSAccessibilityLayoutChangedNotification - ? @{ NSAccessibilityUIElementsKey: @[ accessibilityElement ] } - : nil)); - } - } + sendHandlerNotification (*this, notification); } void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority) { + if (! areAnyAccessibilityClientsActive()) + return; + #if JUCE_OBJC_HAS_AVAILABLE_FEATURE if (@available (macOS 10.10, *)) #endif diff --git a/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp b/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp index 8cb2d526c9..15a596d93b 100644 --- a/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp +++ b/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp @@ -66,15 +66,15 @@ public: accessibilityElement->invalidateElement(); --providerCount; - if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) { ComSmartPtr provider; accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); - wrapper->disconnectProvider (provider); + uiaWrapper->disconnectProvider (provider); if (providerCount == 0) - wrapper->disconnectAllProviders(); + uiaWrapper->disconnectAllProviders(); } } @@ -94,21 +94,39 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const return nativeImpl->accessibilityElement; } +bool areAnyAccessibilityClientsActive() +{ + const auto areClientsListening = [] + { + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + return uiaWrapper->clientsAreListening() != 0; + + return false; + }; + + const auto isScreenReaderRunning = [] + { + BOOL isRunning = FALSE; + SystemParametersInfo (SPI_GETSCREENREADER, 0, (PVOID) &isRunning, 0); + + return isRunning != 0; + }; + + return areClientsListening() || isScreenReaderRunning(); +} + template void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback) { - if (isStartingUpOrShuttingDown() || ! isHandlerValid (handler)) + if (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler)) return; - if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) { - if (! wrapper->clientsAreListening()) - return; - ComSmartPtr provider; handler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); - callback (wrapper, provider); + callback (uiaWrapper, provider); } } @@ -116,9 +134,9 @@ void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVEN { jassert (event != EVENTID{}); - getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* wrapper, ComSmartPtr& provider) + getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr& provider) { - wrapper->raiseAutomationEvent (provider, event); + uiaWrapper->raiseAutomationEvent (provider, event); }); } @@ -126,12 +144,12 @@ void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler& handler, { jassert (property != PROPERTYID{}); - getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* wrapper, ComSmartPtr& provider) + getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr& provider) { VARIANT oldValue; VariantHelpers::clear (&oldValue); - wrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue); + uiaWrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue); }); } @@ -218,6 +236,9 @@ JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper) void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority) { + if (! areAnyAccessibilityClientsActive()) + return; + if (auto* sharedVoice = SpVoiceWrapper::getInstance()) { auto voicePriority = [priority] @@ -261,13 +282,13 @@ namespace WindowsAccessibility if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler))) return false; - if (auto* wrapper = WindowsUIAWrapper::getInstance()) + if (auto* uiaWrapper = WindowsUIAWrapper::getInstance()) { ComSmartPtr provider; handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())); - if (! wrapper->isProviderDisconnecting (provider)) - *res = wrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider); + if (! uiaWrapper->isProviderDisconnecting (provider)) + *res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider); return true; } @@ -277,8 +298,8 @@ namespace WindowsAccessibility void revokeUIAMapEntriesForWindow (HWND hwnd) { - if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) - wrapper->returnRawElementProvider (hwnd, 0, 0, nullptr); + if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating()) + uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr); } }