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.

276 lines
8.9KB

  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. static bool isStartingUpOrShuttingDown()
  21. {
  22. if (auto* app = JUCEApplicationBase::getInstance())
  23. if (app->isInitialising())
  24. return true;
  25. if (auto* mm = MessageManager::getInstanceWithoutCreating())
  26. if (mm->hasStopMessageBeenSent())
  27. return true;
  28. return false;
  29. }
  30. static bool isHandlerValid (const AccessibilityHandler& handler)
  31. {
  32. if (auto* provider = handler.getNativeImplementation())
  33. return provider->isElementValid();
  34. return false;
  35. }
  36. //==============================================================================
  37. class AccessibilityHandler::AccessibilityNativeImpl
  38. {
  39. public:
  40. explicit AccessibilityNativeImpl (AccessibilityHandler& owner)
  41. : accessibilityElement (new AccessibilityNativeHandle (owner))
  42. {
  43. ++providerCount;
  44. }
  45. ~AccessibilityNativeImpl()
  46. {
  47. accessibilityElement->invalidateElement();
  48. --providerCount;
  49. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  50. {
  51. ComSmartPtr<IRawElementProviderSimple> provider;
  52. accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  53. wrapper->disconnectProvider (provider);
  54. if (providerCount == 0)
  55. wrapper->disconnectAllProviders();
  56. }
  57. }
  58. //==============================================================================
  59. ComSmartPtr<AccessibilityNativeHandle> accessibilityElement;
  60. static int providerCount;
  61. //==============================================================================
  62. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
  63. };
  64. int AccessibilityHandler::AccessibilityNativeImpl::providerCount = 0;
  65. //==============================================================================
  66. AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
  67. {
  68. return nativeImpl->accessibilityElement;
  69. }
  70. template <typename Callback>
  71. void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback)
  72. {
  73. if (isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
  74. return;
  75. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  76. {
  77. if (! wrapper->clientsAreListening())
  78. return;
  79. ComSmartPtr<IRawElementProviderSimple> provider;
  80. handler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  81. callback (wrapper, provider);
  82. }
  83. }
  84. void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVENTID event)
  85. {
  86. jassert (event != EVENTID{});
  87. getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
  88. {
  89. wrapper->raiseAutomationEvent (provider, event);
  90. });
  91. }
  92. void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler& handler, PROPERTYID property, VARIANT newValue)
  93. {
  94. jassert (property != PROPERTYID{});
  95. getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
  96. {
  97. VARIANT oldValue;
  98. VariantHelpers::clear (&oldValue);
  99. wrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue);
  100. });
  101. }
  102. void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
  103. {
  104. if (eventType == InternalAccessibilityEvent::elementCreated
  105. || eventType == InternalAccessibilityEvent::elementDestroyed)
  106. {
  107. if (auto* parent = handler.getParent())
  108. sendAccessibilityAutomationEvent (*parent, UIA_LayoutInvalidatedEventId);
  109. return;
  110. }
  111. auto event = [eventType]() -> EVENTID
  112. {
  113. switch (eventType)
  114. {
  115. case InternalAccessibilityEvent::focusChanged: return UIA_AutomationFocusChangedEventId;
  116. case InternalAccessibilityEvent::windowOpened: return UIA_Window_WindowOpenedEventId;
  117. case InternalAccessibilityEvent::windowClosed: return UIA_Window_WindowClosedEventId;
  118. case InternalAccessibilityEvent::elementCreated:
  119. case InternalAccessibilityEvent::elementDestroyed: break;
  120. }
  121. return {};
  122. }();
  123. if (event != EVENTID{})
  124. sendAccessibilityAutomationEvent (handler, event);
  125. }
  126. void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
  127. {
  128. auto event = [eventType] () -> EVENTID
  129. {
  130. switch (eventType)
  131. {
  132. case AccessibilityEvent::textSelectionChanged: return UIA_Text_TextSelectionChangedEventId;
  133. case AccessibilityEvent::textChanged: return UIA_Text_TextChangedEventId;
  134. case AccessibilityEvent::structureChanged: return UIA_StructureChangedEventId;
  135. case AccessibilityEvent::rowSelectionChanged: return UIA_SelectionItem_ElementSelectedEventId;
  136. case AccessibilityEvent::valueChanged: break;
  137. }
  138. return {};
  139. }();
  140. if (event != EVENTID{})
  141. sendAccessibilityAutomationEvent (*this, event);
  142. }
  143. struct SpVoiceWrapper : public DeletedAtShutdown
  144. {
  145. SpVoiceWrapper()
  146. {
  147. auto hr = voice.CoCreateInstance (CLSID_SpVoice);
  148. jassert (SUCCEEDED (hr));
  149. ignoreUnused (hr);
  150. }
  151. ~SpVoiceWrapper() override
  152. {
  153. clearSingletonInstance();
  154. }
  155. ComSmartPtr<ISpVoice> voice;
  156. JUCE_DECLARE_SINGLETON (SpVoiceWrapper, false)
  157. };
  158. JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper)
  159. void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
  160. {
  161. if (auto* sharedVoice = SpVoiceWrapper::getInstance())
  162. {
  163. auto voicePriority = [priority]
  164. {
  165. switch (priority)
  166. {
  167. case AnnouncementPriority::low: return SPVPRI_OVER;
  168. case AnnouncementPriority::medium: return SPVPRI_NORMAL;
  169. case AnnouncementPriority::high: return SPVPRI_ALERT;
  170. }
  171. jassertfalse;
  172. return SPVPRI_OVER;
  173. }();
  174. sharedVoice->voice->SetPriority (voicePriority);
  175. sharedVoice->voice->Speak (announcementString.toWideCharPointer(), SPF_ASYNC, nullptr);
  176. }
  177. }
  178. AccessibilityHandler::AccessibilityNativeImpl* AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler)
  179. {
  180. return new AccessibilityHandler::AccessibilityNativeImpl (handler);
  181. }
  182. void AccessibilityHandler::DestroyNativeImpl::operator() (AccessibilityHandler::AccessibilityNativeImpl* impl) const noexcept
  183. {
  184. delete impl;
  185. }
  186. //==============================================================================
  187. namespace WindowsAccessibility
  188. {
  189. long getUiaRootObjectId()
  190. {
  191. return static_cast<long> (UiaRootObjectId);
  192. }
  193. bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res)
  194. {
  195. if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
  196. return false;
  197. if (auto* wrapper = WindowsUIAWrapper::getInstance())
  198. {
  199. ComSmartPtr<IRawElementProviderSimple> provider;
  200. handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
  201. if (! wrapper->isProviderDisconnecting (provider))
  202. *res = wrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
  203. return true;
  204. }
  205. return false;
  206. }
  207. void revokeUIAMapEntriesForWindow (HWND hwnd)
  208. {
  209. if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
  210. wrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
  211. }
  212. }
  213. JUCE_IMPLEMENT_SINGLETON (WindowsUIAWrapper)
  214. } // namespace juce