/io/AppDelegate.m
Objective C | 545 lines | 315 code | 148 blank | 82 comment | 32 complexity | 556091471f9698467828eb25dbed9fe0 MD5 | raw file
- //
- // AppDelegate.m
- // io
- //
- // Created by Jørgen Skogmo on 21-05-2012.
- // Copyright (c) 2012 base.io. All rights reserved.
- //
- // 'stringyfication' macros to access preprocessor macros by name.
- // useage: NSLog(@"%s", MACRO_VALUE(SRC_ROOT) );
- // see: http://stackoverflow.com/questions/242817/xcode-preprocessor-macros and
- // http://stackoverflow.com/questions/3261763/accessing-the-value-of-a-preprocessor-macro-definition?rq=1
- //TODO: Make WebKit images too: http://cocoadevblog.heroku.com/webkit-screenshots-cocoa-objective-c
- #define MACRO_NAME(f) #f
- #define MACRO_VALUE(f) MACRO_NAME(f)
- #import "AppDelegate.h"
- // K
- static NSString *const kKinectEnablePrefKey = @"kinectEnabled"; // BOOL
- static NSString *const kLedPrefKey = @"led"; // 0..6
- static NSString *const kTiltPrefKey = @"tilt"; // -30..30
- static NSString *const kTrackCenterPrefKey = @"trackCenter"; // BOOL
- static NSString *const kMirrorPrefKey = @"mirror"; // BOOL
- static NSString *const kAlignPrefKey = @"align"; // BOOL
- static NSString *const kNormalsPrefKey = @"normals"; // BOOL
- static NSString *const kModePrefKey = @"mode"; // MainScene.drawMode
- static NSString *const kPrefsVisiblePrefsKey = @"prefsVisible"; // BOOL
- // IO
- static NSString *const kWebApplicationRoot = @"WebApplicationRoot"; // String
- static NSString *const kSelectedWebApp = @"SelectedWebApp"; // String
- // these were really only intended as a convenience whilst testing...
- static void forTrackerKeys(void(^block)(NSString *key, NSString *prefKey)) {
- NSString *prefix = @"track";
- NSString *keys[] = {@"farZ", @"passCount", @"drawDebug", @"cullBackgroundPortion", @"cylinderRadius", @"handBodyRatio"};
- for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) {
- // append prefix and capitalise first letter of key
- NSString *prefKey = [NSString stringWithFormat:@"%@%@%@", prefix, [[keys[i] substringToIndex:1] uppercaseString], [keys[i] substringFromIndex:1]];
- block(keys[i], prefKey);
- }
- }
- @implementation AppDelegate
- @synthesize window = _window;
- @synthesize webView;
- @synthesize recorder;
- @synthesize recorderMenu;
- @synthesize startStopButton;
- @synthesize pauseUnpauseButton;
- @synthesize menuSeparator;
- @synthesize prefsPanel = _prefsPanel;
- @synthesize appVersionString;
- // K
- @synthesize viewFps = _viewFps;
- @synthesize status = _status;
- @synthesize depthFps = _depthFps;
- @synthesize videoFps = _videoFps;
- @synthesize trackPosition = _trackPosition;
- id webInspector;
- JavaScriptIntegration *JSInt;
- - (void)awakeFromNib {
- // Configure Kinect
- id defs = [NSUserDefaultsController sharedUserDefaultsController];
-
- _kinect = [[KinectManager alloc] init];
- [_kinect bind:@"enabled" toObject:defs withKeyPath:[@"values." stringByAppendingString:kKinectEnablePrefKey] options:nil];
- [_kinect bind:@"led" toObject:defs withKeyPath:[@"values." stringByAppendingString:kLedPrefKey] options:nil];
- [_kinect bind:@"tilt" toObject:defs withKeyPath:[@"values." stringByAppendingString:kTiltPrefKey] options:nil];
- [_kinect bind:@"align" toObject:defs withKeyPath:[@"values." stringByAppendingString:kAlignPrefKey] options:nil];
-
- // for convience update local ivars to reflect those from the kinect so can KVO in UI
- [self bind:@"videoFps" toObject:_kinect withKeyPath:@"videoFps" options:nil];
- [self bind:@"depthFps" toObject:_kinect withKeyPath:@"depthFps" options:nil];
- [self bind:@"status" toObject:_kinect withKeyPath:@"status" options:nil];
-
- _tracker = [[Tracker alloc] init];
- [_tracker bind:@"enabled" toObject:defs withKeyPath:[@"values." stringByAppendingString:kTrackCenterPrefKey] options:nil];
-
- // bind tracker to defaults, and ensure that initial values are sensible
- NSMutableDictionary *dict = [NSMutableDictionary dictionary];
- forTrackerKeys(^(NSString *key, NSString *prefKey) {
- [dict setObject:[_tracker valueForKey:key] forKey:prefKey];
- });
- [[defs defaults] registerDefaults:dict];
- forTrackerKeys(^(NSString *key, NSString *prefKey) {
- [_tracker bind:key toObject:defs withKeyPath:[@"values." stringByAppendingString:prefKey] options:nil];
- });
- _lastPollDate = [NSDate date];
-
- NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(poll) userInfo:nil repeats:YES];
- [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
- [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
-
-
-
- //
-
- [(CustomWindow *)[[NSApp delegate] window] BuildAvailScreensPopup];
-
- }
- - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
-
- BLOCK_TEXTSELECTION = YES;
-
- UDP_HOST = @"127.0.0.1";
- UDP_PORT = 3333;
-
- //[ImageUtils test];
-
- NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
- appVersionString = [NSString stringWithFormat:@"Version: %@", [infoDict objectForKey:@"CFBundleVersion"] ];
- NSLog(@"appVersionString: %@", appVersionString);
- [appVersionField setStringValue:appVersionString];
-
- // When 10.8 is released, we can not change contents of our AppBundle (where the .webapps is currently stored) without re-signing the app
- // Anyway, expecting the user to develop/tweak his own apps working out of the AppBundle is no-good.
- // So: Let the User choose a webapps directory. Store it in Prefs. Use it as APPS_ROOT. (Make sure ioapi.js is loaded from bundle though).
- // Default to AppBundle/Resources/Apps/, so customer-installations will run straight away.
-
- // if no WebApplicationRoot is present in Prefs, use default location
- id pAppRoot = [[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey:kWebApplicationRoot];
-
- //pAppRoot = nil; // TEST
-
- if( pAppRoot ){
- NSLog(@"HAS webroot: %@", pAppRoot);
- APPS_ROOT = [NSString stringWithFormat:@"%@", pAppRoot];
- }else{
- NSString *defRoot = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/apps/"];
- NSLog(@"HAS NO webroot, setting default to: %@", defRoot);
- [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:defRoot forKey:kWebApplicationRoot];
- APPS_ROOT = [NSString stringWithFormat:@"%@", defRoot];
- }
-
- // See NSUserDefaultsController docs here https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaBindings/Concepts/NSUserDefaultsController.html
-
- /*
- // During dev, load all apps from Source Directory instead of the app bundle
- #ifdef SRC_ROOT
- NSLog(@"1 src->%s", MACRO_VALUE(SRC_ROOT));
- APPS_ROOT = [NSString stringWithFormat:@"%s", MACRO_VALUE(SRC_ROOT) ];
- #else
- APPS_ROOT = [[NSBundle mainBundle] resourcePath];
- #endif
-
- NSLog(@"Raw APPS_ROOT -> %@", APPS_ROOT);
- APPS_ROOT = [APPS_ROOT stringByAppendingString:@"/apps/"];
- NSLog(@"AppDelegate: APPS_ROOT -> %@", APPS_ROOT);
- */
-
- [self ListBundledDocRoots];
-
- bShowCursor = NO;
- // Configure Screen Recorder
- self.recorder = [[MCScreenRecorder alloc] init];
- [recorderMenu setAutoenablesItems:NO];
-
-
- // K
- // because of the need to share the context between the two glviews it isn't easily possibly to do this in the NIB
- // instead we replace a placeholder view with a glview once all is ready
- previewglview = [[GLView alloc] initByReplacingView:previewglview sharedContext:mainglview];
-
- /*
- if([[NSUserDefaults standardUserDefaults] boolForKey:kPrefsVisiblePrefsKey]) {
- [[previewglview window] makeKeyAndOrderFront:nil];
- }
- */
-
- // Initialize JavaScript integration
- JSInt = [[JavaScriptIntegration alloc] init];
- [JSInt initWithWebView:webView andWindow:mainWindow];
-
- // Init Communications Socket
- [SharedSockets UDPSend:@"SharedUDPSocket ready message"];
- // Start HTTP Server. Note: I have modified HTTPConnection.m with improved MIME-types
- httpServer = [[HTTPServer alloc] init];
- [httpServer setPort:HTTP_PORT];
-
-
- // Start 'Private' HTTP Server (to load shared/special assets)
- httpServerPrivate = [[HTTPServer alloc] init];
- [httpServerPrivate setPort:HTTP_PORT_PRIVATE];
- [httpServerPrivate setDocumentRoot:APPS_ROOT];
- NSError *error = nil;
- if([httpServerPrivate start:&error]){
- NSLog(@"httpServerPrivate: Started with document root /Users/js/Dropbox/base.io/projects/io/apps/");
- }else{
- NSLog(@"httpServerPrivate: Error: %@", error);
- }
-
- // Load default application
-
- NSString *app = @"api";
- id pSelApp = [[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey:kSelectedWebApp];
- NSLog(@"Prefs for pSelApp: %@", pSelApp);
- if( pSelApp ){
- app = [NSString stringWithFormat:@"%@", pSelApp];
- }
- [self LoadWebapp:app];
-
-
- [(CustomWindow *)[[NSApp delegate] window] SetWidth:1280 Height:720];
- }
- - (void)applicationWillTerminate:(NSNotification *)notification {
- // remember if prefs window was open
- [[NSUserDefaults standardUserDefaults] setBool:[[previewglview window] isVisible] forKey:kPrefsVisiblePrefsKey];
-
- [mainglview setPaused:YES];
- //[previewglview setPaused:YES];
- [_kinect setEnabled:NO]; // ensure it stops
- }
- #pragma mark UICode
- - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
- return YES;
- }
- BOOL bShowCursor;
- - (IBAction)toggleMouse:(id)sender {
- bShowCursor = !bShowCursor;
- NSLog(@"toggleMouse: %@", (bShowCursor ? @"TRUE" : @"FALSE") );
- if( bShowCursor ){
- [NSCursor hide];
- [sender setState:1];
- }else{
- [NSCursor unhide];
- [sender setState:0];
- }
- }
- BOOL bWebInspectorOpen;
- - (IBAction) toggleWebInspector:(id *)sender {
- if( bWebInspectorOpen ){
- bWebInspectorOpen = NO;
- [webInspector close:webView];
- }else{
- bWebInspectorOpen = YES;
- [webInspector detach:webView];
- [webInspector showConsole:webView];
- }
- }
- - (IBAction)showPrefsPanel:(id)sender {
- if( ![self.prefsPanel isVisible] ){
- NSLog(@"open PrefsPanel");
-
- //[self.prefsPanel delegate]
-
- [self.prefsPanel makeKeyAndOrderFront:sender];
- }else{
- NSLog(@"close PrefsPanel");
- //[self.prefsPanel close];
- [self.prefsPanel orderOut:nil];
- }
- }
- - (BOOL)windowShouldClose:(id)sender{
- NSLog(@"PREFS windowShouldClose");
-
- if( [self.prefsPanel isVisible] ){
- NSLog(@"PrefsPanel - ordering out");
- [self.prefsPanel orderOut:nil];
- }
- return NO;
- }
- -(IBAction)PSetDocRoot:(id)sender{
- NSString *appName = [sender title];
- NSLog(@"PSetDocRoot to %@", appName);
-
- [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:appName forKey:kSelectedWebApp];
-
- [self LoadWebapp:appName ];
- }
- -(void)LoadWebapp:(NSString *) appName {
- NSLog(@"LoadWebapp '%@'", appName );
-
- [Cache Disable];
-
- WEBAPP_ROOT = [NSString stringWithFormat:@"%@%@.webapp/", APPS_ROOT, appName];
- [self RestartHTTPServer];
- NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:%i/index.html", HTTP_PORT];
- [webView setMainFrameURL:url];
-
-
- }
- -(void)RestartHTTPServer {
-
- [httpServer stop];
- [httpServer setDocumentRoot:WEBAPP_ROOT];
- [httpServer setPort:HTTP_PORT];
-
- NSError *error = nil;
- if([httpServer start:&error]){
- NSLog(@"HTTPServer: (Re)started with document root: %@", WEBAPP_ROOT);
- }else{
- NSLog(@"Error starting HTTP Server: %@", error);
- }
-
- }
- -(void)ReloadWebView {
- //NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:%i/index.html", HTTP_PORT];
- //[webView setMainFrameURL:url];
-
- [webView reload:nil];
- }
- // -------------------------------------------------------------------------------
- // Recording
- - (IBAction)startStopRecording:(id)sender {
- if (self.recorder.session.running) {
- [self.recorder stop];
-
- // Update UI
- [startStopButton setTitle:@"Start"];
- [pauseUnpauseButton setEnabled:NO];
-
- } else {
-
- float wx = self.window.frame.origin.x;
- float wy = self.window.frame.origin.y;
- float ww = self.window.frame.size.width;
- float wh = self.window.frame.size.height;
- NSLog(@"starting recording for Rect(%f, %f, %f, %f)", wx, wy, ww, wh);
- self.recorder.input.cropRect = CGRectMake(wx, wy, ww, wh);
-
- [self.recorder start];
-
- // Update UI
- [startStopButton setTitle:@"Stop"];
- [pauseUnpauseButton setEnabled:YES];
- [menuSeparator setHidden:NO];
-
- // Add a menu item for the new recording
- [recorderMenu addItem:[[MCScreenRecordingMenuItem alloc] initWithFile:self.recorder.file]];
- }
-
- // We always want to reset the title
- [pauseUnpauseButton setTitle:@"Pause"];
- }
- - (IBAction)pauseUnpauseRecording:(id)sender {
- if ([self.recorder toggle])
- [pauseUnpauseButton setTitle:@"Pause"];
- else
- [pauseUnpauseButton setTitle:@"Resume"];
- }
- // -------------------------------------------------------------------------------
- // Preferences
- // Document Root
- NSString *PDocumentRoot;
- -(void)ListBundledDocRoots {
- // All folders matching '$APPS_ROOT/apps/*.webapp/index.html'
-
- NSLog(@"ListBundledDocRoots LISTING: %@", APPS_ROOT);
- NSMutableArray *roots = [[NSMutableArray alloc] init];//WithCapacity:10];
- NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:APPS_ROOT error:NULL];
- for (NSString *f in files) {
- if( [f hasSuffix:@".webapp"] ){
- NSString *fullPath = [APPS_ROOT stringByAppendingPathComponent:f];
- BOOL isDir;
- if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir] &&isDir) {
- for (NSString *sf in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fullPath error:NULL] ) {
- if( [sf isEqualToString:@"index.html"] ){
- NSString *folderName = [fullPath lastPathComponent];
- NSArray *fp = [folderName componentsSeparatedByString:@".webapp"];
- [roots addObject: [fp objectAtIndex:0] ];
- }
- }
- }
- }
- }
- NSLog(@"ListBundledDocRoots: %@", roots );
- [P_DocRootPopup removeAllItems];
- [P_DocRootPopup addItemsWithTitles: roots];
- }
- // -------------------------------------------------------------------------------
- // APPS_ROOT
- -(IBAction)ShowSelectAppsRootDialog:(id)sender {
- NSOpenPanel *openPanel = [NSOpenPanel openPanel];
- [openPanel setCanChooseFiles:NO];
- [openPanel setCanCreateDirectories:NO];
- [openPanel setAllowsMultipleSelection: NO];
- [openPanel setCanChooseDirectories:YES];
-
- [openPanel setPrompt:@"Select WebApps directory"];
-
- NSString * filePath = @"~/Desktop";
- filePath = [filePath stringByExpandingTildeInPath];
- NSURL *fileURL = [NSURL fileURLWithPath:filePath];
- [openPanel setDirectoryURL:fileURL];
-
- [openPanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
- if (NSFileHandlingPanelOKButton == result) {
- for (NSURL *URL in [openPanel URLs]) {
- NSString *urlString = [URL path];
- NSLog(@"Chosed: %@", urlString );
-
- APPS_ROOT = [NSString stringWithFormat:@"%@/", urlString ];
-
- // Add to prefs:
- [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:APPS_ROOT forKey:kWebApplicationRoot];
- [self ListBundledDocRoots];
-
- NSString *defaultApp = [P_DocRootPopup itemTitleAtIndex:0];
- [self LoadWebapp:defaultApp];
- }
- }
- }];
-
- }
- // -------------------------------------------------------------------------------
- // K
- - (void)poll {
- // since some of the vars aren't set up for KVO (or change far too often), poll them instead
- NSDate *date = [NSDate date];
- NSTimeInterval t = -[_lastPollDate timeIntervalSinceDate:date];
- if(t > 0.5f) {
-
- //[_lastPollDate release];
- //_lastPollDate = [date retain];
- _lastPollDate = date;
-
- int viewCount = [mainglview frameCount];
- [self setViewFps:MAX(viewCount-_viewLastCount, 0)/t];
- _viewLastCount = viewCount;
- }
-
- if([_tracker enabled]) {
- Vector3 pos = [_tracker screenPosition];
- [self setTrackPosition:[NSString stringWithFormat:@"{%5.3f, %5.3f,%-6.3f}", pos.x, pos.y, pos.z]];
- }
- }
- - (IBAction)trackerReset:(id)sender {
- [_tracker reset];
- }
- - (IBAction)trackerDefaults:(id)sender {
- // reset defaults back to the initial registerDefaults
- forTrackerKeys(^(NSString *key, NSString *prefKey) {
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefKey];
- });
- }
- #pragma mark ensure preferences interacts nicely with fullscreen and lack of cursors
- - (IBAction)openPrefs:(id)sender {
- // since cursor is hidden during fullscreen, don't show prefs
- BOOL fullscreen = ([[mainglview window] styleMask]&NSFullScreenWindowMask)==NSFullScreenWindowMask;
- if(!fullscreen) {
- [[previewglview window] makeKeyAndOrderFront:nil];
- } else {
- NSBeep();
- }
- }
- #pragma mark glview control
- - (NSObject<GLViewScene>*)glViewScene:(GLView*)view {
- if(view == mainglview) {
- if(!_mainScene) {
- _mainScene = [[MainScene alloc] initWithKinectManager:_kinect tracker:_tracker];
- id defs = [NSUserDefaultsController sharedUserDefaultsController];
- [_mainScene bind:@"mode" toObject:defs withKeyPath:[@"values." stringByAppendingString:kModePrefKey] options:nil];
- [_mainScene bind:@"normals" toObject:defs withKeyPath:[@"values." stringByAppendingString:kNormalsPrefKey] options:nil];
- [_mainScene bind:@"mirror" toObject:defs withKeyPath:[@"values." stringByAppendingString:kMirrorPrefKey] options:nil];
- }
- return _mainScene;
- }
- if(view == previewglview) {
- if(!_previewScene && _mainScene && [previewglview isKindOfClass:[GLView class]]) { // iskindOf to ensure view has been replaced
- _previewScene = [(MainScene*)_mainScene createPreviewScene];
- }
- return _previewScene;
- }
- return nil;
- }
- @end