1 /* 2 Cockos WDL License 3 4 Copyright (C) 2005 - 2015 Cockos Incorporated 5 Copyright (C) 2016 Auburn Sounds 6 7 Portions copyright other contributors, see each source file for more information 8 9 This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. 10 11 Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 14 1. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 15 1. This notice may not be removed or altered from any source distribution. 16 */ 17 /** 18 Audio Unit plug-in client. Internal. Implementation of the Cocoa View Factory. 19 */ 20 module dplug.au.cocoaviewfactory; 21 22 import std..string; 23 import std.uuid; 24 25 import derelict.carbon; 26 import derelict.cocoa; 27 28 import dplug.core.runtime; 29 import dplug.core.random; 30 import dplug.core.nogc; 31 import dplug.au.client; 32 33 34 import core.stdc.stdio; 35 36 // TODO: this file is ugly, ownership is all over the place 37 // and leaking 38 39 // Register a view factory object, return the class name. 40 const(char)[] registerCocoaViewFactory() nothrow @nogc 41 { 42 acquireCocoaFunctions(); 43 NSApplicationLoad(); // to use Cocoa in Carbon applications 44 DPlugCocoaViewFactory.registerSubclass(); 45 return DPlugCocoaViewFactory.customClassName[0..(22 + 36)]; 46 } 47 48 // TODO: this is never called! This was required a long time ago 49 // but conditions have changed now and we can probably be clean. 50 void unregisterCocoaViewFactory() nothrow @nogc 51 { 52 DPlugCocoaViewFactory.unregisterSubclass(); 53 releaseCocoaFunctions(); 54 } 55 56 57 struct DPlugCocoaViewFactory 58 { 59 nothrow: 60 @nogc: 61 62 // The custom class we use. 63 static __gshared Class clazz = null; 64 65 // This class uses a unique class name for each plugin instance 66 static __gshared char[22 + 36 + 1] customClassName; 67 68 69 NSView parent; 70 alias parent this; 71 72 // create from an id 73 this(id id_) 74 { 75 this._id = id_; 76 } 77 78 ~this() 79 { 80 } 81 82 static void registerSubclass() 83 { 84 if (clazz !is null) // custom class already registered (TODO: use acquire/release semantics instead) 85 return; 86 87 generateNullTerminatedRandomUUID!char(customClassName, "DPlugCocoaViewFactory_"); 88 89 clazz = objc_allocateClassPair(cast(Class) lazyClass!"NSObject", customClassName.ptr, 0); 90 91 class_addMethod(clazz, sel!"description:", cast(IMP) &description, "@@:"); 92 class_addMethod(clazz, sel!"interfaceVersion", cast(IMP) &interfaceVersion, "I@:"); 93 class_addMethod(clazz, sel!"uiViewForAudioUnit:withSize:", cast(IMP) &uiViewForAudioUnit, "@@:^{ComponentInstanceRecord=[1q]}{CGSize=dd}"); 94 95 // Very important: add an instance variable for the this pointer so that the D object can be 96 // retrieved from an id 97 class_addIvar(clazz, "this", (void*).sizeof, (void*).sizeof == 4 ? 2 : 3, "^v"); 98 99 // Replicates the AUCocoaUIBase protocol. 100 // For host to accept that our object follow AUCocoaUIBase, we replicate AUCocoaUIBase 101 // with the same name and methods. 102 // This protocol has to be created at runtime because we don't have @protocol in D. 103 // http://stackoverflow.com/questions/2615725/how-to-create-a-protocol-at-runtime-in-objective-c 104 { 105 106 Protocol *protocol = objc_getProtocol("AUCocoaUIBase".ptr); 107 108 if (protocol == null) 109 { 110 // create it at runtime 111 protocol = objc_allocateProtocol("AUCocoaUIBase".ptr); 112 protocol_addMethodDescription(protocol, sel!"interfaceVersion", "I@:", YES, YES); 113 protocol_addMethodDescription(protocol, sel!"uiViewForAudioUnit:withSize:", "@@:^{ComponentInstanceRecord=[1q]}{CGSize=dd}", YES, YES); 114 objc_registerProtocol(protocol); 115 } 116 117 class_addProtocol(clazz, protocol); 118 } 119 objc_registerClassPair(clazz); 120 } 121 122 static void unregisterSubclass() nothrow 123 { 124 // TODO: remove leaking of class and protocol 125 } 126 } 127 128 DPlugCocoaViewFactory getInstance(id anId) nothrow @nogc 129 { 130 // strange thing: object_getInstanceVariable definition is odd (void**) 131 // and only works for pointer-sized values says SO 132 void* thisPointer = null; 133 Ivar var = object_getInstanceVariable(anId, "this", &thisPointer); 134 assert(var !is null); 135 assert(thisPointer !is null); 136 return *cast(DPlugCocoaViewFactory*)thisPointer; 137 } 138 139 // Overridden function gets called with an id, instead of the self pointer. 140 // So we have to get back the D class object address. 141 // Big thanks to Mike Ash (@macdev) 142 extern(C) nothrow @nogc 143 { 144 id description(id self, SEL selector) 145 { 146 ScopedForeignCallback!(true, false) scopedCallback; 147 scopedCallback.enter(); 148 149 return NSString.stringWith("Filter View"w)._id; 150 } 151 152 uint interfaceVersion(id self, SEL selector) 153 { 154 return 0; 155 } 156 157 // Create the Cocoa view and return it 158 id uiViewForAudioUnit(id self, SEL selector, AudioUnit audioUnit, NSSize preferredSize) 159 { 160 ScopedForeignCallback!(true, true) scopedCallback; 161 scopedCallback.enter(); 162 163 AUClient plugin = cast(AUClient)( cast(void*)GetComponentInstanceStorage(audioUnit) ); 164 if (plugin) 165 { 166 return cast(id)( plugin.openGUIAndReturnCocoaView() ); 167 } 168 else 169 return null; 170 } 171 }