Audio plugin host https://kx.studio/carla
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.

juce_mac_CarbonViewWrapperComponent.h 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. #ifndef JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_H_INCLUDED
  18. #define JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_H_INCLUDED
  19. //==============================================================================
  20. /**
  21. Creates a floating carbon window that can be used to hold a carbon UI.
  22. This is a handy class that's designed to be inlined where needed, e.g.
  23. in the audio plugin hosting code.
  24. */
  25. class CarbonViewWrapperComponent : public Component,
  26. public ComponentMovementWatcher,
  27. public Timer
  28. {
  29. public:
  30. CarbonViewWrapperComponent()
  31. : ComponentMovementWatcher (this),
  32. keepPluginWindowWhenHidden (false),
  33. wrapperWindow (0),
  34. carbonWindow (0),
  35. embeddedView (0),
  36. recursiveResize (false),
  37. repaintChildOnCreation (true)
  38. {
  39. }
  40. ~CarbonViewWrapperComponent()
  41. {
  42. jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
  43. }
  44. virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
  45. virtual void removeView (HIViewRef embeddedView) = 0;
  46. virtual void handleMouseDown (int, int) {}
  47. virtual void handlePaint() {}
  48. virtual bool getEmbeddedViewSize (int& w, int& h)
  49. {
  50. if (embeddedView == 0)
  51. return false;
  52. HIRect bounds;
  53. HIViewGetBounds (embeddedView, &bounds);
  54. w = jmax (1, roundToInt (bounds.size.width));
  55. h = jmax (1, roundToInt (bounds.size.height));
  56. return true;
  57. }
  58. void createWindow()
  59. {
  60. if (wrapperWindow == 0)
  61. {
  62. Rect r;
  63. r.left = (short) getScreenX();
  64. r.top = (short) getScreenY();
  65. r.right = (short) (r.left + getWidth());
  66. r.bottom = (short) (r.top + getHeight());
  67. CreateNewWindow (kDocumentWindowClass,
  68. (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
  69. | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
  70. &r, &wrapperWindow);
  71. jassert (wrapperWindow != 0);
  72. if (wrapperWindow == 0)
  73. return;
  74. carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
  75. [getOwnerWindow() addChildWindow: carbonWindow
  76. ordered: NSWindowAbove];
  77. embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
  78. // Check for the plugin creating its own floating window, and if there is one,
  79. // we need to reparent it to make it visible..
  80. if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
  81. [getOwnerWindow() addChildWindow: floatingChildWindow
  82. ordered: NSWindowAbove];
  83. EventTypeSpec windowEventTypes[] =
  84. {
  85. { kEventClassWindow, kEventWindowGetClickActivation },
  86. { kEventClassWindow, kEventWindowHandleDeactivate },
  87. { kEventClassWindow, kEventWindowBoundsChanging },
  88. { kEventClassMouse, kEventMouseDown },
  89. { kEventClassMouse, kEventMouseMoved },
  90. { kEventClassMouse, kEventMouseDragged },
  91. { kEventClassMouse, kEventMouseUp },
  92. { kEventClassWindow, kEventWindowDrawContent },
  93. { kEventClassWindow, kEventWindowShown },
  94. { kEventClassWindow, kEventWindowHidden }
  95. };
  96. EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
  97. InstallWindowEventHandler (wrapperWindow, upp,
  98. sizeof (windowEventTypes) / sizeof (EventTypeSpec),
  99. windowEventTypes, this, &eventHandlerRef);
  100. setOurSizeToEmbeddedViewSize();
  101. setEmbeddedWindowToOurSize();
  102. creationTime = Time::getCurrentTime();
  103. }
  104. }
  105. void deleteWindow()
  106. {
  107. removeView (embeddedView);
  108. embeddedView = 0;
  109. if (wrapperWindow != 0)
  110. {
  111. NSWindow* ownerWindow = getOwnerWindow();
  112. if ([[ownerWindow childWindows] count] > 0)
  113. {
  114. [ownerWindow removeChildWindow: carbonWindow];
  115. [carbonWindow close];
  116. }
  117. RemoveEventHandler (eventHandlerRef);
  118. DisposeWindow (wrapperWindow);
  119. wrapperWindow = 0;
  120. }
  121. }
  122. //==============================================================================
  123. void setOurSizeToEmbeddedViewSize()
  124. {
  125. int w, h;
  126. if (getEmbeddedViewSize (w, h))
  127. {
  128. if (w != getWidth() || h != getHeight())
  129. {
  130. startTimer (50);
  131. setSize (w, h);
  132. if (Component* p = getParentComponent())
  133. p->setSize (w, h);
  134. }
  135. else
  136. {
  137. startTimer (jlimit (50, 500, getTimerInterval() + 20));
  138. }
  139. }
  140. else
  141. {
  142. stopTimer();
  143. }
  144. }
  145. void setEmbeddedWindowToOurSize()
  146. {
  147. if (! recursiveResize)
  148. {
  149. recursiveResize = true;
  150. if (embeddedView != 0)
  151. {
  152. HIRect r;
  153. r.origin.x = 0;
  154. r.origin.y = 0;
  155. r.size.width = (float) getWidth();
  156. r.size.height = (float) getHeight();
  157. HIViewSetFrame (embeddedView, &r);
  158. }
  159. if (wrapperWindow != 0)
  160. {
  161. jassert (getTopLevelComponent()->getDesktopScaleFactor() == 1.0f);
  162. Rectangle<int> screenBounds (getScreenBounds() * Desktop::getInstance().getGlobalScaleFactor());
  163. Rect wr;
  164. wr.left = (short) screenBounds.getX();
  165. wr.top = (short) screenBounds.getY();
  166. wr.right = (short) screenBounds.getRight();
  167. wr.bottom = (short) screenBounds.getBottom();
  168. SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
  169. // This group stuff is mainly a workaround for Mackie plugins like FinalMix..
  170. WindowGroupRef group = GetWindowGroup (wrapperWindow);
  171. WindowRef attachedWindow;
  172. if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
  173. {
  174. SelectWindow (attachedWindow);
  175. ActivateWindow (attachedWindow, TRUE);
  176. HideWindow (wrapperWindow);
  177. }
  178. ShowWindow (wrapperWindow);
  179. }
  180. recursiveResize = false;
  181. }
  182. }
  183. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  184. {
  185. setEmbeddedWindowToOurSize();
  186. }
  187. // (overridden to intercept movements of the top-level window)
  188. void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override
  189. {
  190. ComponentMovementWatcher::componentMovedOrResized (component, wasMoved, wasResized);
  191. if (&component == getTopLevelComponent())
  192. setEmbeddedWindowToOurSize();
  193. }
  194. void componentPeerChanged() override
  195. {
  196. deleteWindow();
  197. createWindow();
  198. }
  199. void componentVisibilityChanged() override
  200. {
  201. if (isShowing())
  202. createWindow();
  203. else if (! keepPluginWindowWhenHidden)
  204. deleteWindow();
  205. setEmbeddedWindowToOurSize();
  206. }
  207. static void recursiveHIViewRepaint (HIViewRef view)
  208. {
  209. HIViewSetNeedsDisplay (view, true);
  210. HIViewRef child = HIViewGetFirstSubview (view);
  211. while (child != 0)
  212. {
  213. recursiveHIViewRepaint (child);
  214. child = HIViewGetNextView (child);
  215. }
  216. }
  217. void timerCallback() override
  218. {
  219. if (isShowing())
  220. {
  221. setOurSizeToEmbeddedViewSize();
  222. // To avoid strange overpainting problems when the UI is first opened, we'll
  223. // repaint it a few times during the first second that it's on-screen..
  224. if (repaintChildOnCreation && (Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
  225. recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
  226. }
  227. }
  228. void setRepaintsChildHIViewWhenCreated (bool b) noexcept
  229. {
  230. repaintChildOnCreation = b;
  231. }
  232. OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
  233. {
  234. switch (GetEventKind (event))
  235. {
  236. case kEventWindowHandleDeactivate:
  237. ActivateWindow (wrapperWindow, TRUE);
  238. return noErr;
  239. case kEventWindowGetClickActivation:
  240. {
  241. getTopLevelComponent()->toFront (false);
  242. [carbonWindow makeKeyAndOrderFront: nil];
  243. ClickActivationResult howToHandleClick = kActivateAndHandleClick;
  244. SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
  245. sizeof (ClickActivationResult), &howToHandleClick);
  246. if (embeddedView != 0)
  247. HIViewSetNeedsDisplay (embeddedView, true);
  248. return noErr;
  249. }
  250. }
  251. return eventNotHandledErr;
  252. }
  253. static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
  254. {
  255. return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
  256. }
  257. bool keepPluginWindowWhenHidden;
  258. protected:
  259. WindowRef wrapperWindow;
  260. NSWindow* carbonWindow;
  261. HIViewRef embeddedView;
  262. bool recursiveResize, repaintChildOnCreation;
  263. Time creationTime;
  264. EventHandlerRef eventHandlerRef;
  265. NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
  266. };
  267. #endif // JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_H_INCLUDED