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.

312 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #include "../../juce_core/system/juce_TargetPlatform.h"
  14. #if JUCE_MAC
  15. #include "../utility/juce_CheckSettingMacros.h"
  16. #if JucePlugin_Build_VST || JucePlugin_Build_VST3
  17. #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1
  18. #include "../utility/juce_IncludeSystemHeaders.h"
  19. #include "../utility/juce_IncludeModuleHeaders.h"
  20. #include "../utility/juce_FakeMouseMoveGenerator.h"
  21. #include "../utility/juce_CarbonVisibility.h"
  22. //==============================================================================
  23. namespace juce
  24. {
  25. #if ! JUCE_64BIT
  26. JUCE_API void updateEditorCompBoundsVST (Component*);
  27. void updateEditorCompBoundsVST (Component* comp)
  28. {
  29. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  30. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  31. HIRect r;
  32. HIViewGetFrame (dummyView, &r);
  33. HIViewRef root;
  34. HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root);
  35. HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root);
  36. Rect windowPos;
  37. GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos);
  38. comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x),
  39. (int) (windowPos.top + r.origin.y));
  40. }
  41. static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user)
  42. {
  43. updateEditorCompBoundsVST ((Component*) user);
  44. return noErr;
  45. }
  46. static bool shouldManuallyCloseHostWindow()
  47. {
  48. return getHostType().isCubase7orLater() || getHostType().isRenoise() || ((SystemStats::getOperatingSystemType() & 0xff) >= 12);
  49. }
  50. #endif
  51. //==============================================================================
  52. JUCE_API void initialiseMacVST();
  53. void initialiseMacVST()
  54. {
  55. #if ! JUCE_64BIT
  56. NSApplicationLoad();
  57. #endif
  58. }
  59. JUCE_API void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView);
  60. void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView)
  61. {
  62. JUCE_AUTORELEASEPOOL
  63. {
  64. #if ! JUCE_64BIT
  65. if (! isNSView)
  66. {
  67. NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView];
  68. if (shouldManuallyCloseHostWindow())
  69. {
  70. [hostWindow setReleasedWhenClosed: NO];
  71. }
  72. else
  73. {
  74. [hostWindow retain];
  75. [hostWindow setReleasedWhenClosed: YES];
  76. }
  77. [hostWindow setCanHide: YES];
  78. HIViewRef parentView = 0;
  79. WindowAttributes attributes;
  80. GetWindowAttributes ((WindowRef) parentWindowOrView, &attributes);
  81. if ((attributes & kWindowCompositingAttribute) != 0)
  82. {
  83. HIViewRef root = HIViewGetRoot ((WindowRef) parentWindowOrView);
  84. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  85. if (parentView == 0)
  86. parentView = root;
  87. }
  88. else
  89. {
  90. GetRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  91. if (parentView == 0)
  92. CreateRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  93. }
  94. // It seems that the only way to successfully position our overlaid window is by putting a dummy
  95. // HIView into the host's carbon window, and then catching events to see when it gets repositioned
  96. HIViewRef dummyView = 0;
  97. HIImageViewCreate (0, &dummyView);
  98. HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} };
  99. HIViewSetFrame (dummyView, &r);
  100. HIViewAddSubview (parentView, dummyView);
  101. comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView));
  102. EventHandlerRef ref;
  103. const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged };
  104. InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref);
  105. comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref));
  106. updateEditorCompBoundsVST (comp);
  107. #if ! JucePlugin_EditorRequiresKeyboardFocus
  108. comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  109. #else
  110. comp->addToDesktop (ComponentPeer::windowIsTemporary);
  111. #endif
  112. comp->setVisible (true);
  113. comp->toFront (false);
  114. NSView* pluginView = (NSView*) comp->getWindowHandle();
  115. NSWindow* pluginWindow = [pluginView window];
  116. [pluginWindow setExcludedFromWindowsMenu: YES];
  117. [pluginWindow setCanHide: YES];
  118. [hostWindow addChildWindow: pluginWindow
  119. ordered: NSWindowAbove];
  120. [hostWindow orderFront: nil];
  121. [pluginWindow orderFront: nil];
  122. attachWindowHidingHooks (comp, (WindowRef) parentWindowOrView, hostWindow);
  123. return hostWindow;
  124. }
  125. #endif
  126. ignoreUnused (isNSView);
  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, 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. removeWindowHidingHooks (comp);
  154. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  155. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  156. if (HIViewIsValid (dummyView))
  157. CFRelease (dummyView);
  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