The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

291 lines
9.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
  21. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
  22. static bool isStartingUpOrShuttingDown()
  23. {
  24. if (auto* app = JUCEApplicationBase::getInstance())
  25. if (app->isInitialising())
  26. return true;
  27. if (auto* mm = MessageManager::getInstanceWithoutCreating())
  28. if (mm->hasStopMessageBeenSent())
  29. return true;
  30. return false;
  31. }
  32. static bool isHandlerValid (const AccessibilityHandler& handler)
  33. {
  34. if (auto* provider = handler.getNativeImplementation())
  35. return provider->isElementValid();
  36. return false;
  37. }
  38. //==============================================================================
  39. class AccessibilityHandler::AccessibilityNativeImpl
  40. {
  41. public:
  42. explicit AccessibilityNativeImpl (AccessibilityHandler& owner)
  43. : accessibilityElement (new AccessibilityNativeHandle (owner))
  44. {
  45. ++providerCount;
  46. }
  47. ~AccessibilityNativeImpl()
  48. {
  49. accessibilityElement->invalidateElement();
  50. --providerCount;
  51. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  52. {
  53. ComSmartPtr<IRawElementProviderSimple> provider;
  54. accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  55. wrapper->disconnectProvider (provider);
  56. if (providerCount == 0)
  57. wrapper->disconnectAllProviders();
  58. }
  59. }
  60. //==============================================================================
  61. ComSmartPtr<AccessibilityNativeHandle> accessibilityElement;
  62. static int providerCount;
  63. //==============================================================================
  64. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
  65. };
  66. int AccessibilityHandler::AccessibilityNativeImpl::providerCount = 0;
  67. //==============================================================================
  68. AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
  69. {
  70. return nativeImpl->accessibilityElement;
  71. }
  72. template <typename Callback>
  73. void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback)
  74. {
  75. if (isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
  76. return;
  77. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  78. {
  79. if (! wrapper->clientsAreListening())
  80. return;
  81. ComSmartPtr<IRawElementProviderSimple> provider;
  82. handler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  83. callback (wrapper, provider);
  84. }
  85. }
  86. void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVENTID event)
  87. {
  88. jassert (event != EVENTID{});
  89. getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
  90. {
  91. wrapper->raiseAutomationEvent (provider, event);
  92. });
  93. }
  94. void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler& handler, PROPERTYID property, VARIANT newValue)
  95. {
  96. jassert (property != PROPERTYID{});
  97. getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
  98. {
  99. VARIANT oldValue;
  100. VariantHelpers::clear (&oldValue);
  101. wrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue);
  102. });
  103. }
  104. void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
  105. {
  106. if (eventType == InternalAccessibilityEvent::elementCreated
  107. || eventType == InternalAccessibilityEvent::elementDestroyed)
  108. {
  109. if (auto* parent = handler.getParent())
  110. sendAccessibilityAutomationEvent (*parent, UIA_LayoutInvalidatedEventId);
  111. return;
  112. }
  113. auto event = [eventType]() -> EVENTID
  114. {
  115. switch (eventType)
  116. {
  117. case InternalAccessibilityEvent::focusChanged: return UIA_AutomationFocusChangedEventId;
  118. case InternalAccessibilityEvent::windowOpened: return UIA_Window_WindowOpenedEventId;
  119. case InternalAccessibilityEvent::windowClosed: return UIA_Window_WindowClosedEventId;
  120. case InternalAccessibilityEvent::elementCreated:
  121. case InternalAccessibilityEvent::elementDestroyed: break;
  122. }
  123. return {};
  124. }();
  125. if (event != EVENTID{})
  126. sendAccessibilityAutomationEvent (handler, event);
  127. }
  128. void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
  129. {
  130. if (eventType == AccessibilityEvent::titleChanged)
  131. {
  132. VARIANT newValue;
  133. VariantHelpers::setString (getTitle(), &newValue);
  134. sendAccessibilityPropertyChangedEvent (*this, UIA_NamePropertyId, newValue);
  135. }
  136. auto event = [eventType] () -> EVENTID
  137. {
  138. switch (eventType)
  139. {
  140. case AccessibilityEvent::textSelectionChanged: return UIA_Text_TextSelectionChangedEventId;
  141. case AccessibilityEvent::textChanged: return UIA_Text_TextChangedEventId;
  142. case AccessibilityEvent::structureChanged: return UIA_StructureChangedEventId;
  143. case AccessibilityEvent::rowSelectionChanged: return UIA_SelectionItem_ElementSelectedEventId;
  144. case AccessibilityEvent::titleChanged:
  145. case AccessibilityEvent::valueChanged: break;
  146. }
  147. return {};
  148. }();
  149. if (event != EVENTID{})
  150. sendAccessibilityAutomationEvent (*this, event);
  151. }
  152. struct SpVoiceWrapper : public DeletedAtShutdown
  153. {
  154. SpVoiceWrapper()
  155. {
  156. auto hr = voice.CoCreateInstance (CLSID_SpVoice);
  157. jassert (SUCCEEDED (hr));
  158. ignoreUnused (hr);
  159. }
  160. ~SpVoiceWrapper() override
  161. {
  162. clearSingletonInstance();
  163. }
  164. ComSmartPtr<ISpVoice> voice;
  165. JUCE_DECLARE_SINGLETON (SpVoiceWrapper, false)
  166. };
  167. JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper)
  168. void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
  169. {
  170. if (auto* sharedVoice = SpVoiceWrapper::getInstance())
  171. {
  172. auto voicePriority = [priority]
  173. {
  174. switch (priority)
  175. {
  176. case AnnouncementPriority::low: return SPVPRI_OVER;
  177. case AnnouncementPriority::medium: return SPVPRI_NORMAL;
  178. case AnnouncementPriority::high: return SPVPRI_ALERT;
  179. }
  180. jassertfalse;
  181. return SPVPRI_OVER;
  182. }();
  183. sharedVoice->voice->SetPriority (voicePriority);
  184. sharedVoice->voice->Speak (announcementString.toWideCharPointer(), SPF_ASYNC, nullptr);
  185. }
  186. }
  187. AccessibilityHandler::AccessibilityNativeImpl* AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler)
  188. {
  189. return new AccessibilityHandler::AccessibilityNativeImpl (handler);
  190. }
  191. void AccessibilityHandler::DestroyNativeImpl::operator() (AccessibilityHandler::AccessibilityNativeImpl* impl) const noexcept
  192. {
  193. delete impl;
  194. }
  195. //==============================================================================
  196. namespace WindowsAccessibility
  197. {
  198. long getUiaRootObjectId()
  199. {
  200. return static_cast<long> (UiaRootObjectId);
  201. }
  202. bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res)
  203. {
  204. if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
  205. return false;
  206. if (auto* wrapper = WindowsUIAWrapper::getInstance())
  207. {
  208. ComSmartPtr<IRawElementProviderSimple> provider;
  209. handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  210. if (! wrapper->isProviderDisconnecting (provider))
  211. *res = wrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
  212. return true;
  213. }
  214. return false;
  215. }
  216. void revokeUIAMapEntriesForWindow (HWND hwnd)
  217. {
  218. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  219. wrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
  220. }
  221. }
  222. JUCE_IMPLEMENT_SINGLETON (WindowsUIAWrapper)
  223. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  224. } // namespace juce