2 summary:: Cocoa / Objective-C bridge
8 This is experimental (03/2006), things might change and be careful wrong or unsupported Cocoa-calls can crash this Application !
11 subsection::Object Creation - LifeCycle
13 On creation only the init message is passed, alloc is called internally. Instance methods and Class methods are supported by the bridge, but if an object (id or SCNSObject) is returned by the method you owns it (even for autoreleased object - because they are retained internally by SuperCollider), so you must call strong::release:: when you're done with it (SCNSObject(s) are not automatically garbage collected).
15 subsection::Invocation
17 Once your Objective-C object is allocated / retained you can call it using strong::invoke::.
20 The Objective-C synthax:
22 NSNumber *n = [[NSNumber alloc] initWithFloat: 1.1];
27 n = SCNSObject("NSNumber", "initWithFloat:", [1.1]);
28 n.invoke("floatValue");
30 Multiple messages are put together in one String and their arguments in one Array.
35 NSWindow *c = [[NSWindow alloc] initWithContentRect: rect styleMask: 10 backing: 2 defer:YES];
39 c = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:",[Rect(0,0,400,100), 10, 2, 1]);
42 subsection::Deferring your calls
44 Some methods need to be defered. If you want to defer ust call invoke with defer:true. Watch out there is no smart protection for methods that need defer until now! In general you should defer graphic operations.
45 So calling this might crash sc-lang:
47 c.invoke("makeKeyAndOrderFront:", [nil]);
49 but this line is fine:
51 c.invoke("makeKeyAndOrderFront:", [nil], true);
54 subsection::Common Conversion Table
56 SuperCollider will try to convert types when possible, here are the most common types and their translation betweem the two languages.
59 ## strong::SuperCollider Types ->:: || strong::Objective-C Types (when using invoke / SCNSObject.new)::
60 ## SCNSObject || id (NSObject)
61 ## Nil || nil, NULL pointer
62 ## Number (Float, Integer) || float, int, long, short, char, NSNumber
63 ## Boolean || YES, NO, bool, NSNumber
64 ## String || NSString, SEL, char*, void*
67 ## Point || NSPoint, NSRange, NSSize
68 ## Int8Array || void*, char*
69 ## Int16Array || void*, short*
70 ## Int32Array || void*, int*
71 ## DoubleArray || void*, double*
72 ## FloatArray || void*, float*
74 ## Array || QTTime, NSRange, NSSize, SCNSObject*
78 ## strong::Objective-C Types ->:: || strong::SuperCollider Types (on method return)::
79 ## NSString, char* || String
81 ## NSSize, NSRange, QTTime || Array
84 ## BOOL, long, char, int, short || Integer
85 ## float, double || Float
86 ## c99 _bool || Boolean
87 ## *(pointer type) || RawPointer
88 ## id, (any other NSObject) || SCNSObject
94 Creates a new SCNSObject instance. SCNSObject creates a bridge between SuperCollider and Objective-C / Cocoa. It holds an NSObject and sends messages to it. The class and messages are passed as Strings. Arguments must be in an Array.
97 a = SCNSObject("NSHost", "currentHost");
98 [\name, a.invoke("name"), \address, a.invoke("address")].postln;
103 The Objective-C name of the class you want to invoke / instantiate.
106 can be either a class method or an instance initX method, depending on the possible initialization call. You do not need to specify alloc if you instantiate an object, it is automatically done for you.
109 the Array of arguments for the initname method.
112 defer the call. Default is false.
114 method::newFromRawPointer
115 Creates a new SCNSObject from a link::Classes/RawPointer::. Might be handy for very special occasion.
118 i = SCImage.new("/Library/Desktop Pictures/Ripples Blue.jpg");
119 a = SCNSObject.newFromRawPointer(i.slotAt(0)).invoke("nsimage");
120 a.className.postln; // verify :)
121 // now do what you want with the NSImage of the SCImage
122 i.free; // release it when done
123 // you do not have here to release the SCNSObject - it is dangerous to do so in this case
127 Dump the current NSObjects in the pool, so retained by SuperCollider.
130 Release all the current NSObjects in the pool and clear it. Call this method only if you really know what you are doing : all the SCNSObjects will be unvalidated !
132 InstanceMethods::invoke
133 Invoke an SCNSObject.
136 The method to call the receiver with.
139 The arguments link::Classes/Array::.
142 defer the call. Default is false. (might be needed for GUI otherwise you may experience a crash).
145 Release the internal NSObject retained by the application pool. You must call this method when you're done with your object.
148 It is not fully equivalent to a [myObject release], the NSObject is removed from the pool so if the application is the only one who retained it and owns it, it will properly dealloc it. But if the object is not IN the pool, it won't do anything.
152 SCNSObject.freePool; // free all object first to see
153 i = SCImage.new("/Library/Desktop Pictures/Ripples Blue.jpg"); // create a simple SCImage
154 a = SCNSObject.newFromRawPointer(i.slotAt(0)); // get the SCImage object
155 a.className; // SCImage
157 SCNSObject.dumpPool; // look, we do not have any NSObject in our pool
158 // a.release; // so this won't do anything - just clear our NSObject ref - note that the SCImage is not IN the pool
159 a.invoke("retain"); // now we can retain the SCImage - but since the method returns the object it is also added - retained also in the pool !
160 i.free; // release the SCImage
161 SCNSObject.dumpPool; // look the SCImage is here
162 a.invoke("release"); // now we have to release it twice
163 SCNSObject.dumpPool; //
164 a.release; // should be fine
168 Creates a link::Classes/CocoaAction::, a special delegate to handle Target / Action mechanism (See explainations above). strong::initAction:: is a convenience method to add an action to a gui element, mostly for strong::NSControl:: subclasses. Once an action is setted, a special delegate is created on your behalf to wich you can attach a action. You can access this delegate using the strong::nsAction:: accessor method.
172 var win, topview, slider;
174 win = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:", [Rect(100,140,400,30), 10, 2, 1]);
175 win.registerNotification("NSWindowWillCloseNotification", {|name, nsnotification, object|
176 [win, topview, slider].do { |obj| obj.release };
177 [name, nsnotification, object].postln}
180 slider = SCNSObject("NSSlider", "initWithFrame:", [Rect(0,0,390,20)]);
181 slider.invoke("setFloatValue:", [0.5]);
182 slider.initAction.action_({|v,val| val.postln});
184 topview = win.invoke("contentView");
185 topview.invoke("addSubview:", [slider]);
187 win.invoke("makeKeyAndOrderFront:", [win], true);
188 win.invoke("setTitle:", ["cocoa test"]);
193 may be "doFloatAction:" (default), "doIntAction:", "doStateAction:" or "doAction:".
196 Creates a special link::Classes/CocoaAction:: delegate object to handle delegate methods and notifications. Should not be confused with the nsAction one. This delegate can be retrieved with the strong::nsDelegate:: accessor.
198 method::registerNotification
199 Register a special notification (see NSNotification) with a link::Classes/Function:: that will be triggered each time it is sent. This method will create a defaut strong::nsDelegate:: if it does exist already.
204 win = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:", [Rect(100,400,400,400), 10, 2, 1]);
205 win.registerNotification("NSWindowWillCloseNotification", {|name, nsnotification, object, delegate|
206 [delegate /* the window.nsDelegate */, delegate.object /* the win SCNSObject */, name, nsnotification, object].postln;
209 win.invoke("setMinSize:", [[100,100]]);
210 win.invoke("makeKeyAndOrderFront:", [win], true);
211 win.invoke("setTitle:", ["notification test - Close Me"]);
215 argument::aNotificationName
216 The name of the notification.
219 The responder function.
222 The object of the notification, default is this.
225 SCNSObject holding an NSData object can be converted to array types using the strong::asArray:: method.
228 d = SCNSObject.new("NSData", "dataWithBytes:length:", ["hellomydear", 11]); // 11 bytes passed
229 e = d.asArray(\string); // get it back as a String
232 d = SCNSObject.new("NSData", "dataWithBytes:length:", [Int32Array[98,99,100,101], 4*4]); // 4x32bit integers = 16 bytes
233 e = d.asArray(\int32); // get it back as an Int32Array
236 d = SCNSObject.new("NSData", "dataWithBytes:length:", [Int16Array[98,99,100,101], 4*2]); // 4x16bit integers = 8 bytes
237 e = d.asArray(\int16); // get it back an Int16Array
242 \string \int8 \int16 \int32 \float \double are the possible argument for an explicit conversion.
247 //create a window and add a Slider that posts its value.
251 win = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:",
252 [Rect(100,140,400,30), 10, 2, 1]);
253 win.setDelegate.action_({ // for NSWindow objects using setDelegate.action will trigger the nsAction.action function when it is closed
254 "closing window, releasing objects".postln;
255 [slider,e].do{|it| it.release};
257 slider = SCNSObject("NSSlider", "initWithFrame:", [Rect(0,0,390,20)]);
258 e = SCNSObject("SCGraphView", "initWithFrame:", [Rect(0,0,400,30)]);
259 win.invoke("setContentView:", [e], true);
260 e.invoke("addSubview:", [slider], true);
261 slider.invoke("setFloatValue:", [0.5]);
262 win.invoke("makeKeyAndOrderFront:", [nil], true);
263 win.invoke("setTitle:", ["cocoa test"]);
266 a = slider.initAction;
267 a.action_({|v,val| val.postln});}.defer(0.1);
272 ~win.invoke("close", defer:true);
276 c = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:",[Rect(0,0,400,100), 10, 2, 1]);
277 c.setDelegate.action_({ // for NSWindow objects using setDelegate.action will trigger the nsAction.action function when it is closed
278 "closing window, releasing objects".postln;
279 [c,d,e].do{|it| it.release};
281 d = SCNSObject("NSTextField", "initWithFrame:", [Rect(0,0,100,20)]);
282 e = SCNSObject("NSView", "initWithFrame:", [Rect(0,0,400,100)]);
283 c.invoke("setContentView:", [e], true);
284 e.invoke("addSubview:", [d], true);
285 c.invoke("makeKeyAndOrderFront:", [nil], true);
291 c = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:",[Rect(100,100,100,20), 10, 2, 1]);
292 c.setDelegate.action_({ // for NSWindow objects using setDelegate.action will trigger the nsAction.action function when it is closed
293 "closing window, releasing objects".postln;
294 [c,d,e].do{|it| it.release};
296 d = SCNSObject("NSButton", "initWithFrame:", [Rect(0,0,100,20)]);
297 e = SCNSObject("NSView", "initWithFrame:", [Rect(0,0,400,100)]);
298 c.invoke("setContentView:", [e], true);
299 e.invoke("addSubview:", [d], true);
300 c.invoke("makeKeyAndOrderFront:", [nil], true);
301 d.invoke("setButtonType:", [3]);
303 d.initAction("doStateAction:");
304 d.nsAction.action_({|it,val| val.postln;});
311 simple QTMovie example
312 creates a movie in the SuperCollider folder + adds an image to it
316 d = SCNSObject("NSMutableDictionary", "dictionary");
317 d.invoke("setObject:forKey:", ["jpeg", "QTAddImageCodecType"]);
319 e = SCNSObject("NSMutableDictionary", "dictionary");
320 e.invoke("setObject:forKey:", [true, "QTMovieFlatten"]);
322 m = SCNSObject("QTMovie", "initToWritableFile:error:", [Platform.classLibraryDir ++ "/../test.mov", nil]); // creates an empty movie
323 i = SCImage("/Library/Desktop Pictures/Ripples Blue.jpg");
325 // newFromRawPointer does not need any release so fine to get the invocation result directly
326 a = SCNSObject.newFromRawPointer(i.slotAt(0)).invoke("nsimage"); // this is how you can create a NSImage from
327 m.invoke("addImage:forDuration:withAttributes:", [a, [3, 1], d]); // 3 seconds
328 m.invoke("updateMovieFile");
330 [m, d, e].do ({ |object| object.release; });
334 // HUD Panels - 10.5 only
336 w = SCNSObject("NSPanel", "initWithContentRect:styleMask:backing:defer:", [Rect(250, 250, 300, 200), (1<<13) + (1<<4) + 4 + 2 + 8, 2, true]);
337 w.registerNotification("NSWindowWillCloseNotification", {|notificationName,nsNotification|
340 w.invoke("makeKeyAndOrderFront:", [nil], true);
345 /*----------------------
346 Notification Examples
348 ________________________*/
351 var win, root, cocoaUI, cell, webview, levelIndicator;
352 win = SCNSObject("NSWindow", "initWithContentRect:styleMask:backing:defer:", [Rect(250, 250, 800, 600), 15, 2, 1]);
354 root = SCNSObject("NSView", "initWithFrame:", [Rect(0, 0, 800, 600)]);
355 root.invoke("setAutoresizingMask:", [1 + 2 + 8 + 16]);
357 webview = SCNSObject("WebView", "initWithFrame:frameName:groupName:", [Rect(10, 30, 800-20, 600-40), "mywebview", "mywebviewgroup"]);
358 webview.invoke("setAutoresizingMask:", [1 + 2 + 8 + 16]);
360 ~webview = webview; // just to retrieve the source after
362 cell = SCNSObject("NSLevelIndicatorCell", "initWithLevelIndicatorStyle:", [1]);
363 levelIndicator = SCNSObject("NSLevelIndicator", "initWithFrame:", [Rect(10, 5, 800-20, 10)]);
364 levelIndicator.invoke("setCell:", [cell]);
365 levelIndicator.invoke("setMinValue:", [0]);
366 levelIndicator.invoke("setMaxValue:", [100]);
367 levelIndicator.invoke("setFloatValue:", [0]);
368 levelIndicator.invoke("setContinuous:", [true]);
372 cocoaUI.add(webview);
373 cocoaUI.add(levelIndicator);
375 win.invoke("setContentView:", [root]);
376 root.invoke("addSubview:", [webview]);
377 root.invoke("addSubview:", [levelIndicator]);
381 win.registerNotification("NSWindowWillCloseNotification", {
382 |notificationName, nsNotificationObjectAsRawPointer|
383 "closing window".postln;
384 cocoaUI.do {|ui| ui.invoke("removeFromSuperviewWithoutNeedingDisplay")};
388 levelIndicator.release;
392 win.registerNotification("NSWindowDidMoveNotification", {
393 |notificationName, nsNotificationObjectAsRawPointer|
394 notificationName.postln;
397 win.registerNotification("NSWindowDidMiniaturizeNotification", {
398 |notificationName, nsNotificationObjectAsRawPointer|
399 notificationName.postln;
401 // Webview Notifications
402 webview.registerNotification("WebProgressEstimateChangedNotification", {
403 |notificationName, nsNotificationObjectAsRawPointer|
405 value = webview.invoke("estimatedProgress");
406 levelIndicator.invoke("setFloatValue:", [value*100]);
407 ("loading progress: "+ (value*100) + "%").postln;
410 webview.registerNotification("WebProgressFinishedNotification", {
411 |notificationName, nsNotificationObjectAsRawPointer|
413 levelIndicator.invoke("setFloatValue:", [0]);
414 t0 = webview.invoke("mainFrame");
415 t1 = t0.invoke("dataSource"); t0.release;
416 t0 = t1.invoke("initialRequest"); t1.release;
417 t1 = t0.invoke("URL"); t0.release;
418 t0 = t1.invoke("absoluteString"); t1.release;
419 (t0 ++ " finished Loading").postln;
420 win.invoke("setTitle:", [t0]);
423 win.invoke("makeKeyAndOrderFront:", [win], true);
428 url = "http://swiki.hfbk-hamburg.de:8888/MusicTechnology/6";
429 webview.invoke("setMainFrameURL:", [url]);
436 /*----------------------
438 using Webview html source
439 Do not close the window before you exec this code or reload previous example !
440 ________________________*/
442 /// interpret it AFTER previous example for getting source html file
443 var mainframe, datasource, nsdata;
444 mainframe = ~webview.invoke("mainFrame");
445 datasource = mainframe.invoke("dataSource"); mainframe.release;
446 nsdata = datasource.invoke("data"); datasource.release;
447 nsdata.isSubclassOf("NSData").postln; //
448 "---- HTML Source ----".postln;
449 nsdata.asArray(\string).postln;
450 "---- End of HTML Source ----".postln;
456 /*----------------------
457 special Delegates actions with return values
458 using NSURLConnection as an example
459 ________________________*/
466 url = SCNSObject("NSURL", "initWithString:", ["http://www.audiosynth.com"]);
467 ~urlRequest = SCNSObject("NSURLRequest", "requestWithURL:cachePolicy:timeoutInterval:", [url, 0, 60]); url.release;
469 // redirection to set after delegate call
470 url = SCNSObject("NSURL", "initWithString:", ["http://www.apple.com"]);
471 ~redirection = SCNSObject("NSURLRequest", "requestWithURL:cachePolicy:timeoutInterval:", [url, 0, 60]); url.release;
473 // we need here to set a void object to set its delegate before it is allocated really
474 // because urlConnection does not have a setDelegate: method
475 ~urlConnection = SCNSObject.newClear;
476 ~urlConnection.setDelegate; // create and attach a special delegate
477 ~urlConnection.nsDelegate.addMethod("connectionDidFinishLoading:", nil, "@", {
478 |method, args| [method, args].postln;
481 //// Custom Delegate Method with return values allowed (automatic conversion for most)
482 //// Here we have to provide the (name, return type of the delegate method, and the type encoding for the arguments)
483 //// see http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_13_section_9.html#//apple_ref/doc/uid/TP30001163-CH9-TPXREF165 for explanations
484 ~urlConnection.nsDelegate.addMethod("connection:didReceiveResponse:", nil, "@@", {
485 |method, args| [method, args].postln;
488 ~urlConnection.nsDelegate.addMethod("connection:willSendRequest:redirectResponse:", "@", "@@@", {
490 [method, arguments].postln;
491 url = ~redirection.invoke("URL");
492 ("redirecting to "++url.invoke("absoluteString")).postln; url.release;
493 ^~redirection; // redirect !
496 // we can init the object now
497 ~urlConnection.init("NSURLConnection", "initWithRequest:delegate:", [~urlRequest, ~urlConnection.nsDelegate]); // now we can alloc the object and attach its delegate
501 ~urlConnection.release;
503 ~redirection.release;