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.

318 lines
11KB

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