1 /* 2 Cockos WDL License 3 4 Copyright (C) 2005 - 2015 Cockos Incorporated 5 Copyright (C) 2016 Guillaume Piolat 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 version(AU): 23 24 import std.string; 25 import std.uuid; 26 27 import derelict.carbon; 28 import derelict.cocoa; 29 30 import dplug.core.runtime; 31 import dplug.core.random; 32 import dplug.core.nogc; 33 import dplug.au.client; 34 35 36 import core.stdc.stdio; 37 38 // TODO: this file is ugly, ownership is all over the place 39 // and leaking 40 41 // Register a view factory object, return the class name. 42 const(char)[] registerCocoaViewFactory() nothrow @nogc 43 { 44 acquireCocoaFunctions(); 45 NSApplicationLoad(); // to use Cocoa in Carbon applications 46 DPlugCocoaViewFactory.registerSubclass(); 47 return DPlugCocoaViewFactory.customClassName[0..(22 + 36)]; 48 } 49 50 // TODO: this is never called! This was required a long time ago 51 // but conditions have changed now and we can probably be clean. 52 void unregisterCocoaViewFactory() nothrow @nogc 53 { 54 DPlugCocoaViewFactory.unregisterSubclass(); 55 releaseCocoaFunctions(); 56 } 57 58 59 struct DPlugCocoaViewFactory 60 { 61 nothrow: 62 @nogc: 63 64 // The custom class we use. 65 static __gshared Class clazz = null; 66 67 // This class uses a unique class name for each plugin instance 68 static __gshared char[22 + 36 + 1] customClassName; 69 70 71 NSView parent; 72 alias parent this; 73 74 // create from an id 75 this(id id_) 76 { 77 this._id = id_; 78 } 79 80 ~this() 81 { 82 } 83 84 static void registerSubclass() 85 { 86 if (clazz !is null) // custom class already registered (TODO: use acquire/release semantics instead) 87 return; 88 89 generateNullTerminatedRandomUUID!char(customClassName, "DPlugCocoaViewFactory_"); 90 91 clazz = objc_allocateClassPair(cast(Class) lazyClass!"NSObject", customClassName.ptr, 0); 92 93 class_addMethod(clazz, sel!"description:", cast(IMP) &description, "@@:"); 94 class_addMethod(clazz, sel!"interfaceVersion", cast(IMP) &interfaceVersion, "I@:"); 95 class_addMethod(clazz, sel!"uiViewForAudioUnit:withSize:", cast(IMP) &uiViewForAudioUnit, "@@:^{ComponentInstanceRecord=[1q]}{CGSize=dd}"); 96 97 // Very important: add an instance variable for the this pointer so that the D object can be 98 // retrieved from an id 99 class_addIvar(clazz, "this", (void*).sizeof, (void*).sizeof == 4 ? 2 : 3, "^v"); 100 101 // Replicates the AUCocoaUIBase protocol. 102 // For host to accept that our object follow AUCocoaUIBase, we replicate AUCocoaUIBase 103 // with the same name and methods. 104 // This protocol has to be created at runtime because we don't have @protocol in D. 105 // http://stackoverflow.com/questions/2615725/how-to-create-a-protocol-at-runtime-in-objective-c 106 { 107 108 Protocol *protocol = objc_getProtocol("AUCocoaUIBase".ptr); 109 110 if (protocol == null) 111 { 112 // create it at runtime 113 protocol = objc_allocateProtocol("AUCocoaUIBase".ptr); 114 protocol_addMethodDescription(protocol, sel!"interfaceVersion", "I@:", YES, YES); 115 protocol_addMethodDescription(protocol, sel!"uiViewForAudioUnit:withSize:", "@@:^{ComponentInstanceRecord=[1q]}{CGSize=dd}", YES, YES); 116 objc_registerProtocol(protocol); 117 } 118 119 class_addProtocol(clazz, protocol); 120 } 121 objc_registerClassPair(clazz); 122 } 123 124 static void unregisterSubclass() nothrow 125 { 126 // TODO: remove leaking of class and protocol 127 } 128 } 129 130 DPlugCocoaViewFactory getInstance(id anId) nothrow @nogc 131 { 132 // strange thing: object_getInstanceVariable definition is odd (void**) 133 // and only works for pointer-sized values says SO 134 void* thisPointer = null; 135 Ivar var = object_getInstanceVariable(anId, "this", &thisPointer); 136 assert(var !is null); 137 assert(thisPointer !is null); 138 return *cast(DPlugCocoaViewFactory*)thisPointer; 139 } 140 141 // Overridden function gets called with an id, instead of the self pointer. 142 // So we have to get back the D class object address. 143 // Big thanks to Mike Ash (@macdev) 144 extern(C) nothrow @nogc 145 { 146 id description(id self, SEL selector) 147 { 148 ScopedForeignCallback!(true, false) scopedCallback; 149 scopedCallback.enter(); 150 151 return NSString.stringWith("Filter View"w)._id; 152 } 153 154 uint interfaceVersion(id self, SEL selector) 155 { 156 return 0; 157 } 158 159 // Create the Cocoa view and return it 160 id uiViewForAudioUnit(id self, SEL selector, AudioUnit audioUnit, NSSize preferredSize) 161 { 162 ScopedForeignCallback!(true, true) scopedCallback; 163 scopedCallback.enter(); 164 165 AUClient plugin; 166 167 void* res; 168 uint pointerSize = cast(uint)(size_t.sizeof); 169 if (AudioUnitGetProperty (audioUnit, AUClient.kAudioUnitProperty_DPLUG_AUCLIENT_INSTANCE, kAudioUnitScope_Global, 0, &res, &pointerSize) == noErr) 170 { 171 plugin = cast(AUClient)res; 172 } 173 174 if (plugin) 175 { 176 return cast(id)( plugin.openGUIAndReturnCocoaView() ); 177 } 178 else 179 { 180 return null; 181 } 182 } 183 }