Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / editors / scapp / iPhone / iSCLangController.mm
blobba0c18d5b9c8f72e7a15c74b26276ffe234275cc
1 //
2 //  iSCLangController.m
3 //  isclang
4 //
5 //  Created by Axel Balley on 26/10/08.
6 //  Copyright 2008 __MyCompanyName__. All rights reserved.
7 //
8 #import <AudioToolbox/AudioToolbox.h>
9 #import <MediaPlayer/MediaPlayer.h>
12 #import "HTTPServer.h"
13 #import "MyHTTPConnection.h"
16 #import "iSCLangController.h"
17 #include <pthread.h>
18 #include "PyrObject.h"
19 #include "PyrKernel.h"
20 #include "GC.h"
21 #include "VMGlobals.h"
22 #include "SCBase.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;
34 PyrSymbol* s_stop;
35 PyrSymbol* s_interpretPrintCmdLine;
38 static iSCLangController* theController = 0;
40 void closeAllGUIScreens()
46 void initGUI()
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
70 struct PostBuf {
71         char *buf;
72         long wrpos;
73         long rdpos;
74         pthread_mutex_t mutex;
76         void Init();
77         void Flush(UITextView *view);
80 static PostBuf mainPostBuf;
82 #define POSTBUFLEN 131072
83 #define POSTBUFMASK 131071
85 void PostBuf::Init()
87         buf = (char *)malloc(POSTBUFLEN);
88         //HoldMemory(buf, POSTBUFLEN);
89         wrpos = 0;
90         rdpos = 0;
91         pthread_mutex_init(&mutex, NULL);
94 void PostBuf::Flush(UITextView *logView)
96         long numtoread;
97         long localwritepos = wrpos;
99         if (localwritepos >= rdpos) {
100                 numtoread = localwritepos - rdpos;
101         } else {
102                 numtoread = POSTBUFLEN - (rdpos - localwritepos);
103         }
104         if (numtoread > 0) {
105                 long endpos;
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;
113                         secondpart = endpos;
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];
119                         rdpos = endpos;
120                 } else {
121                         NSString *s = [[logView text] stringByAppendingString:[NSString stringWithCString: buf + rdpos length: numtoread]];
122                         [logView setText:s];
124                         if (endpos == POSTBUFLEN) rdpos = 0;
125                         else rdpos = endpos;
126                 }
127         int offset = [logView contentSize].height - [logView bounds].size.height;
128         if (offset>=0) [logView setContentOffset:CGPointMake(0,offset) animated:NO];
129         }
132 void initPostBuffer();
133 void initPostBuffer()
135         mainPostBuf.Init();
138 void vposttext(const char *str, int length);
139 void vposttext(const char *str, int length)
141         printf(str);
143         pthread_mutex_lock(&mainPostBuf.mutex);
145         for (int i=0; i<length && str[i]; ++i) {
146                 if (((mainPostBuf.wrpos+1) & POSTBUFMASK) == mainPostBuf.rdpos) {
147                         break;
148                         //mainPostBuf.Flush(); CANNOT DO THIS FROM OTHER THAN COCOA'S THREAD!
149                 }
150                 mainPostBuf.buf[mainPostBuf.wrpos] = str[i];
151                 mainPostBuf.wrpos = (mainPostBuf.wrpos+1) & POSTBUFMASK;
152         }
153         pthread_mutex_unlock(&mainPostBuf.mutex);
156 void postfl(const char *fmt, ...)
158         va_list ap;
159         va_start(ap, fmt);
161         char buf[512];
162         int len = vsnprintf(buf, sizeof(buf), fmt, ap);
164         vposttext(buf, len);
167 void post(const char *fmt, ...)
169         va_list ap;
170         va_start(ap, fmt);
172         char buf[512];
173         int len = vsnprintf(buf, sizeof(buf), fmt, ap);
175         vposttext(buf, len);
178 void error(const char *fmt, ...)
180         va_list ap;
181         va_start(ap, fmt);
183         char buf[512];
184         int len = vsnprintf(buf, sizeof(buf), fmt, ap);
186         vposttext(buf, len);
189 void postText(const char *text, long length)
191         char buf[512];
192         strncpy(buf, text, length);
193         buf[length] = 0;
195         vposttext(buf, length);
198 void postChar(char c)
200         char buf[2];
201         buf[0] = c;
202         buf[1] = 0;
204         vposttext(buf, 1);
207 void flushPostBuf()
209         if (theController) mainPostBuf.Flush([theController logView]);
212 void setPostFile(FILE* file)
216 int vpost(const char *fmt, va_list ap)
218         char buf[512];
219         int len = vsnprintf(buf, sizeof(buf), fmt, ap);
221         vposttext(buf, len);
222         return 0;
226 void setCmdLine(const char *buf)
228         int size = strlen(buf);
229         if (compiledOK) {
230                 pthread_mutex_lock(&gLangMutex);
231                 if (compiledOK) {
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);
239                 }
240                 pthread_mutex_unlock(&gLangMutex);
241         }
244 void AudioSessionAudioRouteChangeCbk(void *inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
246         iSCLangController *c = (iSCLangController *) inClientData;
247         if (inID==kAudioSessionProperty_AudioRouteChange && inData)
248         {
249                 CFDictionaryRef dict = (CFDictionaryRef) inData;
250                 CFNumberRef reason = (CFNumberRef) CFDictionaryGetValue(dict, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
251                 SInt32 r;
252                 CFNumberGetValue(reason, kCFNumberSInt32Type, &r);
253                 if (r==kAudioSessionRouteChangeReason_Override) return;
254                 /*
255                 CFStringRef route;
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];
260                 CFRelease(route);
261                 */
262                 c.routeOverride = kAudioSessionOverrideAudioRoute_None;
263                 [[c speakersButton] setStyle:UIBarButtonItemStyleBordered];
264                 /*
265                 UInt32 override = c.routeOverride;
266                 if (override==kAudioSessionOverrideAudioRoute_Speaker)
267                 {
268                         AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(override), &override);
269                 }
270                 */
271         }
274 @implementation iSCLangController
276 @synthesize routeOverride;
278 + (iSCLangController *) sharedInstance
280         return theController;
283 - (id) init
285         if (theController)
286         {
287                 [super dealloc];
288                 return 0;
289         }
291         if (self=[super init])
292         {
293                 theController = self;
294                 deferredOperations = [NSMutableArray arrayWithCapacity: 8];
295                 [deferredOperations retain];
296         }
298         return self;
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);
313         CFRelease(url);
315         NSError *error;
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];
322         NSString *dir;
323         dir = [support stringByAppendingString:@"/SCClassLibrary"];
324         if (![manager fileExistsAtPath:dir])
325         {
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];
347         }
348         dir = [support stringByAppendingString:@"/sounds"];
349         if (![manager fileExistsAtPath:dir])
350         {
351                 NSString *from = [s stringByAppendingString:@"/sounds"];
352                 if ([manager fileExistsAtPath:from])
353                 {
354                         [manager copyItemAtPath:from toPath:dir error:&error];
355                 }
356         }
357         dir = [support stringByAppendingString:@"/Extensions"];
358         if (![manager fileExistsAtPath:dir])
359         {
360                 NSString *from = [s stringByAppendingString:@"/Extensions"];
361                 if ([manager fileExistsAtPath:from])
362                 {
363                         [manager copyItemAtPath:from toPath:dir error:&error];
364                 }
365         }
366         dir = [support stringByAppendingString:@"/plugins"];
367         if (![manager fileExistsAtPath:dir])
368         {
369                 NSString *from = [s stringByAppendingString:@"/plugins"];
370                 if ([manager fileExistsAtPath:from])
371                 {
372                         [manager copyItemAtPath:from toPath:dir error:&error];
373                 }
374         }
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)
380         {
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];
385         }
386         dir = [support stringByAppendingString:@"/Recordings"];
387         if (![manager fileExistsAtPath:dir])
388         {
389                 [manager createDirectoryAtPath:dir attributes:nil];
390         }
391         dir = [support stringByAppendingString:@"/synthdefs"];
392         if (![manager fileExistsAtPath:dir])
393         {
394                 [manager createDirectoryAtPath:dir attributes:nil];
395         }
396         dir = [support stringByAppendingString:@"/tmp"];
397         if ([manager fileExistsAtPath:dir]) [manager removeItemAtPath:dir error:nil];
398         [manager createDirectoryAtPath:dir attributes:nil];
400         CFRelease(s);
403         initPostBuffer();
404         [logView setFont:[[logView font] fontWithSize:9.0f]];
405         [logView setTextColor:[UIColor blueColor]];
407         NSString *path;
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];
423 #endif
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);
433         init_OSC(57120);
434         schedInit();
435         compileLibrary();
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];
462         setCmdLine(cmd);
464         if (pthread_mutex_trylock(&gLangMutex) == 0)
465         {
466                 runLibrary(s_interpretPrintCmdLine);
467                 pthread_mutex_unlock(&gLangMutex);
468         }
469         free(cmd);
472 - (UITextView *) logView
474         return logView;
477 - (UIBarButtonItem *) speakersButton
479         return speakersButton;
482 - (void)doPeriodicTask: (NSTimer*) timer
484         [self performDeferredOperations];
485         //[self doAnimatedViews];
486         flushPostBuf();
489 - (void)doClockTask: (NSTimer*) timer
491         if (pthread_mutex_trylock(&gLangMutex) == 0)
492         {
493                 if (compiledOK) runLibrary(s_tick);
494                 pthread_mutex_unlock(&gLangMutex);
495     }
496     flushPostBuf();
499 - (void) triggerStop:(id)sender
501         if (pthread_mutex_trylock(&gLangMutex) == 0)
502         {
503         runLibrary(s_stop);
504         pthread_mutex_unlock(&gLangMutex);
505         }
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];
529         /*
530         if (more) [tabBarController setSelectedViewController:more];
531         else [tabBarController setSelectedViewController:[window controller]];
532         */
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)
545         {
546                 if (v!=the_controller)
547                 {
548                         new_controllers = [new_controllers arrayByAddingObject:v];
549                         selection = v;
550                 }
551         }
552         [tabBarController setViewControllers:new_controllers animated:YES];
553         [tabBarController setSelectedViewController:selection];
554         [window close];
557 - (void)defer: (NSInvocation*) action
559     [deferredOperations addObject: action];
563 - (void)removeDeferredOperationsFor:(id) object
565         NSMutableArray *newArray = [NSMutableArray arrayWithCapacity: 8];
566         [newArray retain];
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];
571                 }
572         }
573         [deferredOperations release];
574         deferredOperations = newArray;
577 - (void)performDeferredOperations
579     while ([deferredOperations count]) {
580                 NSInvocation* action = (NSInvocation*)[deferredOperations objectAtIndex: 0];
581                 [action retain];
582         [deferredOperations removeObjectAtIndex: 0];
583                 ///NSLog(@"%d %@ %08X\n", [deferredOperations count], action, [action target]);
584                 [action invoke];
585                 [action release];
586     }
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];
603         recordingPlayer = 0;
606 - (void) dealloc
608         [deferredOperations release];
610         [super dealloc];
613 @end