1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00

VST plugins: added plugin-side support for the hasCockosViewAsConfig extension.

This commit is contained in:
jules 2014-01-23 22:11:14 +00:00
parent 0937ee4c37
commit 388065788b
2 changed files with 207 additions and 169 deletions

View file

@ -111,11 +111,11 @@ namespace juce
{
#if JUCE_MAC
extern void initialiseMac();
extern void* attachComponentToWindowRef (Component*, void* windowRef);
extern void detachComponentFromWindowRef (Component*, void* nsWindow);
extern void setNativeHostWindowSize (void* nsWindow, Component*, int newWidth, int newHeight);
extern void checkWindowVisibility (void* nsWindow, Component*);
extern bool forwardCurrentKeyEventToHost (Component*);
extern void* attachComponentToWindowRef (Component*, void* parent, bool isNSView);
extern void detachComponentFromWindowRef (Component*, void* window, bool isNSView);
extern void setNativeHostWindowSize (void* window, Component*, int newWidth, int newHeight, bool isNSView);
extern void checkWindowVisibility (void* window, Component*, bool isNSView);
extern bool forwardCurrentKeyEventToHost (Component*, bool isNSView);
#if ! JUCE_64BIT
extern void updateEditorCompBounds (Component*);
#endif
@ -283,6 +283,11 @@ public:
hasShutdown (false),
firstProcessCallback (true),
shouldDeleteEditor (false),
#if JUCE_64BIT
useNSView (true),
#else
useNSView (false),
#endif
processTempBuffer (1, 1),
hostWindow (0)
{
@ -366,61 +371,70 @@ public:
}
//==============================================================================
bool getEffectName (char* name)
bool getEffectName (char* name) override
{
String (JucePlugin_Name).copyToUTF8 (name, 64);
return true;
}
bool getVendorString (char* text)
bool getVendorString (char* text) override
{
String (JucePlugin_Manufacturer).copyToUTF8 (text, 64);
return true;
}
bool getProductString (char* text) { return getEffectName (text); }
VstInt32 getVendorVersion() { return convertHexVersionToDecimal (JucePlugin_VersionCode); }
VstPlugCategory getPlugCategory() { return JucePlugin_VSTCategory; }
bool keysRequired() { return (JucePlugin_EditorRequiresKeyboardFocus) != 0; }
bool getProductString (char* text) override { return getEffectName (text); }
VstInt32 getVendorVersion() override { return convertHexVersionToDecimal (JucePlugin_VersionCode); }
VstPlugCategory getPlugCategory() override { return JucePlugin_VSTCategory; }
bool keysRequired() { return (JucePlugin_EditorRequiresKeyboardFocus) != 0; }
VstInt32 canDo (char* text)
VstInt32 canDo (char* text) override
{
VstInt32 result = 0;
if (strcmp (text, "receiveVstEvents") == 0
|| strcmp (text, "receiveVstMidiEvent") == 0
|| strcmp (text, "receiveVstMidiEvents") == 0)
|| strcmp (text, "receiveVstMidiEvent") == 0
|| strcmp (text, "receiveVstMidiEvents") == 0)
{
#if JucePlugin_WantsMidiInput
result = 1;
return 1;
#else
result = -1;
return -1;
#endif
}
else if (strcmp (text, "sendVstEvents") == 0
|| strcmp (text, "sendVstMidiEvent") == 0
|| strcmp (text, "sendVstMidiEvents") == 0)
if (strcmp (text, "sendVstEvents") == 0
|| strcmp (text, "sendVstMidiEvent") == 0
|| strcmp (text, "sendVstMidiEvents") == 0)
{
#if JucePlugin_ProducesMidiOutput
result = 1;
return 1;
#else
result = -1;
return -1;
#endif
}
else if (strcmp (text, "receiveVstTimeInfo") == 0
|| strcmp (text, "conformsToWindowRules") == 0
|| strcmp (text, "bypass") == 0)
if (strcmp (text, "receiveVstTimeInfo") == 0
|| strcmp (text, "conformsToWindowRules") == 0
|| strcmp (text, "bypass") == 0)
{
result = 1;
return 1;
}
else if (strcmp (text, "openCloseAnyThread") == 0)
if (strcmp (text, "openCloseAnyThread") == 0)
{
// This tells Wavelab to use the UI thread to invoke open/close,
// like all other hosts do.
result = -1;
return -1;
}
return result;
#if JUCE_MAC
if (strcmp (text, "hasCockosViewAsConfig") == 0)
{
useNSView = true;
return 0xbeef0000;
}
#endif
return 0;
}
bool getInputProperties (VstInt32 index, VstPinProperties* properties)
@ -1014,7 +1028,7 @@ public:
#if JUCE_MAC
if (hostWindow != 0)
checkWindowVisibility (hostWindow, editorComp);
checkWindowVisibility (hostWindow, editorComp, useNSView);
#endif
tryMasterIdle();
@ -1107,7 +1121,7 @@ public:
#if JUCE_MAC
if (hostWindow != 0)
{
detachComponentFromWindowRef (editorComp, hostWindow);
detachComponentFromWindowRef (editorComp, hostWindow, useNSView);
hostWindow = 0;
}
#endif
@ -1164,7 +1178,7 @@ public:
Window editorWnd = (Window) editorComp->getWindowHandle();
XReparentWindow (display, editorWnd, hostWindow, 0, 0);
#else
hostWindow = attachComponentToWindowRef (editorComp, ptr);
hostWindow = attachComponentToWindowRef (editorComp, ptr, useNSView);
#endif
editorComp->setVisible (true);
@ -1210,7 +1224,7 @@ public:
{
// some hosts don't support the sizeWindow call, so do it manually..
#if JUCE_MAC
setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight);
setNativeHostWindowSize (hostWindow, editorComp, newWidth, newHeight, useNSView);
#elif JUCE_LINUX
// (Currently, all linux hosts support sizeWindow, so this should never need to happen)
@ -1323,7 +1337,7 @@ public:
{
// If we have an unused keypress, move the key-focus to a host window
// and re-inject the event..
return forwardCurrentKeyEventToHost (this);
return forwardCurrentKeyEventToHost (this, wrapper.useNSView);
}
#endif
@ -1338,7 +1352,8 @@ public:
editor->setBounds (getLocalBounds());
#if JUCE_MAC && ! JUCE_64BIT
updateEditorCompBounds (this);
if (! wrapper.useNSView)
updateEditorCompBounds (this);
#endif
}
@ -1349,8 +1364,9 @@ public:
const int cw = child->getWidth();
const int ch = child->getHeight();
#if JUCE_MAC && JUCE_64BIT
setTopLeftPosition (0, getHeight() - ch);
#if JUCE_MAC
if (wrapper.useNSView)
setTopLeftPosition (0, getHeight() - ch);
#endif
wrapper.resizeHostWindow (cw, ch);
@ -1405,7 +1421,8 @@ private:
VSTMidiEventList outgoingEvents;
VstSpeakerArrangementType speakerIn, speakerOut;
int numInChans, numOutChans;
bool isProcessing, isBypassed, hasShutdown, firstProcessCallback, shouldDeleteEditor;
bool isProcessing, isBypassed, hasShutdown, firstProcessCallback;
bool shouldDeleteEditor, useNSView;
HeapBlock<float*> channels;
Array<float*> tempChannels; // see note in processReplacing()
AudioSampleBuffer processTempBuffer;

View file

@ -77,13 +77,82 @@ void initialiseMac()
#endif
}
void* attachComponentToWindowRef (Component* comp, void* windowRef);
void* attachComponentToWindowRef (Component* comp, void* windowRef)
void* attachComponentToWindowRef (Component* comp, void* parentWindowOrView, bool isNSView);
void* attachComponentToWindowRef (Component* comp, void* parentWindowOrView, bool isNSView)
{
JUCE_AUTORELEASEPOOL
{
#if JUCE_64BIT
NSView* parentView = (NSView*) windowRef;
#if ! JUCE_64BIT
if (! isNSView)
{
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: parentWindowOrView];
[hostWindow retain];
[hostWindow setCanHide: YES];
[hostWindow setReleasedWhenClosed: 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));
updateEditorCompBounds (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
(void) isNSView;
NSView* parentView = (NSView*) parentWindowOrView;
#if JucePlugin_EditorRequiresKeyboardFocus
comp->addToDesktop (0, parentView);
@ -100,168 +169,120 @@ void* attachComponentToWindowRef (Component* comp, void* windowRef)
[[parentView window] setAcceptsMouseMovedEvents: YES];
return parentView;
#else
NSWindow* hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];
[hostWindow retain];
[hostWindow setCanHide: YES];
[hostWindow setReleasedWhenClosed: YES];
HIViewRef parentView = 0;
WindowAttributes attributes;
GetWindowAttributes ((WindowRef) windowRef, &attributes);
if ((attributes & kWindowCompositingAttribute) != 0)
{
HIViewRef root = HIViewGetRoot ((WindowRef) windowRef);
HIViewFindByID (root, kHIViewWindowContentID, &parentView);
if (parentView == 0)
parentView = root;
}
else
{
GetRootControl ((WindowRef) windowRef, (ControlRef*) &parentView);
if (parentView == 0)
CreateRootControl ((WindowRef) windowRef, (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));
updateEditorCompBounds (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) windowRef, hostWindow);
return hostWindow;
#endif
}
}
void detachComponentFromWindowRef (Component* comp, void* nsWindow);
void detachComponentFromWindowRef (Component* comp, void* nsWindow)
void detachComponentFromWindowRef (Component* comp, void* window, bool isNSView);
void detachComponentFromWindowRef (Component* comp, void* window, bool isNSView)
{
JUCE_AUTORELEASEPOOL
{
#if JUCE_64BIT
comp->removeFromDesktop();
#else
EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
RemoveEventHandler (ref);
#if ! JUCE_64BIT
if (! isNSView)
{
EventHandlerRef ref = (EventHandlerRef) (void*) (pointer_sized_int)
comp->getProperties() ["boundsEventRef"].toString().getHexValue64();
RemoveEventHandler (ref);
removeWindowHidingHooks (comp);
removeWindowHidingHooks (comp);
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
HIViewRef dummyView = (HIViewRef) (void*) (pointer_sized_int)
comp->getProperties() ["dummyViewRef"].toString().getHexValue64();
if (HIViewIsValid (dummyView))
CFRelease (dummyView);
if (HIViewIsValid (dummyView))
CFRelease (dummyView);
NSWindow* hostWindow = (NSWindow*) nsWindow;
NSView* pluginView = (NSView*) comp->getWindowHandle();
NSWindow* pluginWindow = [pluginView window];
NSWindow* hostWindow = (NSWindow*) window;
NSView* pluginView = (NSView*) comp->getWindowHandle();
NSWindow* pluginWindow = [pluginView window];
[pluginView retain];
[hostWindow removeChildWindow: pluginWindow];
[pluginWindow close];
comp->removeFromDesktop();
[pluginView release];
[pluginView retain];
[hostWindow removeChildWindow: pluginWindow];
[pluginWindow close];
comp->removeFromDesktop();
[pluginView release];
[hostWindow release];
[hostWindow release];
static bool needToRunMessageLoop = ! PluginHostType().isReaper();
static bool needToRunMessageLoop = ! PluginHostType().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);
// 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);
return;
}
#endif
(void) isNSView;
comp->removeFromDesktop();
}
}
void setNativeHostWindowSize (void* nsWindow, Component* component, int newWidth, int newHeight);
void setNativeHostWindowSize (void* nsWindow, Component* component, int newWidth, int newHeight)
void setNativeHostWindowSize (void* window, Component* component, int newWidth, int newHeight, bool isNSView);
void setNativeHostWindowSize (void* window, Component* component, int newWidth, int newHeight, bool isNSView)
{
(void) isNSView; (void) window; (void) isNSView;
JUCE_AUTORELEASEPOOL
{
#if JUCE_64BIT
if (NSView* hostView = (NSView*) nsWindow)
#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
if (NSView* hostView = (NSView*) window)
{
// xxx is this necessary, or do the hosts detect a change in the child view and do this automatically?
[hostView setFrameSize: NSMakeSize ([hostView frame].size.width + (newWidth - component->getWidth()),
[hostView frame].size.height + (newHeight - component->getHeight()))];
}
#else
(void) nsWindow;
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);
}
#endif
}
}
void checkWindowVisibility (void* nsWindow, Component* comp);
void checkWindowVisibility (void* nsWindow, Component* comp)
void checkWindowVisibility (void* window, Component* comp, bool isNSView);
void checkWindowVisibility (void* window, Component* comp, bool isNSView)
{
(void) window; (void) comp; (void) isNSView;
#if ! JUCE_64BIT
comp->setVisible ([((NSWindow*) nsWindow) isVisible]);
if (! isNSView)
comp->setVisible ([((NSWindow*) window) isVisible]);
#endif
}
bool forwardCurrentKeyEventToHost (Component* comp);
bool forwardCurrentKeyEventToHost (Component* comp)
bool forwardCurrentKeyEventToHost (Component* comp, bool isNSView);
bool forwardCurrentKeyEventToHost (Component* comp, bool isNSView)
{
#if JUCE_64BIT
(void) comp;
return false;
#else
NSWindow* win = [(NSView*) comp->getWindowHandle() window];
[[win parentWindow] makeKeyWindow];
repostCurrentNSEvent();
return true;
#if ! JUCE_64BIT
if (! isNSView)
{
NSWindow* win = [(NSView*) comp->getWindowHandle() window];
[[win parentWindow] makeKeyWindow];
repostCurrentNSEvent();
return true;
}
#endif
(void) comp; (void) isNSView;
return false;
}
} // (juce namespace)