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.

311 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, 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. ignoreUnused (isNSView);
  128. NSView* parentView = [(NSView*) parentWindowOrView retain];
  129. #if JucePlugin_EditorRequiresKeyboardFocus
  130. comp->addToDesktop (0, parentView);
  131. #else
  132. comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView);
  133. #endif
  134. // (this workaround is because Wavelab provides a zero-size parent view..)
  135. if ([parentView frame].size.height == 0)
  136. [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
  137. comp->setVisible (true);
  138. comp->toFront (false);
  139. [[parentView window] setAcceptsMouseMovedEvents: YES];
  140. return parentView;
  141. }
  142. }
  143. JUCE_API void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView);
  144. void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView)
  145. {
  146. JUCE_AUTORELEASEPOOL
  147. {
  148. #if ! JUCE_64BIT
  149. if (! isNSView)
  150. {
  151. EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
  152. comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
  153. RemoveEventHandler (ref);
  154. CFUniquePtr<HIViewRef> dummyView ((HIViewRef) (void*) (pointer_sized_int)
  155. comp->getProperties() ["dummyViewRef"].toString().getHexValue64());
  156. if (HIViewIsValid (dummyView.get()))
  157. dummyView = nullptr;
  158. NSWindow* hostWindow = (NSWindow*) window;
  159. NSView* pluginView = (NSView*) comp->getWindowHandle();
  160. NSWindow* pluginWindow = [pluginView window];
  161. [pluginView retain];
  162. [hostWindow removeChildWindow: pluginWindow];
  163. [pluginWindow close];
  164. comp->removeFromDesktop();
  165. [pluginView release];
  166. if (shouldManuallyCloseHostWindow())
  167. [hostWindow close];
  168. else
  169. [hostWindow release];
  170. #if JUCE_MODAL_LOOPS_PERMITTED
  171. static bool needToRunMessageLoop = ! getHostType().isReaper();
  172. // The event loop needs to be run between closing the window and deleting the plugin,
  173. // presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes
  174. // in Live when you delete the plugin with its window open.
  175. // (Doing it this way rather than using a single longer timeout means that we can guarantee
  176. // how many messages will be dispatched, which seems to be vital in Reaper)
  177. if (needToRunMessageLoop)
  178. for (int i = 20; --i >= 0;)
  179. MessageManager::getInstance()->runDispatchLoopUntil (1);
  180. #endif
  181. return;
  182. }
  183. #endif
  184. ignoreUnused (isNSView);
  185. comp->removeFromDesktop();
  186. [(id) window release];
  187. }
  188. }
  189. JUCE_API void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView);
  190. void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView)
  191. {
  192. JUCE_AUTORELEASEPOOL
  193. {
  194. #if ! JUCE_64BIT
  195. if (! isNSView)
  196. {
  197. if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  198. component->getProperties() ["dummyViewRef"].toString().getHexValue64())
  199. {
  200. HIRect frameRect;
  201. HIViewGetFrame (dummyView, &frameRect);
  202. frameRect.size.width = newWidth;
  203. frameRect.size.height = newHeight;
  204. HIViewSetFrame (dummyView, &frameRect);
  205. }
  206. return;
  207. }
  208. #endif
  209. ignoreUnused (isNSView);
  210. if (NSView* hostView = (NSView*) window)
  211. {
  212. const int dx = newWidth - component->getWidth();
  213. const int dy = newHeight - component->getHeight();
  214. NSRect r = [hostView frame];
  215. r.size.width += dx;
  216. r.size.height += dy;
  217. r.origin.y -= dy;
  218. [hostView setFrame: r];
  219. }
  220. }
  221. }
  222. JUCE_API void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView);
  223. void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView)
  224. {
  225. ignoreUnused (window, comp, isNSView);
  226. #if ! JUCE_64BIT
  227. if (! isNSView)
  228. comp->setVisible ([((NSWindow*) window) isVisible]);
  229. #endif
  230. }
  231. JUCE_API bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView);
  232. bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView)
  233. {
  234. #if ! JUCE_64BIT
  235. if (! isNSView)
  236. {
  237. NSWindow* win = [(NSView*) comp->getWindowHandle() window];
  238. [[win parentWindow] makeKeyWindow];
  239. repostCurrentNSEvent();
  240. return true;
  241. }
  242. #endif
  243. ignoreUnused (comp, isNSView);
  244. return false;
  245. }
  246. } // (juce namespace)
  247. #endif
  248. #endif