2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
31 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
33 static bool isStartingUpOrShuttingDown()
35 if (auto* app
= JUCEApplicationBase::getInstance())
36 if (app
->isInitialising())
39 if (auto* mm
= MessageManager::getInstanceWithoutCreating())
40 if (mm
->hasStopMessageBeenSent())
46 static bool isHandlerValid (const AccessibilityHandler
& handler
)
48 if (auto* provider
= handler
.getNativeImplementation())
49 return provider
->isElementValid();
54 //==============================================================================
55 class AccessibilityHandler::AccessibilityNativeImpl
58 explicit AccessibilityNativeImpl (AccessibilityHandler
& owner
)
59 : accessibilityElement (new AccessibilityNativeHandle (owner
))
64 ~AccessibilityNativeImpl()
66 ComSmartPtr
<IRawElementProviderSimple
> provider
;
67 accessibilityElement
->QueryInterface (IID_PPV_ARGS (provider
.resetAndGetPointerAddress()));
69 accessibilityElement
->invalidateElement();
72 if (auto* uiaWrapper
= WindowsUIAWrapper::getInstanceWithoutCreating())
74 uiaWrapper
->disconnectProvider (provider
);
76 if (providerCount
== 0 && JUCEApplicationBase::isStandaloneApp())
77 uiaWrapper
->disconnectAllProviders();
81 //==============================================================================
82 ComSmartPtr
<AccessibilityNativeHandle
> accessibilityElement
;
83 static int providerCount
;
85 //==============================================================================
86 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl
)
89 int AccessibilityHandler::AccessibilityNativeImpl::providerCount
= 0;
91 //==============================================================================
92 AccessibilityNativeHandle
* AccessibilityHandler::getNativeImplementation() const
94 return nativeImpl
->accessibilityElement
;
97 static bool areAnyAccessibilityClientsActive()
99 const auto areClientsListening
= []
101 if (auto* uiaWrapper
= WindowsUIAWrapper::getInstanceWithoutCreating())
102 return uiaWrapper
->clientsAreListening() != 0;
107 const auto isScreenReaderRunning
= []
109 BOOL isRunning
= FALSE
;
110 SystemParametersInfo (SPI_GETSCREENREADER
, 0, (PVOID
) &isRunning
, 0);
112 return isRunning
!= 0;
115 return areClientsListening() || isScreenReaderRunning();
118 template <typename Callback
>
119 void getProviderWithCheckedWrapper (const AccessibilityHandler
& handler
, Callback
&& callback
)
121 if (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler
))
124 if (auto* uiaWrapper
= WindowsUIAWrapper::getInstanceWithoutCreating())
126 ComSmartPtr
<IRawElementProviderSimple
> provider
;
127 handler
.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider
.resetAndGetPointerAddress()));
129 callback (uiaWrapper
, provider
);
133 void sendAccessibilityAutomationEvent (const AccessibilityHandler
& handler
, EVENTID event
)
135 jassert (event
!= EVENTID
{});
137 getProviderWithCheckedWrapper (handler
, [event
] (WindowsUIAWrapper
* uiaWrapper
, ComSmartPtr
<IRawElementProviderSimple
>& provider
)
139 uiaWrapper
->raiseAutomationEvent (provider
, event
);
143 void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler
& handler
, PROPERTYID property
, VARIANT newValue
)
145 jassert (property
!= PROPERTYID
{});
147 getProviderWithCheckedWrapper (handler
, [property
, newValue
] (WindowsUIAWrapper
* uiaWrapper
, ComSmartPtr
<IRawElementProviderSimple
>& provider
)
150 VariantHelpers::clear (&oldValue
);
152 uiaWrapper
->raiseAutomationPropertyChangedEvent (provider
, property
, oldValue
, newValue
);
156 void notifyAccessibilityEventInternal (const AccessibilityHandler
& handler
, InternalAccessibilityEvent eventType
)
158 if (eventType
== InternalAccessibilityEvent::elementCreated
159 || eventType
== InternalAccessibilityEvent::elementDestroyed
)
161 if (auto* parent
= handler
.getParent())
162 sendAccessibilityAutomationEvent (*parent
, ComTypes::UIA_LayoutInvalidatedEventId
);
167 if (eventType
== InternalAccessibilityEvent::windowOpened
168 || eventType
== InternalAccessibilityEvent::windowClosed
)
170 if (auto* peer
= handler
.getComponent().getPeer())
171 if ((peer
->getStyleFlags() & ComponentPeer::windowHasTitleBar
) == 0)
175 auto event
= [eventType
]() -> EVENTID
179 case InternalAccessibilityEvent::focusChanged
: return ComTypes::UIA_AutomationFocusChangedEventId
;
180 case InternalAccessibilityEvent::windowOpened
: return ComTypes::UIA_Window_WindowOpenedEventId
;
181 case InternalAccessibilityEvent::windowClosed
: return ComTypes::UIA_Window_WindowClosedEventId
;
182 case InternalAccessibilityEvent::elementCreated
:
183 case InternalAccessibilityEvent::elementDestroyed
:
184 case InternalAccessibilityEvent::elementMovedOrResized
: break;
190 if (event
!= EVENTID
{})
191 sendAccessibilityAutomationEvent (handler
, event
);
194 void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType
) const
196 if (eventType
== AccessibilityEvent::titleChanged
)
199 VariantHelpers::setString (getTitle(), &newValue
);
201 sendAccessibilityPropertyChangedEvent (*this, UIA_NamePropertyId
, newValue
);
205 if (eventType
== AccessibilityEvent::valueChanged
)
207 if (auto* valueInterface
= getValueInterface())
209 const auto propertyType
= getRole() == AccessibilityRole::slider
? UIA_RangeValueValuePropertyId
210 : UIA_ValueValuePropertyId
;
212 const auto value
= getRole() == AccessibilityRole::slider
213 ? VariantHelpers::getWithValue (valueInterface
->getCurrentValue())
214 : VariantHelpers::getWithValue (valueInterface
->getCurrentValueAsString());
216 sendAccessibilityPropertyChangedEvent (*this, propertyType
, value
);
222 auto event
= [eventType
]() -> EVENTID
226 case AccessibilityEvent::textSelectionChanged
: return ComTypes::UIA_Text_TextSelectionChangedEventId
;
227 case AccessibilityEvent::textChanged
: return ComTypes::UIA_Text_TextChangedEventId
;
228 case AccessibilityEvent::structureChanged
: return ComTypes::UIA_StructureChangedEventId
;
229 case AccessibilityEvent::rowSelectionChanged
: return ComTypes::UIA_SelectionItem_ElementSelectedEventId
;
230 case AccessibilityEvent::titleChanged
:
231 case AccessibilityEvent::valueChanged
: break;
237 if (event
!= EVENTID
{})
238 sendAccessibilityAutomationEvent (*this, event
);
241 struct SpVoiceWrapper
: public DeletedAtShutdown
245 auto hr
= voice
.CoCreateInstance (ComTypes::CLSID_SpVoice
);
247 jassertquiet (SUCCEEDED (hr
));
250 ~SpVoiceWrapper() override
252 clearSingletonInstance();
255 ComSmartPtr
<ISpVoice
> voice
;
257 JUCE_DECLARE_SINGLETON (SpVoiceWrapper
, false)
260 JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper
)
263 void AccessibilityHandler::postAnnouncement (const String
& announcementString
, AnnouncementPriority priority
)
265 if (! areAnyAccessibilityClientsActive())
268 if (auto* sharedVoice
= SpVoiceWrapper::getInstance())
270 auto voicePriority
= [priority
]
274 case AnnouncementPriority::low
: return SPVPRI_OVER
;
275 case AnnouncementPriority::medium
: return SPVPRI_NORMAL
;
276 case AnnouncementPriority::high
: return SPVPRI_ALERT
;
283 sharedVoice
->voice
->SetPriority (voicePriority
);
284 sharedVoice
->voice
->Speak (announcementString
.toWideCharPointer(), SPF_ASYNC
, nullptr);
288 //==============================================================================
289 namespace WindowsAccessibility
291 static long getUiaRootObjectId()
293 return static_cast<long> (UiaRootObjectId
);
296 static bool handleWmGetObject (AccessibilityHandler
* handler
, WPARAM wParam
, LPARAM lParam
, LRESULT
* res
)
298 if (isStartingUpOrShuttingDown() || (handler
== nullptr || ! isHandlerValid (*handler
)))
301 if (auto* uiaWrapper
= WindowsUIAWrapper::getInstance())
303 ComSmartPtr
<IRawElementProviderSimple
> provider
;
304 handler
->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider
.resetAndGetPointerAddress()));
306 if (! uiaWrapper
->isProviderDisconnecting (provider
))
307 *res
= uiaWrapper
->returnRawElementProvider ((HWND
) handler
->getComponent().getWindowHandle(), wParam
, lParam
, provider
);
315 static void revokeUIAMapEntriesForWindow (HWND hwnd
)
317 if (auto* uiaWrapper
= WindowsUIAWrapper::getInstanceWithoutCreating())
318 uiaWrapper
->returnRawElementProvider (hwnd
, 0, 0, nullptr);
323 JUCE_IMPLEMENT_SINGLETON (WindowsUIAWrapper
)
325 JUCE_END_IGNORE_WARNINGS_GCC_LIKE