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.

320 lines
11KB

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