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.

274 lines
10KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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. // Your project must contain an AppConfig.h file with your project-specific settings in it,
  18. // and your header search path must make it accessible to the module's files.
  19. #include "AppConfig.h"
  20. #include "../utility/juce_CheckSettingMacros.h"
  21. #if 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_FakeMouseMoveGenerator.h"
  26. #include "../utility/juce_CarbonVisibility.h"
  27. #undef Component
  28. #undef Point
  29. //==============================================================================
  30. namespace juce
  31. {
  32. static void initialiseMac()
  33. {
  34. #if ! JUCE_64BIT
  35. NSApplicationLoad();
  36. #endif
  37. }
  38. #if ! JUCE_64BIT
  39. static void updateComponentPos (Component* const comp)
  40. {
  41. DBG ("updateComponentPos()");
  42. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  43. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  44. HIRect r;
  45. HIViewGetFrame (dummyView, &r);
  46. HIViewRef root;
  47. HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root);
  48. HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root);
  49. Rect windowPos;
  50. GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos);
  51. comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x),
  52. (int) (windowPos.top + r.origin.y));
  53. }
  54. static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user)
  55. {
  56. updateComponentPos ((Component*) user);
  57. return noErr;
  58. }
  59. #endif
  60. static void* attachComponentToWindowRef (Component* comp, void* windowRef, bool isHIView)
  61. {
  62. DBG ("attachComponentToWindowRef()");
  63. JUCE_AUTORELEASEPOOL
  64. {
  65. #if JUCE_64BIT
  66. (void) isHIView;
  67. NSView* parentView = (NSView*) windowRef;
  68. #if JucePlugin_EditorRequiresKeyboardFocus
  69. comp->addToDesktop (0, parentView);
  70. #else
  71. comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView);
  72. #endif
  73. // (this workaround is because Wavelab provides a zero-size parent view..)
  74. if ([parentView frame].size.height == 0)
  75. [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
  76. comp->setVisible (true);
  77. comp->toFront (false);
  78. [[parentView window] setAcceptsMouseMovedEvents: YES];
  79. return parentView;
  80. #else
  81. //treat NSView like 64bit
  82. if (! isHIView)
  83. {
  84. NSView* parentView = (NSView*) windowRef;
  85. #if JucePlugin_EditorRequiresKeyboardFocus
  86. comp->addToDesktop (0, parentView);
  87. #else
  88. comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView);
  89. #endif
  90. // (this workaround is because Wavelab provides a zero-size parent view..)
  91. if ([parentView frame].size.height == 0)
  92. [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint];
  93. comp->setVisible (true);
  94. comp->toFront (false);
  95. [[parentView window] setAcceptsMouseMovedEvents: YES];
  96. return parentView;
  97. }
  98. NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];
  99. [hostWindow retain];
  100. [hostWindow setCanHide: YES];
  101. [hostWindow setReleasedWhenClosed: YES];
  102. HIViewRef parentView = nullptr;
  103. WindowAttributes attributes;
  104. GetWindowAttributes ((WindowRef) windowRef, &attributes);
  105. if ((attributes & kWindowCompositingAttribute) != 0)
  106. {
  107. HIViewRef root = HIViewGetRoot ((WindowRef) windowRef);
  108. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  109. if (parentView == nullptr)
  110. parentView = root;
  111. }
  112. else
  113. {
  114. GetRootControl ((WindowRef) windowRef, (ControlRef*) &parentView);
  115. if (parentView == nullptr)
  116. CreateRootControl ((WindowRef) windowRef, (ControlRef*) &parentView);
  117. }
  118. // It seems that the only way to successfully position our overlaid window is by putting a dummy
  119. // HIView into the host's carbon window, and then catching events to see when it gets repositioned
  120. HIViewRef dummyView = 0;
  121. HIImageViewCreate (0, &dummyView);
  122. HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} };
  123. HIViewSetFrame (dummyView, &r);
  124. HIViewAddSubview (parentView, dummyView);
  125. comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView));
  126. EventHandlerRef ref;
  127. const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged };
  128. InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref);
  129. comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref));
  130. updateComponentPos (comp);
  131. #if ! JucePlugin_EditorRequiresKeyboardFocus
  132. comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  133. #else
  134. comp->addToDesktop (ComponentPeer::windowIsTemporary);
  135. #endif
  136. comp->setVisible (true);
  137. comp->toFront (false);
  138. NSView* pluginView = (NSView*) comp->getWindowHandle();
  139. NSWindow* pluginWindow = [pluginView window];
  140. [pluginWindow setExcludedFromWindowsMenu: YES];
  141. [pluginWindow setCanHide: YES];
  142. [hostWindow addChildWindow: pluginWindow
  143. ordered: NSWindowAbove];
  144. [hostWindow orderFront: nil];
  145. [pluginWindow orderFront: nil];
  146. attachWindowHidingHooks (comp, (WindowRef) windowRef, hostWindow);
  147. return hostWindow;
  148. #endif
  149. }
  150. }
  151. static void detachComponentFromWindowRef (Component* comp, void* nsWindow, bool isHIView)
  152. {
  153. JUCE_AUTORELEASEPOOL
  154. {
  155. #if ! JUCE_64BIT
  156. if (isHIView)
  157. {
  158. JUCE_AUTORELEASEPOOL
  159. {
  160. EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
  161. comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
  162. RemoveEventHandler (ref);
  163. removeWindowHidingHooks (comp);
  164. HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
  165. comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
  166. if (HIViewIsValid (dummyView))
  167. CFRelease (dummyView);
  168. NSWindow* hostWindow = (NSWindow*) nsWindow;
  169. NSView* pluginView = (NSView*) comp->getWindowHandle();
  170. NSWindow* pluginWindow = [pluginView window];
  171. [hostWindow removeChildWindow: pluginWindow];
  172. comp->removeFromDesktop();
  173. [hostWindow release];
  174. }
  175. // The event loop needs to be run between closing the window and deleting the plugin,
  176. // presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes
  177. // in Live and Reaper when you delete the plugin with its window open.
  178. // (Doing it this way rather than using a single longer timout means that we can guarantee
  179. // how many messages will be dispatched, which seems to be vital in Reaper)
  180. for (int i = 20; --i >= 0;)
  181. MessageManager::getInstance()->runDispatchLoopUntil (1);
  182. return;
  183. }
  184. #endif
  185. (void) nsWindow; (void) isHIView;
  186. comp->removeFromDesktop();
  187. }
  188. }
  189. static void setNativeHostWindowSize (void* nsWindow, Component* component, int newWidth, int newHeight, bool isHIView)
  190. {
  191. JUCE_AUTORELEASEPOOL
  192. {
  193. #if ! JUCE_64BIT
  194. if (isHIView)
  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. (void) nsWindow; (void) isHIView;
  209. component->setSize (newWidth, newHeight);
  210. }
  211. }
  212. } // (juce namespace)
  213. #endif