diff options
author | David Robillard <d@drobilla.net> | 2008-01-10 21:20:59 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2008-01-10 21:20:59 +0000 |
commit | bb457bb960c5bd7ed538f9d31477293415739f68 (patch) | |
tree | 84324a63b87c03589cd165b9e474296eaebb4772 /gtk2_ardour/au_pluginui.mm | |
parent | 73dd9d37e7d715e0d78c0e51569968f9494dac7f (diff) |
Merge libs/ardour and gtk2_ardour with 2.0-ongoing R2837.
git-svn-id: svn://localhost/ardour2/trunk@2883 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'gtk2_ardour/au_pluginui.mm')
-rw-r--r-- | gtk2_ardour/au_pluginui.mm | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/gtk2_ardour/au_pluginui.mm b/gtk2_ardour/au_pluginui.mm new file mode 100644 index 0000000000..51f73199f7 --- /dev/null +++ b/gtk2_ardour/au_pluginui.mm @@ -0,0 +1,567 @@ +#include <pbd/error.h> +#include <ardour/audio_unit.h> +#include <ardour/insert.h> + +#include <gdk/gdkquartz.h> + +#include "au_pluginui.h" +#include "gui_thread.h" + +#include <appleutility/CAAudioUnit.h> +#include <appleutility/CAComponent.h> + +#import <AudioUnit/AUCocoaUIView.h> +#import <CoreAudioKit/AUGenericView.h> + +#include "i18n.h" + +using namespace ARDOUR; +using namespace Gtk; +using namespace sigc; +using namespace std; +using namespace PBD; + +static const float kOffsetForAUView_X = 220; +static const float kOffsetForAUView_Y = 90; + +AUPluginUI::AUPluginUI (boost::shared_ptr<PluginInsert> insert) + : PlugUIBase (insert) +{ + if ((au = boost::dynamic_pointer_cast<AUPlugin> (insert->plugin())) == 0) { + error << _("unknown type of editor-supplying plugin (note: no AudioUnit support in this version of ardour)") << endmsg; + throw failed_constructor (); + } + + bool has_carbon; + bool has_cocoa; + + carbon_parented = false; + cocoa_parented = false; + cocoa_parent = 0; + cocoa_window = 0; + + test_view_support (has_carbon, has_cocoa); + + if (has_cocoa) { + create_cocoa_view (); + } else if (has_carbon) { + create_carbon_view (has_carbon); + } else { + /* fallback to cocoa */ + create_cocoa_view (); + } +} + + +AUPluginUI::~AUPluginUI () +{ + if (carbon_parented) { + NSWindow* win = get_nswindow(); + RemoveEventHandler(carbon_event_handler); + [win removeChildWindow:cocoa_parent]; + } +} + +void +AUPluginUI::test_view_support (bool& has_carbon, bool& has_cocoa) +{ + has_carbon = test_carbon_view_support(); + has_cocoa = test_cocoa_view_support(); +} + +bool +AUPluginUI::test_carbon_view_support () +{ + bool ret = false; + + carbon_descriptor.componentType = kAudioUnitCarbonViewComponentType; + carbon_descriptor.componentSubType = 'gnrc'; + carbon_descriptor.componentManufacturer = 'appl'; + carbon_descriptor.componentFlags = 0; + carbon_descriptor.componentFlagsMask = 0; + + OSStatus err; + + // ask the AU for its first editor component + UInt32 propertySize; + err = AudioUnitGetPropertyInfo(*au->get_au(), kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &propertySize, NULL); + if (!err) { + int nEditors = propertySize / sizeof(ComponentDescription); + ComponentDescription *editors = new ComponentDescription[nEditors]; + err = AudioUnitGetProperty(*au->get_au(), kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, editors, &propertySize); + if (!err) { + // just pick the first one for now + carbon_descriptor = editors[0]; + ret = true; + } + delete[] editors; + } + + return ret; +} + +bool +AUPluginUI::test_cocoa_view_support () +{ + UInt32 dataSize = 0; + Boolean isWritable = 0; + OSStatus err = AudioUnitGetPropertyInfo(*au->get_au(), + kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, &dataSize, &isWritable); + + return dataSize > 0 && err == noErr; +} + +bool +AUPluginUI::plugin_class_valid (Class pluginClass) +{ + if([pluginClass conformsToProtocol: @protocol(AUCocoaUIBase)]) { + if([pluginClass instancesRespondToSelector: @selector(interfaceVersion)] && + [pluginClass instancesRespondToSelector: @selector(uiViewForAudioUnit:withSize:)]) { + return true; + } + } + return false; +} + +int +AUPluginUI::create_cocoa_view () +{ + BOOL wasAbleToLoadCustomView = NO; + AudioUnitCocoaViewInfo* cocoaViewInfo = NULL; + UInt32 numberOfClasses = 0; + UInt32 dataSize; + Boolean isWritable; + NSString* factoryClassName = 0; + NSURL* CocoaViewBundlePath; + + OSStatus result = AudioUnitGetPropertyInfo (*au->get_au(), + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable ); + + numberOfClasses = (dataSize - sizeof(CFURLRef)) / sizeof(CFStringRef); + + // Does view have custom Cocoa UI? + + if ((result == noErr) && (numberOfClasses > 0) ) { + cocoaViewInfo = (AudioUnitCocoaViewInfo *)malloc(dataSize); + if(AudioUnitGetProperty(*au->get_au(), + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + cocoaViewInfo, + &dataSize) == noErr) { + + CocoaViewBundlePath = (NSURL *)cocoaViewInfo->mCocoaAUViewBundleLocation; + + // we only take the first view in this example. + factoryClassName = (NSString *)cocoaViewInfo->mCocoaAUViewClass[0]; + + } else { + + if (cocoaViewInfo != NULL) { + free (cocoaViewInfo); + cocoaViewInfo = NULL; + } + } + } + + NSRect crect = { { 0, 0 }, { 1, 1} }; + + // [A] Show custom UI if view has it + + if (CocoaViewBundlePath && factoryClassName) { + NSBundle *viewBundle = [NSBundle bundleWithPath:[CocoaViewBundlePath path]]; + if (viewBundle == nil) { + error << _("AUPluginUI: error loading AU view's bundle") << endmsg; + return -1; + } else { + Class factoryClass = [viewBundle classNamed:factoryClassName]; + if (!factoryClass) { + error << _("AUPluginUI: error getting AU view's factory class from bundle") << endmsg; + return -1; + } + + // make sure 'factoryClass' implements the AUCocoaUIBase protocol + if (!plugin_class_valid (factoryClass)) { + error << _("AUPluginUI: U view's factory class does not properly implement the AUCocoaUIBase protocol") << endmsg; + return -1; + } + // make a factory + id factoryInstance = [[[factoryClass alloc] init] autorelease]; + if (factoryInstance == nil) { + error << _("AUPluginUI: Could not create an instance of the AU view factory") << endmsg; + return -1; + } + + // make a view + au_view = [factoryInstance uiViewForAudioUnit:*au->get_au() withSize:crect.size]; + + // cleanup + [CocoaViewBundlePath release]; + if (cocoaViewInfo) { + UInt32 i; + for (i = 0; i < numberOfClasses; i++) + CFRelease(cocoaViewInfo->mCocoaAUViewClass[i]); + + free (cocoaViewInfo); + } + wasAbleToLoadCustomView = YES; + } + } + + if (!wasAbleToLoadCustomView) { + // [B] Otherwise show generic Cocoa view + au_view = [[AUGenericView alloc] initWithAudioUnit:*au->get_au()]; + [(AUGenericView *)au_view setShowsExpertParameters:YES]; + } + + /* make a child cocoa window */ + + cocoa_window = [[NSWindow alloc] + initWithContentRect:crect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + + return 0; +} + +int +AUPluginUI::create_carbon_view (bool generic) +{ + OSStatus err; + ControlRef root_control; + + Component editComponent = FindNextComponent(NULL, &carbon_descriptor); + + OpenAComponent(editComponent, &editView); + if (!editView) { + error << _("AU Carbon view: cannot open AU Component") << endmsg; + return -1; + } + + Rect r = { 100, 100, 100, 100 }; + WindowAttributes attr = WindowAttributes (kWindowStandardHandlerAttribute | + kWindowCompositingAttribute| + kWindowNoShadowAttribute| + kWindowNoTitleBarAttribute); + + if ((err = CreateNewWindow(kDocumentWindowClass, attr, &r, &carbon_window)) != noErr) { + error << string_compose (_("AUPluginUI: cannot create carbon window (err: %1)"), err) << endmsg; + return -1; + } + + if ((err = GetRootControl(carbon_window, &root_control)) != noErr) { + error << string_compose (_("AUPlugin: cannot get root control of carbon window (err: %1)"), err) << endmsg; + return -1; + } + + ControlRef viewPane; + Float32Point location = { 0.0, 0.0 }; + Float32Point size = { 0.0, 0.0 } ; + + if ((err = AudioUnitCarbonViewCreate (editView, *au->get_au(), carbon_window, root_control, &location, &size, &viewPane)) != noErr) { + error << string_compose (_("AUPluginUI: cannot create carbon plugin view (err: %1)"), err) << endmsg; + return -1; + } + + // resize window + + Rect bounds; + GetControlBounds(viewPane, &bounds); + size.x = bounds.right-bounds.left; + size.y = bounds.bottom-bounds.top; + SizeWindow(carbon_window, (short) (size.x + 0.5), (short) (size.y + 0.5), true); + + prefwidth = (int) (size.x + 0.5); + prefheight = (int) (size.y + 0.5); + +#if 0 + mViewPaneResizer->WantEventTypes (GetControlEventTarget(mAUViewPane), GetEventTypeCount(resizeEvent), resizeEvent); +#endif + return 0; +} + +NSWindow* +AUPluginUI::get_nswindow () +{ + Gtk::Container* toplevel = get_toplevel(); + + if (!toplevel || !toplevel->is_toplevel()) { + error << _("AUPluginUI: no top level window!") << endmsg; + return 0; + } + + NSWindow* true_parent = gdk_quartz_window_get_nswindow (toplevel->get_window()->gobj()); + + if (!true_parent) { + error << _("AUPluginUI: no top level window!") << endmsg; + return 0; + } + + return true_parent; +} + +void +AUPluginUI::activate () +{ + NSWindow* win = get_nswindow (); + [win setLevel:NSFloatingWindowLevel]; + + if (carbon_parented) { + [cocoa_parent makeKeyAndOrderFront:nil]; + ActivateWindow (carbon_window, TRUE); + } +} + +void +AUPluginUI::deactivate () +{ + /* nothing to do here */ +} + + +OSStatus +_carbon_event (EventHandlerCallRef nextHandlerRef, EventRef event, void *userData) +{ + return ((AUPluginUI*)userData)->carbon_event (nextHandlerRef, event); +} + +OSStatus +AUPluginUI::carbon_event (EventHandlerCallRef nextHandlerRef, EventRef event) +{ + UInt32 eventKind = GetEventKind(event); + ClickActivationResult howToHandleClick; + NSWindow* win = get_nswindow (); + + switch (eventKind) { + case kEventWindowHandleActivate: + [win makeMainWindow]; + return eventNotHandledErr; + break; + + case kEventWindowHandleDeactivate: + return eventNotHandledErr; + break; + + case kEventWindowGetClickActivation: + howToHandleClick = kActivateAndHandleClick; + SetEventParameter(event, kEventParamClickActivation, typeClickActivationResult, + sizeof(ClickActivationResult), &howToHandleClick); + break; + } + + return noErr; +} + +int +AUPluginUI::parent_carbon_window () +{ + NSWindow* win = get_nswindow (); + int x, y; + + if (!win) { + return -1; + } + + Gtk::Container* toplevel = get_toplevel(); + + if (!toplevel || !toplevel->is_toplevel()) { + error << _("AUPluginUI: no top level window!") << endmsg; + return -1; + } + + toplevel->get_window()->get_root_origin (x, y); + + /* compute how tall the title bar is, because we have to offset the position of the carbon window + by that much. + */ + + NSRect content_frame = [NSWindow contentRectForFrameRect:[win frame] styleMask:[win styleMask]]; + NSRect wm_frame = [NSWindow frameRectForContentRect:content_frame styleMask:[win styleMask]]; + + int titlebar_height = wm_frame.size.height - content_frame.size.height; + + MoveWindow (carbon_window, x, y + titlebar_height, false); + ShowWindow (carbon_window); + + // create the cocoa window for the carbon one and make it visible + cocoa_parent = [[NSWindow alloc] initWithWindowRef: carbon_window]; + + EventTypeSpec windowEventTypes[] = { + {kEventClassWindow, kEventWindowGetClickActivation }, + {kEventClassWindow, kEventWindowHandleDeactivate }, + {kEventClassWindow, kEventWindowHandleActivate } + }; + + EventHandlerUPP ehUPP = NewEventHandlerUPP(_carbon_event); + OSStatus result = InstallWindowEventHandler (carbon_window, ehUPP, + sizeof(windowEventTypes) / sizeof(EventTypeSpec), + windowEventTypes, this, &carbon_event_handler); + if (result != noErr) { + return -1; + } + + [win addChildWindow:cocoa_parent ordered:NSWindowAbove]; + [win setLevel:NSFloatingWindowLevel]; + [win setHidesOnDeactivate:YES]; + + carbon_parented = true; + + return 0; +} + +int +AUPluginUI::parent_cocoa_window () +{ + NSWindow* win = get_nswindow (); + + if (!win) { + return -1; + } + + Gtk::Container* toplevel = get_toplevel(); + + if (!toplevel || !toplevel->is_toplevel()) { + error << _("AUPluginUI: no top level window!") << endmsg; + return -1; + } + + // Get the size of the new AU View's frame + NSRect au_view_frame = [au_view frame]; + + if (au_view_frame.size.width > 500 || au_view_frame.size.height > 500) { + + /* its too big - use a scrollview */ + + NSRect frameRect = [[cocoa_window contentView] frame]; + scroll_view = [[[NSScrollView alloc] initWithFrame:frameRect] autorelease]; + [scroll_view setDrawsBackground:NO]; + [scroll_view setHasHorizontalScroller:YES]; + [scroll_view setHasVerticalScroller:YES]; + + NSSize frameSize = [NSScrollView frameSizeForContentSize:au_view_frame.size + hasHorizontalScroller:[scroll_view hasHorizontalScroller] + hasVerticalScroller:[scroll_view hasVerticalScroller] + borderType:[scroll_view borderType]]; + + // Create a new frame with same origin as current + // frame but size equal to the size of the new view + NSRect newFrame; + newFrame.origin = [scroll_view frame].origin; + newFrame.size = frameSize; + + // Set the new frame and document views on the scroll view + NSRect currentFrame = [scroll_view frame]; + [scroll_view setFrame:newFrame]; + [scroll_view setDocumentView:au_view]; + + cerr << "scroll view size is " << newFrame.size.width << " x " << newFrame.size.height << endl; + + NSSize oldContentSize = [[cocoa_window contentView] frame].size; + NSSize newContentSize = oldContentSize; + + cerr << "original size is " << newContentSize.width << " x " << newContentSize.height << endl; + + newContentSize.width += (newFrame.size.width - currentFrame.size.width); + newContentSize.height += (newFrame.size.height - currentFrame.size.height); + + [cocoa_window setContentSize:newContentSize]; + [cocoa_window setContentView:scroll_view]; + + } else { + + [cocoa_window setContentSize:au_view_frame.size]; + [cocoa_window setContentView:au_view]; + + } + + /* compute how tall the title bar is, because we have to offset the position of the child window + by that much. + */ + + NSRect content_frame = [NSWindow contentRectForFrameRect:[win frame] styleMask:[win styleMask]]; + NSRect wm_frame = [NSWindow frameRectForContentRect:content_frame styleMask:[win styleMask]]; + int titlebar_height = wm_frame.size.height - content_frame.size.height; + + // move cocoa window into position relative to the toplevel window + + NSRect view_frame = [[cocoa_window contentView] frame]; + view_frame.origin.x = content_frame.origin.x; + view_frame.origin.y = content_frame.origin.y; + + [cocoa_window setFrame:view_frame display:NO]; + + /* make top level window big enough to hold cocoa window and titlebar */ + + content_frame.size.width = view_frame.size.width; + content_frame.size.height = view_frame.size.height + titlebar_height; + + [win setFrame:content_frame display:NO]; + + /* now make cocoa window a child of this top level */ + + [win addChildWindow:cocoa_window ordered:NSWindowAbove]; + // [win setLevel:NSFloatingWindowLevel]; + [win setHidesOnDeactivate:YES]; + + cocoa_parented = true; + + return 0; +} + +void +AUPluginUI::on_realize () +{ + VBox::on_realize (); + + if (cocoa_window) { + + if (parent_cocoa_window ()) { + } + + } else if (carbon_window) { + + if (parent_carbon_window ()) { + // ShowWindow (carbon_window); + } + } +} + +void +AUPluginUI::on_show () +{ + cerr << "AU plugin window shown\n"; + + VBox::on_show (); + + if (cocoa_window) { + [cocoa_window setIsVisible:YES]; + } else if (carbon_window) { + [cocoa_parent setIsVisible:YES]; + } +} + +bool +AUPluginUI::start_updating (GdkEventAny* any) +{ + return false; +} + +bool +AUPluginUI::stop_updating (GdkEventAny* any) +{ + return false; +} + +PlugUIBase* +create_au_gui (boost::shared_ptr<PluginInsert> plugin_insert, VBox** box) +{ + AUPluginUI* aup = new AUPluginUI (plugin_insert); + (*box) = aup; + return aup; +} |