5 // Created by Axel Balley on 26/10/08.
6 // Copyright 2008 __MyCompanyName__. All rights reserved.
8 #import <AudioToolbox/AudioToolbox.h>
9 #import <MediaPlayer/MediaPlayer.h>
12 #import "HTTPServer.h"
13 #import "MyHTTPConnection.h"
16 #import "iSCLangController.h"
18 #include "PyrObject.h"
19 #include "PyrKernel.h"
21 #include "VMGlobals.h"
23 #include "SC_DirUtils.h"
24 #include "SC_LanguageClient.h"
25 #include "SC_WorldOptions.h"
28 #define START_HTTP_SERVER
30 extern PyrSymbol* s_interpretCmdLine;
31 extern PyrSymbol* s_run;
32 extern PyrSymbol* s_tick;
35 PyrSymbol* s_interpretPrintCmdLine;
38 static iSCLangController* theController = 0;
40 void closeAllGUIScreens()
50 void initGUIPrimitives()
52 s_interpretCmdLine = getsym("interpretCmdLine");
53 s_interpretPrintCmdLine = getsym("interpretPrintCmdLine");
54 //s_run = getsym("run");
55 s_tick = getsym("tick");
59 void initSCViewPrimitives()
64 void initRendezvousPrimitives()
68 //////////////// queue
74 pthread_mutex_t mutex;
77 void Flush(UITextView *view);
80 static PostBuf mainPostBuf;
82 #define POSTBUFLEN 131072
83 #define POSTBUFMASK 131071
87 buf = (char *)malloc(POSTBUFLEN);
88 //HoldMemory(buf, POSTBUFLEN);
91 pthread_mutex_init(&mutex, NULL);
94 void PostBuf::Flush(UITextView *logView)
97 long localwritepos = wrpos;
99 if (localwritepos >= rdpos) {
100 numtoread = localwritepos - rdpos;
102 numtoread = POSTBUFLEN - (rdpos - localwritepos);
106 endpos = rdpos + numtoread;
107 if (endpos > POSTBUFLEN) {
108 // wrap around end in two copies
109 long firstpart, secondpart;
111 firstpart = POSTBUFLEN - rdpos;
112 endpos -= POSTBUFLEN;
115 NSString *s = [[logView text] stringByAppendingString:[NSString stringWithCString: buf + rdpos length: firstpart]];
116 NSString *s2 = [s stringByAppendingString:[NSString stringWithCString: buf length: secondpart]];
117 [logView setText:s2];
121 NSString *s = [[logView text] stringByAppendingString:[NSString stringWithCString: buf + rdpos length: numtoread]];
124 if (endpos == POSTBUFLEN) rdpos = 0;
127 int offset = [logView contentSize].height - [logView bounds].size.height;
128 if (offset>=0) [logView setContentOffset:CGPointMake(0,offset) animated:NO];
132 void initPostBuffer();
133 void initPostBuffer()
138 void vposttext(const char *str, int length);
139 void vposttext(const char *str, int length)
143 pthread_mutex_lock(&mainPostBuf.mutex);
145 for (int i=0; i<length && str[i]; ++i) {
146 if (((mainPostBuf.wrpos+1) & POSTBUFMASK) == mainPostBuf.rdpos) {
148 //mainPostBuf.Flush(); CANNOT DO THIS FROM OTHER THAN COCOA'S THREAD!
150 mainPostBuf.buf[mainPostBuf.wrpos] = str[i];
151 mainPostBuf.wrpos = (mainPostBuf.wrpos+1) & POSTBUFMASK;
153 pthread_mutex_unlock(&mainPostBuf.mutex);
156 void postfl(const char *fmt, ...)
162 int len = vsnprintf(buf, sizeof(buf), fmt, ap);
167 void post(const char *fmt, ...)
173 int len = vsnprintf(buf, sizeof(buf), fmt, ap);
178 void error(const char *fmt, ...)
184 int len = vsnprintf(buf, sizeof(buf), fmt, ap);
189 void postText(const char *text, long length)
192 strncpy(buf, text, length);
195 vposttext(buf, length);
198 void postChar(char c)
209 if (theController) mainPostBuf.Flush([theController logView]);
212 void setPostFile(FILE* file)
216 int vpost(const char *fmt, va_list ap)
219 int len = vsnprintf(buf, sizeof(buf), fmt, ap);
226 void setCmdLine(const char *buf)
228 int size = strlen(buf);
230 pthread_mutex_lock(&gLangMutex);
232 VMGlobals *g = gMainVMGlobals;
234 PyrString* strobj = newPyrStringN(g->gc, size, 0, true);
235 memcpy(strobj->s, buf, size);
237 SetObject(&slotRawInterpreter(&g->process->interpreter)->cmdLine, strobj);
238 g->gc->GCWrite(slotRawObject(&g->process->interpreter), strobj);
240 pthread_mutex_unlock(&gLangMutex);
244 void AudioSessionAudioRouteChangeCbk(void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
246 iSCLangController *c = (iSCLangController *) inClientData;
247 if (inID==kAudioSessionProperty_AudioRouteChange && inData)
249 CFDictionaryRef dict = (CFDictionaryRef) inData;
250 CFNumberRef reason = (CFNumberRef) CFDictionaryGetValue(dict, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
252 CFNumberGetValue(reason, kCFNumberSInt32Type, &r);
253 if (r==kAudioSessionRouteChangeReason_Override) return;
256 UInt32 size = sizeof(route);
257 AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &route);
258 if (!CFStringCompare(route, CFSTR("Speaker"), 0)) [[c speakersButton] setStyle:UIBarButtonItemStyleDone];
259 else [[c speakersButton] setStyle:UIBarButtonItemStyleBordered];
262 c.routeOverride = kAudioSessionOverrideAudioRoute_None;
263 [[c speakersButton] setStyle:UIBarButtonItemStyleBordered];
265 UInt32 override = c.routeOverride;
266 if (override==kAudioSessionOverrideAudioRoute_Speaker)
268 AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(override), &override);
274 @implementation iSCLangController
276 @synthesize routeOverride;
278 + (iSCLangController *) sharedInstance
280 return theController;
291 if (self=[super init])
293 theController = self;
294 deferredOperations = [NSMutableArray arrayWithCapacity: 8];
295 [deferredOperations retain];
301 - (void) awakeFromNib
303 routeOverride = kAudioSessionOverrideAudioRoute_None;
304 AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(routeOverride), &routeOverride);
306 AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, AudioSessionAudioRouteChangeCbk, self);
309 NSFileManager *manager = [NSFileManager defaultManager];
310 CFBundleRef bundle = CFBundleGetMainBundle();
311 CFURLRef url = CFBundleCopyBundleURL(bundle);
312 NSString *s = (NSString *) CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
316 char supportpath[256];
317 sc_GetUserAppSupportDirectory(supportpath, 256);
318 NSString *support = [NSString stringWithCString:supportpath encoding:NSASCIIStringEncoding];
320 if (![manager fileExistsAtPath:support]) [manager createDirectoryAtPath:support attributes:nil];
323 dir = [support stringByAppendingString:@"/SCClassLibrary"];
324 if (![manager fileExistsAtPath:dir])
326 [manager createDirectoryAtPath:dir attributes:nil];
328 NSString *from, *dest;
329 from = [s stringByAppendingString:@"/Common"];
330 dest = [dir stringByAppendingString:@"/Common"];
331 [manager copyItemAtPath:from toPath:dest error:&error];
332 from = [s stringByAppendingString:@"/DefaultLibrary"];
333 dest = [dir stringByAppendingString:@"/DefaultLibrary"];
334 [manager copyItemAtPath:from toPath:dest error:&error];
335 from = [s stringByAppendingString:@"/Platform"];
336 dest = [dir stringByAppendingString:@"/Platform"];
337 [manager copyItemAtPath:from toPath:dest error:&error];
338 from = [s stringByAppendingString:@"/backwards_compatibility"];
339 dest = [dir stringByAppendingString:@"/backwards_compatibility"];
340 [manager copyItemAtPath:from toPath:dest error:&error];
341 from = [s stringByAppendingString:@"/JITLib"];
342 dest = [dir stringByAppendingString:@"/JITLib"];
343 [manager copyItemAtPath:from toPath:dest error:&error];
344 from = [s stringByAppendingString:@"/SCDoc"];
345 dest = [dir stringByAppendingString:@"/SCDoc"];
346 [manager copyItemAtPath:from toPath:dest error:&error];
348 dir = [support stringByAppendingString:@"/sounds"];
349 if (![manager fileExistsAtPath:dir])
351 NSString *from = [s stringByAppendingString:@"/sounds"];
352 if ([manager fileExistsAtPath:from])
354 [manager copyItemAtPath:from toPath:dir error:&error];
357 dir = [support stringByAppendingString:@"/Extensions"];
358 if (![manager fileExistsAtPath:dir])
360 NSString *from = [s stringByAppendingString:@"/Extensions"];
361 if ([manager fileExistsAtPath:from])
363 [manager copyItemAtPath:from toPath:dir error:&error];
366 dir = [support stringByAppendingString:@"/plugins"];
367 if (![manager fileExistsAtPath:dir])
369 NSString *from = [s stringByAppendingString:@"/plugins"];
370 if ([manager fileExistsAtPath:from])
372 [manager copyItemAtPath:from toPath:dir error:&error];
375 dir = [support stringByAppendingString:@"/patches"];
376 if (![manager fileExistsAtPath:dir]) [manager createDirectoryAtPath:dir attributes:nil];
377 NSString *patches_dir = [s stringByAppendingString:@"/patches"];
378 NSArray *patches = [manager contentsOfDirectoryAtPath:patches_dir error:nil];
379 for (NSString *patch in patches)
381 NSString *origin = [patches_dir stringByAppendingPathComponent:patch];
382 NSString *destination = [dir stringByAppendingPathComponent:patch];
383 if ([manager fileExistsAtPath:destination]) [manager removeItemAtPath:destination error:nil];
384 [manager copyItemAtPath:origin toPath:destination error:&error];
386 dir = [support stringByAppendingString:@"/Recordings"];
387 if (![manager fileExistsAtPath:dir])
389 [manager createDirectoryAtPath:dir attributes:nil];
391 dir = [support stringByAppendingString:@"/synthdefs"];
392 if (![manager fileExistsAtPath:dir])
394 [manager createDirectoryAtPath:dir attributes:nil];
396 dir = [support stringByAppendingString:@"/tmp"];
397 if ([manager fileExistsAtPath:dir]) [manager removeItemAtPath:dir error:nil];
398 [manager createDirectoryAtPath:dir attributes:nil];
404 [logView setFont:[[logView font] fontWithSize:9.0f]];
405 [logView setTextColor:[UIColor blueColor]];
408 //path = [support stringByAppendingString:@"/patches"];
409 [browserViewController setTarget:self withSelector:@selector(selectFile:)];
410 [browserViewController setPath:support];
412 [tabBarController setCustomizableViewControllers:nil];
414 [liveViewController setTarget:self withSelector:@selector(interpret:)];
416 #ifdef START_HTTP_SERVER
417 HTTPServer *httpServer = [HTTPServer new];
418 //[httpServer setType:@"_webdav._tcp."];
419 [httpServer setPort:8080];
420 [httpServer setConnectionClass:[MyHTTPConnection class]];
421 [httpServer setDocumentRoot:[NSURL fileURLWithPath:support]];
422 [httpServer start:&error];
425 //[self performSelectorInBackground:@selector(start:) withObject:nil];
426 [self performSelector:@selector(start:) withObject:nil afterDelay:0.2f];
429 - (void) start:(id)arg
431 //pyr_init_mem_pools(2*1024*1024, 256*1024);
432 pyr_init_mem_pools(1*1024*1024, 256*1024);
437 appClockTimer = [NSTimer scheduledTimerWithTimeInterval:0.02f target:self selector:@selector(doClockTask:) userInfo:nil repeats:YES];
438 deferredTaskTimer = [NSTimer scheduledTimerWithTimeInterval: 0.038 target: self selector:@selector(doPeriodicTask:) userInfo: nil repeats: YES];
440 s_stop = getsym("stop");
441 s_interpretPrintCmdLine = getsym("interpretPrintCmdLine");
444 - (void) selectFile:(NSString *)string
446 NSString *ext = [string pathExtension];
447 if (![ext compare:@"aif"] || ![ext compare:@"aiff"] || ![ext compare:@"wav"]) [self selectRecording:string];
448 else [self selectPatch:string];
451 - (void) selectPatch:(NSString *)string
453 [liveViewController loadFile:string];
454 [tabBarController performSelector:@selector(setSelectedViewController:) withObject:liveViewController afterDelay:0.5f];
457 - (void) interpret: (NSString *) string
459 int length = [string length];
460 char *cmd = (char *) malloc(length+1);
461 [string getCString:cmd maxLength:length+1 encoding:NSASCIIStringEncoding];
464 if (pthread_mutex_trylock(&gLangMutex) == 0)
466 runLibrary(s_interpretPrintCmdLine);
467 pthread_mutex_unlock(&gLangMutex);
472 - (UITextView *) logView
477 - (UIBarButtonItem *) speakersButton
479 return speakersButton;
482 - (void)doPeriodicTask: (NSTimer*) timer
484 [self performDeferredOperations];
485 //[self doAnimatedViews];
489 - (void)doClockTask: (NSTimer*) timer
491 if (pthread_mutex_trylock(&gLangMutex) == 0)
493 if (compiledOK) runLibrary(s_tick);
494 pthread_mutex_unlock(&gLangMutex);
499 - (void) triggerStop:(id)sender
501 if (pthread_mutex_trylock(&gLangMutex) == 0)
504 pthread_mutex_unlock(&gLangMutex);
509 - (void) toggleSpeakers:(id)sender
511 UIBarButtonItem *b = (UIBarButtonItem *) sender;
512 if (routeOverride==kAudioSessionOverrideAudioRoute_None) routeOverride = kAudioSessionOverrideAudioRoute_Speaker;
513 else routeOverride = kAudioSessionOverrideAudioRoute_None;
514 AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(routeOverride), &routeOverride);
516 [b setStyle:(routeOverride==kAudioSessionOverrideAudioRoute_None)?UIBarButtonItemStyleBordered:UIBarButtonItemStyleDone];
519 - (void) insertWindow:(SCNSWindow *)window
521 NSArray *controllers = [tabBarController viewControllers];
522 controllers = [controllers arrayByAddingObject:[window controller]];
523 [tabBarController setViewControllers:controllers animated:YES];
526 - (void) makeWindowFront:(SCNSWindow *)window
528 UINavigationController *more = [tabBarController moreNavigationController];
530 if (more) [tabBarController setSelectedViewController:more];
531 else [tabBarController setSelectedViewController:[window controller]];
533 NSArray *controllers = [tabBarController viewControllers];
534 if ([controllers count]>5) [tabBarController setSelectedViewController:more];
535 else [tabBarController setSelectedViewController:[window controller]];
538 - (void) closeWindow:(SCNSWindow *)window
540 NSArray *controllers = [tabBarController viewControllers];
541 NSArray *new_controllers = [NSArray array];
542 UIViewController *the_controller = [window controller];
543 UIViewController *selection;
544 for (UIViewController *v in controllers)
546 if (v!=the_controller)
548 new_controllers = [new_controllers arrayByAddingObject:v];
552 [tabBarController setViewControllers:new_controllers animated:YES];
553 [tabBarController setSelectedViewController:selection];
557 - (void)defer: (NSInvocation*) action
559 [deferredOperations addObject: action];
563 - (void)removeDeferredOperationsFor:(id) object
565 NSMutableArray *newArray = [NSMutableArray arrayWithCapacity: 8];
567 for (unsigned int i=0; i<[deferredOperations count]; ++i) {
568 NSInvocation* action = (NSInvocation*)[deferredOperations objectAtIndex: i];
569 if ([action target] != object) {
570 [newArray addObject: action];
573 [deferredOperations release];
574 deferredOperations = newArray;
577 - (void)performDeferredOperations
579 while ([deferredOperations count]) {
580 NSInvocation* action = (NSInvocation*)[deferredOperations objectAtIndex: 0];
582 [deferredOperations removeObjectAtIndex: 0];
583 ///NSLog(@"%d %@ %08X\n", [deferredOperations count], action, [action target]);
589 - (void) selectRecording:(NSString *)string
591 NSURL *url = [NSURL fileURLWithPath:string];
592 recordingPlayer = [[MPMoviePlayerController alloc] initWithContentURL:url];
593 [recordingPlayer setScalingMode:MPMovieScalingModeAspectFill];
594 [recordingPlayer setMovieControlMode:MPMovieControlModeDefault];
595 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(recordingPlaybackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:recordingPlayer];
596 [recordingPlayer play];
599 - (void) recordingPlaybackDidFinish:(NSNotification *)notification
601 [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:recordingPlayer];
602 if (recordingPlayer) [recordingPlayer release];
608 [deferredOperations release];