VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_basics / native / accessibility / juce_win32_Accessibility.cpp
blob9be3ac5e3556318e856bb6f195571921a8fe0ee7
1 /*
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
8 licensing.
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
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
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())
37 return true;
39 if (auto* mm = MessageManager::getInstanceWithoutCreating())
40 if (mm->hasStopMessageBeenSent())
41 return true;
43 return false;
46 static bool isHandlerValid (const AccessibilityHandler& handler)
48 if (auto* provider = handler.getNativeImplementation())
49 return provider->isElementValid();
51 return false;
54 //==============================================================================
55 class AccessibilityHandler::AccessibilityNativeImpl
57 public:
58 explicit AccessibilityNativeImpl (AccessibilityHandler& owner)
59 : accessibilityElement (new AccessibilityNativeHandle (owner))
61 ++providerCount;
64 ~AccessibilityNativeImpl()
66 ComSmartPtr<IRawElementProviderSimple> provider;
67 accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
69 accessibilityElement->invalidateElement();
70 --providerCount;
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;
104 return false;
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))
122 return;
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)
149 VARIANT oldValue;
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);
164 return;
167 if (eventType == InternalAccessibilityEvent::windowOpened
168 || eventType == InternalAccessibilityEvent::windowClosed)
170 if (auto* peer = handler.getComponent().getPeer())
171 if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) == 0)
172 return;
175 auto event = [eventType]() -> EVENTID
177 switch (eventType)
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;
187 return {};
188 }();
190 if (event != EVENTID{})
191 sendAccessibilityAutomationEvent (handler, event);
194 void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
196 if (eventType == AccessibilityEvent::titleChanged)
198 VARIANT newValue;
199 VariantHelpers::setString (getTitle(), &newValue);
201 sendAccessibilityPropertyChangedEvent (*this, UIA_NamePropertyId, newValue);
202 return;
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);
219 return;
222 auto event = [eventType]() -> EVENTID
224 switch (eventType)
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;
234 return {};
235 }();
237 if (event != EVENTID{})
238 sendAccessibilityAutomationEvent (*this, event);
241 struct SpVoiceWrapper : public DeletedAtShutdown
243 SpVoiceWrapper()
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())
266 return;
268 if (auto* sharedVoice = SpVoiceWrapper::getInstance())
270 auto voicePriority = [priority]
272 switch (priority)
274 case AnnouncementPriority::low: return SPVPRI_OVER;
275 case AnnouncementPriority::medium: return SPVPRI_NORMAL;
276 case AnnouncementPriority::high: return SPVPRI_ALERT;
279 jassertfalse;
280 return SPVPRI_OVER;
281 }();
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)))
299 return false;
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);
309 return true;
312 return false;
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
327 } // namespace juce