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.

309 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../../juce_core/system/juce_TargetPlatform.h"
  18. #include "../utility/juce_CheckSettingMacros.h"
  19. #if JucePlugin_Build_VST || JucePlugin_Build_VST3
  20. #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1
  21. #include "../utility/juce_IncludeSystemHeaders.h"
  22. #include "../utility/juce_IncludeModuleHeaders.h"
  23. #include "../utility/juce_FakeMouseMoveGenerator.h"
  24. #include "../utility/juce_CarbonVisibility.h"
  25. //==============================================================================
  26. namespace juce
  27. {
  28. #if ! JUCE_64BIT
  29. void updateEditorCompBoundsVST (Component*);
  30. void updateEditorCompBoundsVST (Component* comp)
  31. {
  32. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  33. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  34. HIRect r;
  35. HIViewGetFrame (dummyView, &r);
  36. HIViewRef root;
  37. HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root);
  38. HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root);
  39. Rect windowPos;
  40. GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos);
  41. comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x),
  42. (int) (windowPos.top + r.origin.y));
  43. }
  44. static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user)
  45. {
  46. updateEditorCompBoundsVST ((Component*) user);
  47. return noErr;
  48. }
  49. #endif
  50. //==============================================================================
  51. void initialiseMacVST();
  52. void initialiseMacVST()
  53. {
  54. #if ! JUCE_64BIT
  55. NSApplicationLoad();
  56. #endif
  57. }
  58. void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView);
  59. void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView)
  60. {
  61. JUCE_AUTORELEASEPOOL
  62. {
  63. #if ! JUCE_64BIT
  64. if (! isNSView)
  65. {
  66. NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView];
  67. if (getHostType().isCubase7orLater())
  68. {
  69. [hostWindow setReleasedWhenClosed: NO];
  70. }
  71. else
  72. {
  73. [hostWindow retain];
  74. [hostWindow setReleasedWhenClosed: YES];
  75. }
  76. [hostWindow setCanHide: YES];
  77. HIViewRef parentView = 0;
  78. WindowAttributes attributes;
  79. GetWindowAttributes ((WindowRef) parentWindowOrView, &attributes);
  80. if ((attributes & kWindowCompositingAttribute) != 0)
  81. {
  82. HIViewRef root = HIViewGetRoot ((WindowRef) parentWindowOrView);
  83. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  84. if (parentView == 0)
  85. parentView = root;
  86. }
  87. else
  88. {
  89. GetRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  90. if (parentView == 0)
  91. CreateRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView);
  92. }
  93. // It seems that the only way to successfully position our overlaid window is by putting a dummy
  94. // HIView into the host's carbon window, and then catching events to see when it gets repositioned
  95. HIViewRef dummyView = 0;
  96. HIImageViewCreate (0, &dummyView);
  97. HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} };
  98. HIViewSetFrame (dummyView, &r);
  99. HIViewAddSubview (parentView, dummyView);
  100. comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView));
  101. EventHandlerRef ref;
  102. const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged };
  103. InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref);
  104. comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref));
  105. updateEditorCompBoundsVST (comp);
  106. #if ! JucePlugin_EditorRequiresKeyboardFocus
  107. comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  108. #else
  109. comp->addToDesktop (ComponentPeer::windowIsTemporary);
  110. #endif
  111. comp->setVisible (true);
  112. comp->toFront (false);
  113. NSView* pluginView = (NSView*) comp->getWindowHandle();
  114. NSWindow* pluginWindow = [pluginView window];
  115. [pluginWindow setExcludedFromWindowsMenu: YES];
  116. [pluginWindow setCanHide: YES];
  117. [hostWindow addChildWindow: pluginWindow
  118. ordered: NSWindowAbove];
  119. [hostWindow orderFront: nil];
  120. [pluginWindow orderFront: nil];
  121. attachWindowHidingHooks (comp, (WindowRef) parentWindowOrView, hostWindow);
  122. return hostWindow;
  123. }
  124. #endif
  125. ignoreUnused (isNSView);
  126. NSView* parentView = [(NSView*) parentWindowOrView retain];
  127. #if JucePlugin_EditorRequiresKeyboardFocus
  128. comp->addToDesktop (0, parentView);
  129. #else
  130. comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView);
  131. #endif
  132. // (this workaround is because Wavelab provides a zero-size parent view..)
  133. if ([parentView frame].size.height == 0)
  134. [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
  135. comp->setVisible (true);
  136. comp->toFront (false);
  137. [[parentView window] setAcceptsMouseMovedEvents: YES];
  138. return parentView;
  139. }
  140. }
  141. void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView);
  142. void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView)
  143. {
  144. JUCE_AUTORELEASEPOOL
  145. {
  146. #if ! JUCE_64BIT
  147. if (! isNSView)
  148. {
  149. EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
  150. comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
  151. RemoveEventHandler (ref);
  152. removeWindowHidingHooks (comp);
  153. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  154. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  155. if (HIViewIsValid (dummyView))
  156. CFRelease (dummyView);
  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 (getHostType().isCubase7orLater())
  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 timout 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. ignoreUnused (isNSView);
  184. comp->removeFromDesktop();
  185. [(id) window release];
  186. }
  187. }
  188. void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView);
  189. void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView)
  190. {
  191. JUCE_AUTORELEASEPOOL
  192. {
  193. #if ! JUCE_64BIT
  194. if (! isNSView)
  195. {
  196. if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  197. component->getProperties() ["dummyViewRef"].toString().getHexValue64())
  198. {
  199. HIRect frameRect;
  200. HIViewGetFrame (dummyView, &frameRect);
  201. frameRect.size.width = newWidth;
  202. frameRect.size.height = newHeight;
  203. HIViewSetFrame (dummyView, &frameRect);
  204. }
  205. return;
  206. }
  207. #endif
  208. ignoreUnused (isNSView);
  209. if (NSView* hostView = (NSView*) window)
  210. {
  211. const int dx = newWidth - component->getWidth();
  212. const int dy = newHeight - component->getHeight();
  213. NSRect r = [hostView frame];
  214. r.size.width += dx;
  215. r.size.height += dy;
  216. r.origin.y -= dy;
  217. [hostView setFrame: r];
  218. }
  219. }
  220. }
  221. void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView);
  222. void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView)
  223. {
  224. ignoreUnused (window, comp, isNSView);
  225. #if ! JUCE_64BIT
  226. if (! isNSView)
  227. comp->setVisible ([((NSWindow*) window) isVisible]);
  228. #endif
  229. }
  230. bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView);
  231. bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView)
  232. {
  233. #if ! JUCE_64BIT
  234. if (! isNSView)
  235. {
  236. NSWindow* win = [(NSView*) comp->getWindowHandle() window];
  237. [[win parentWindow] makeKeyWindow];
  238. repostCurrentNSEvent();
  239. return true;
  240. }
  241. #endif
  242. ignoreUnused (comp, isNSView);
  243. return false;
  244. }
  245. } // (juce namespace)
  246. #endif