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.

306 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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. #include <juce_core/system/juce_TargetPlatform.h>
  19. #if JUCE_MAC
  20. #include "../utility/juce_CheckSettingMacros.h"
  21. #if JucePlugin_Build_VST || JucePlugin_Build_VST3
  22. #include "../utility/juce_IncludeSystemHeaders.h"
  23. #include "../utility/juce_IncludeModuleHeaders.h"
  24. //==============================================================================
  25. namespace juce
  26. {
  27. #if ! JUCE_64BIT
  28. JUCE_API void updateEditorCompBoundsVST (Component*);
  29. void updateEditorCompBoundsVST (Component* comp)
  30. {
  31. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  32. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  33. HIRect r;
  34. HIViewGetFrame (dummyView, &r);
  35. HIViewRef root;
  36. HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root);
  37. HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root);
  38. Rect windowPos;
  39. GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos);
  40. comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x),
  41. (int) (windowPos.top + r.origin.y));
  42. }
  43. static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user)
  44. {
  45. updateEditorCompBoundsVST ((Component*) user);
  46. return noErr;
  47. }
  48. static bool shouldManuallyCloseHostWindow()
  49. {
  50. return getHostType().isCubase7orLater() || getHostType().isRenoise() || ((SystemStats::getOperatingSystemType() & 0xff) >= 12);
  51. }
  52. #endif
  53. //==============================================================================
  54. JUCE_API void initialiseMacVST();
  55. void initialiseMacVST()
  56. {
  57. #if ! JUCE_64BIT
  58. NSApplicationLoad();
  59. #endif
  60. }
  61. JUCE_API void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView);
  62. void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, [[maybe_unused]] bool isNSView)
  63. {
  64. JUCE_AUTORELEASEPOOL
  65. {
  66. #if ! JUCE_64BIT
  67. if (! isNSView)
  68. {
  69. NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView];
  70. if (shouldManuallyCloseHostWindow())
  71. {
  72. [hostWindow setReleasedWhenClosed: NO];
  73. }
  74. else
  75. {
  76. [hostWindow retain];
  77. [hostWindow setReleasedWhenClosed: YES];
  78. }
  79. [hostWindow setCanHide: YES];
  80. HIViewRef parentView = 0;
  81. WindowAttributes attributes;
  82. GetWindowAttributes ((WindowRef) parentWindowOrView, &attributes);
  83. if ((attributes & kWindowCompositingAttribute) != 0)
  84. {
  85. HIViewRef root = HIViewGetRoot ((WindowRef) parentWindowOrView);
  86. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  87. if (parentView == 0)
  88. parentView = root;
  89. }
  90. else
  91. {
  92. GetRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  93. if (parentView == 0)
  94. CreateRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  95. }
  96. // It seems that the only way to successfully position our overlaid window is by putting a dummy
  97. // HIView into the host's carbon window, and then catching events to see when it gets repositioned
  98. HIViewRef dummyView = 0;
  99. HIImageViewCreate (0, &dummyView);
  100. HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} };
  101. HIViewSetFrame (dummyView, &r);
  102. HIViewAddSubview (parentView, dummyView);
  103. comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView));
  104. EventHandlerRef ref;
  105. const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged };
  106. InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref);
  107. comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref));
  108. updateEditorCompBoundsVST (comp);
  109. #if ! JucePlugin_EditorRequiresKeyboardFocus
  110. comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  111. #else
  112. comp->addToDesktop (ComponentPeer::windowIsTemporary);
  113. #endif
  114. comp->setVisible (true);
  115. comp->toFront (false);
  116. NSView* pluginView = (NSView*) comp->getWindowHandle();
  117. NSWindow* pluginWindow = [pluginView window];
  118. [pluginWindow setExcludedFromWindowsMenu: YES];
  119. [pluginWindow setCanHide: YES];
  120. [hostWindow addChildWindow: pluginWindow
  121. ordered: NSWindowAbove];
  122. [hostWindow orderFront: nil];
  123. [pluginWindow orderFront: nil];
  124. return hostWindow;
  125. }
  126. #endif
  127. NSView* parentView = [(NSView*) parentWindowOrView retain];
  128. #if JucePlugin_EditorRequiresKeyboardFocus
  129. comp->addToDesktop (0, parentView);
  130. #else
  131. comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView);
  132. #endif
  133. // (this workaround is because Wavelab provides a zero-size parent view..)
  134. if ([parentView frame].size.height == 0)
  135. [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
  136. comp->setVisible (true);
  137. comp->toFront (false);
  138. [[parentView window] setAcceptsMouseMovedEvents: YES];
  139. return parentView;
  140. }
  141. }
  142. JUCE_API void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView);
  143. void detachComponentFromWindowRefVST (Component* comp, void* window, [[maybe_unused]] bool isNSView)
  144. {
  145. JUCE_AUTORELEASEPOOL
  146. {
  147. #if ! JUCE_64BIT
  148. if (! isNSView)
  149. {
  150. EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
  151. comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
  152. RemoveEventHandler (ref);
  153. CFUniquePtr<HIViewRef> dummyView ((HIViewRef) (void*) (pointer_sized_int)
  154. comp->getProperties() ["dummyViewRef"].toString().getHexValue64());
  155. if (HIViewIsValid (dummyView.get()))
  156. dummyView = nullptr;
  157. NSWindow* hostWindow = (NSWindow*) window;
  158. NSView* pluginView = (NSView*) comp->getWindowHandle();
  159. NSWindow* pluginWindow = [pluginView window];
  160. [pluginView retain];
  161. [hostWindow removeChildWindow: pluginWindow];
  162. [pluginWindow close];
  163. comp->removeFromDesktop();
  164. [pluginView release];
  165. if (shouldManuallyCloseHostWindow())
  166. [hostWindow close];
  167. else
  168. [hostWindow release];
  169. #if JUCE_MODAL_LOOPS_PERMITTED
  170. static bool needToRunMessageLoop = ! getHostType().isReaper();
  171. // The event loop needs to be run between closing the window and deleting the plugin,
  172. // presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes
  173. // in Live when you delete the plugin with its window open.
  174. // (Doing it this way rather than using a single longer timeout means that we can guarantee
  175. // how many messages will be dispatched, which seems to be vital in Reaper)
  176. if (needToRunMessageLoop)
  177. for (int i = 20; --i >= 0;)
  178. MessageManager::getInstance()->runDispatchLoopUntil (1);
  179. #endif
  180. return;
  181. }
  182. #endif
  183. comp->removeFromDesktop();
  184. [(id) window release];
  185. }
  186. }
  187. JUCE_API void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView);
  188. void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, [[maybe_unused]] bool isNSView)
  189. {
  190. JUCE_AUTORELEASEPOOL
  191. {
  192. #if ! JUCE_64BIT
  193. if (! isNSView)
  194. {
  195. if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  196. component->getProperties() ["dummyViewRef"].toString().getHexValue64())
  197. {
  198. HIRect frameRect;
  199. HIViewGetFrame (dummyView, &frameRect);
  200. frameRect.size.width = newWidth;
  201. frameRect.size.height = newHeight;
  202. HIViewSetFrame (dummyView, &frameRect);
  203. }
  204. return;
  205. }
  206. #endif
  207. if (NSView* hostView = (NSView*) window)
  208. {
  209. const int dx = newWidth - component->getWidth();
  210. const int dy = newHeight - component->getHeight();
  211. NSRect r = [hostView frame];
  212. r.size.width += dx;
  213. r.size.height += dy;
  214. r.origin.y -= dy;
  215. [hostView setFrame: r];
  216. }
  217. }
  218. }
  219. JUCE_API void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView);
  220. void checkWindowVisibilityVST ([[maybe_unused]] void* window,
  221. [[maybe_unused]] Component* comp,
  222. [[maybe_unused]] bool isNSView)
  223. {
  224. #if ! JUCE_64BIT
  225. if (! isNSView)
  226. comp->setVisible ([((NSWindow*) window) isVisible]);
  227. #endif
  228. }
  229. JUCE_API bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView);
  230. bool forwardCurrentKeyEventToHostVST ([[maybe_unused]] Component* comp, [[maybe_unused]] bool isNSView)
  231. {
  232. #if ! JUCE_64BIT
  233. if (! isNSView)
  234. {
  235. NSWindow* win = [(NSView*) comp->getWindowHandle() window];
  236. [[win parentWindow] makeKeyWindow];
  237. repostCurrentNSEvent();
  238. return true;
  239. }
  240. #endif
  241. return false;
  242. }
  243. } // (juce namespace)
  244. #endif
  245. #endif