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