/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #include "../../juce_core/system/juce_TargetPlatform.h" #if JUCE_MAC #include "../utility/juce_CheckSettingMacros.h" #if JucePlugin_Build_VST || JucePlugin_Build_VST3 #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1 #include "../utility/juce_IncludeSystemHeaders.h" #include "../utility/juce_IncludeModuleHeaders.h" #include "../utility/juce_FakeMouseMoveGenerator.h" #include "../utility/juce_CarbonVisibility.h" //============================================================================== namespace juce { #if ! JUCE_64BIT JUCE_API void updateEditorCompBoundsVST (Component*); void updateEditorCompBoundsVST (Component* comp) { HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); HIRect r; HIViewGetFrame (dummyView, &r); HIViewRef root; HIViewFindByID (HIViewGetRoot (HIViewGetWindow (dummyView)), kHIViewWindowContentID, &root); HIViewConvertRect (&r, HIViewGetSuperview (dummyView), root); Rect windowPos; GetWindowBounds (HIViewGetWindow (dummyView), kWindowContentRgn, &windowPos); comp->setTopLeftPosition ((int) (windowPos.left + r.origin.x), (int) (windowPos.top + r.origin.y)); } static pascal OSStatus viewBoundsChangedEvent (EventHandlerCallRef, EventRef, void* user) { updateEditorCompBoundsVST ((Component*) user); return noErr; } static bool shouldManuallyCloseHostWindow() { return getHostType().isCubase7orLater() || getHostType().isRenoise() || ((SystemStats::getOperatingSystemType() & 0xff) >= 12); } #endif //============================================================================== JUCE_API void initialiseMacVST(); void initialiseMacVST() { #if ! JUCE_64BIT NSApplicationLoad(); #endif } JUCE_API void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView); void* attachComponentToWindowRefVST (Component* comp, void* parentWindowOrView, bool isNSView) { JUCE_AUTORELEASEPOOL { #if ! JUCE_64BIT if (! isNSView) { NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView]; if (shouldManuallyCloseHostWindow()) { [hostWindow setReleasedWhenClosed: NO]; } else { [hostWindow retain]; [hostWindow setReleasedWhenClosed: YES]; } [hostWindow setCanHide: YES]; HIViewRef parentView = 0; WindowAttributes attributes; GetWindowAttributes ((WindowRef) parentWindowOrView, &attributes); if ((attributes & kWindowCompositingAttribute) != 0) { HIViewRef root = HIViewGetRoot ((WindowRef) parentWindowOrView); HIViewFindByID (root, kHIViewWindowContentID, &parentView); if (parentView == 0) parentView = root; } else { GetRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView); if (parentView == 0) CreateRootControl ((WindowRef) parentWindowOrView, (ControlRef*) &parentView); } // It seems that the only way to successfully position our overlaid window is by putting a dummy // HIView into the host's carbon window, and then catching events to see when it gets repositioned HIViewRef dummyView = 0; HIImageViewCreate (0, &dummyView); HIRect r = { {0, 0}, { (float) comp->getWidth(), (float) comp->getHeight()} }; HIViewSetFrame (dummyView, &r); HIViewAddSubview (parentView, dummyView); comp->getProperties().set ("dummyViewRef", String::toHexString ((pointer_sized_int) (void*) dummyView)); EventHandlerRef ref; const EventTypeSpec kControlBoundsChangedEvent = { kEventClassControl, kEventControlBoundsChanged }; InstallEventHandler (GetControlEventTarget (dummyView), NewEventHandlerUPP (viewBoundsChangedEvent), 1, &kControlBoundsChangedEvent, (void*) comp, &ref); comp->getProperties().set ("boundsEventRef", String::toHexString ((pointer_sized_int) (void*) ref)); updateEditorCompBoundsVST (comp); #if ! JucePlugin_EditorRequiresKeyboardFocus comp->addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses); #else comp->addToDesktop (ComponentPeer::windowIsTemporary); #endif comp->setVisible (true); comp->toFront (false); NSView* pluginView = (NSView*) comp->getWindowHandle(); NSWindow* pluginWindow = [pluginView window]; [pluginWindow setExcludedFromWindowsMenu: YES]; [pluginWindow setCanHide: YES]; [hostWindow addChildWindow: pluginWindow ordered: NSWindowAbove]; [hostWindow orderFront: nil]; [pluginWindow orderFront: nil]; attachWindowHidingHooks (comp, (WindowRef) parentWindowOrView, hostWindow); return hostWindow; } #endif ignoreUnused (isNSView); NSView* parentView = [(NSView*) parentWindowOrView retain]; #if JucePlugin_EditorRequiresKeyboardFocus comp->addToDesktop (0, parentView); #else comp->addToDesktop (ComponentPeer::windowIgnoresKeyPresses, parentView); #endif // (this workaround is because Wavelab provides a zero-size parent view..) if ([parentView frame].size.height == 0) [((NSView*) comp->getWindowHandle()) setFrameOrigin: NSZeroPoint]; comp->setVisible (true); comp->toFront (false); [[parentView window] setAcceptsMouseMovedEvents: YES]; return parentView; } } JUCE_API void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView); void detachComponentFromWindowRefVST (Component* comp, void* window, bool isNSView) { JUCE_AUTORELEASEPOOL { #if ! JUCE_64BIT if (! isNSView) { EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int) comp->getProperties() ["boundsEventRef"].toString().getHexValue64(); RemoveEventHandler (ref); removeWindowHidingHooks (comp); HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) comp->getProperties() ["dummyViewRef"].toString().getHexValue64(); if (HIViewIsValid (dummyView)) CFRelease (dummyView); NSWindow* hostWindow = (NSWindow*) window; NSView* pluginView = (NSView*) comp->getWindowHandle(); NSWindow* pluginWindow = [pluginView window]; [pluginView retain]; [hostWindow removeChildWindow: pluginWindow]; [pluginWindow close]; comp->removeFromDesktop(); [pluginView release]; if (shouldManuallyCloseHostWindow()) [hostWindow close]; else [hostWindow release]; #if JUCE_MODAL_LOOPS_PERMITTED static bool needToRunMessageLoop = ! getHostType().isReaper(); // The event loop needs to be run between closing the window and deleting the plugin, // presumably to let the cocoa objects get tidied up. Leaving out this line causes crashes // in Live when you delete the plugin with its window open. // (Doing it this way rather than using a single longer timout means that we can guarantee // how many messages will be dispatched, which seems to be vital in Reaper) if (needToRunMessageLoop) for (int i = 20; --i >= 0;) MessageManager::getInstance()->runDispatchLoopUntil (1); #endif return; } #endif ignoreUnused (isNSView); comp->removeFromDesktop(); [(id) window release]; } } JUCE_API void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView); void setNativeHostWindowSizeVST (void* window, Component* component, int newWidth, int newHeight, bool isNSView) { JUCE_AUTORELEASEPOOL { #if ! JUCE_64BIT if (! isNSView) { if (HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int) component->getProperties() ["dummyViewRef"].toString().getHexValue64()) { HIRect frameRect; HIViewGetFrame (dummyView, &frameRect); frameRect.size.width = newWidth; frameRect.size.height = newHeight; HIViewSetFrame (dummyView, &frameRect); } return; } #endif ignoreUnused (isNSView); if (NSView* hostView = (NSView*) window) { const int dx = newWidth - component->getWidth(); const int dy = newHeight - component->getHeight(); NSRect r = [hostView frame]; r.size.width += dx; r.size.height += dy; r.origin.y -= dy; [hostView setFrame: r]; } } } JUCE_API void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView); void checkWindowVisibilityVST (void* window, Component* comp, bool isNSView) { ignoreUnused (window, comp, isNSView); #if ! JUCE_64BIT if (! isNSView) comp->setVisible ([((NSWindow*) window) isVisible]); #endif } JUCE_API bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView); bool forwardCurrentKeyEventToHostVST (Component* comp, bool isNSView) { #if ! JUCE_64BIT if (! isNSView) { NSWindow* win = [(NSView*) comp->getWindowHandle() window]; [[win parentWindow] makeKeyWindow]; repostCurrentNSEvent(); return true; } #endif ignoreUnused (comp, isNSView); return false; } } // (juce namespace) #endif #endif