PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/io/AppDelegate.m

https://gitlab.com/base.io/io
Objective C | 545 lines | 315 code | 148 blank | 82 comment | 32 complexity | 556091471f9698467828eb25dbed9fe0 MD5 | raw file
  1. //
  2. // AppDelegate.m
  3. // io
  4. //
  5. // Created by Jørgen Skogmo on 21-05-2012.
  6. // Copyright (c) 2012 base.io. All rights reserved.
  7. //
  8. // 'stringyfication' macros to access preprocessor macros by name.
  9. // useage: NSLog(@"%s", MACRO_VALUE(SRC_ROOT) );
  10. // see: http://stackoverflow.com/questions/242817/xcode-preprocessor-macros and
  11. // http://stackoverflow.com/questions/3261763/accessing-the-value-of-a-preprocessor-macro-definition?rq=1
  12. //TODO: Make WebKit images too: http://cocoadevblog.heroku.com/webkit-screenshots-cocoa-objective-c
  13. #define MACRO_NAME(f) #f
  14. #define MACRO_VALUE(f) MACRO_NAME(f)
  15. #import "AppDelegate.h"
  16. // K
  17. static NSString *const kKinectEnablePrefKey = @"kinectEnabled"; // BOOL
  18. static NSString *const kLedPrefKey = @"led"; // 0..6
  19. static NSString *const kTiltPrefKey = @"tilt"; // -30..30
  20. static NSString *const kTrackCenterPrefKey = @"trackCenter"; // BOOL
  21. static NSString *const kMirrorPrefKey = @"mirror"; // BOOL
  22. static NSString *const kAlignPrefKey = @"align"; // BOOL
  23. static NSString *const kNormalsPrefKey = @"normals"; // BOOL
  24. static NSString *const kModePrefKey = @"mode"; // MainScene.drawMode
  25. static NSString *const kPrefsVisiblePrefsKey = @"prefsVisible"; // BOOL
  26. // IO
  27. static NSString *const kWebApplicationRoot = @"WebApplicationRoot"; // String
  28. static NSString *const kSelectedWebApp = @"SelectedWebApp"; // String
  29. // these were really only intended as a convenience whilst testing...
  30. static void forTrackerKeys(void(^block)(NSString *key, NSString *prefKey)) {
  31. NSString *prefix = @"track";
  32. NSString *keys[] = {@"farZ", @"passCount", @"drawDebug", @"cullBackgroundPortion", @"cylinderRadius", @"handBodyRatio"};
  33. for(int i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) {
  34. // append prefix and capitalise first letter of key
  35. NSString *prefKey = [NSString stringWithFormat:@"%@%@%@", prefix, [[keys[i] substringToIndex:1] uppercaseString], [keys[i] substringFromIndex:1]];
  36. block(keys[i], prefKey);
  37. }
  38. }
  39. @implementation AppDelegate
  40. @synthesize window = _window;
  41. @synthesize webView;
  42. @synthesize recorder;
  43. @synthesize recorderMenu;
  44. @synthesize startStopButton;
  45. @synthesize pauseUnpauseButton;
  46. @synthesize menuSeparator;
  47. @synthesize prefsPanel = _prefsPanel;
  48. @synthesize appVersionString;
  49. // K
  50. @synthesize viewFps = _viewFps;
  51. @synthesize status = _status;
  52. @synthesize depthFps = _depthFps;
  53. @synthesize videoFps = _videoFps;
  54. @synthesize trackPosition = _trackPosition;
  55. id webInspector;
  56. JavaScriptIntegration *JSInt;
  57. - (void)awakeFromNib {
  58. // Configure Kinect
  59. id defs = [NSUserDefaultsController sharedUserDefaultsController];
  60. _kinect = [[KinectManager alloc] init];
  61. [_kinect bind:@"enabled" toObject:defs withKeyPath:[@"values." stringByAppendingString:kKinectEnablePrefKey] options:nil];
  62. [_kinect bind:@"led" toObject:defs withKeyPath:[@"values." stringByAppendingString:kLedPrefKey] options:nil];
  63. [_kinect bind:@"tilt" toObject:defs withKeyPath:[@"values." stringByAppendingString:kTiltPrefKey] options:nil];
  64. [_kinect bind:@"align" toObject:defs withKeyPath:[@"values." stringByAppendingString:kAlignPrefKey] options:nil];
  65. // for convience update local ivars to reflect those from the kinect so can KVO in UI
  66. [self bind:@"videoFps" toObject:_kinect withKeyPath:@"videoFps" options:nil];
  67. [self bind:@"depthFps" toObject:_kinect withKeyPath:@"depthFps" options:nil];
  68. [self bind:@"status" toObject:_kinect withKeyPath:@"status" options:nil];
  69. _tracker = [[Tracker alloc] init];
  70. [_tracker bind:@"enabled" toObject:defs withKeyPath:[@"values." stringByAppendingString:kTrackCenterPrefKey] options:nil];
  71. // bind tracker to defaults, and ensure that initial values are sensible
  72. NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  73. forTrackerKeys(^(NSString *key, NSString *prefKey) {
  74. [dict setObject:[_tracker valueForKey:key] forKey:prefKey];
  75. });
  76. [[defs defaults] registerDefaults:dict];
  77. forTrackerKeys(^(NSString *key, NSString *prefKey) {
  78. [_tracker bind:key toObject:defs withKeyPath:[@"values." stringByAppendingString:prefKey] options:nil];
  79. });
  80. _lastPollDate = [NSDate date];
  81. NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(poll) userInfo:nil repeats:YES];
  82. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSEventTrackingRunLoopMode];
  83. [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSModalPanelRunLoopMode];
  84. //
  85. [(CustomWindow *)[[NSApp delegate] window] BuildAvailScreensPopup];
  86. }
  87. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  88. BLOCK_TEXTSELECTION = YES;
  89. UDP_HOST = @"127.0.0.1";
  90. UDP_PORT = 3333;
  91. //[ImageUtils test];
  92. NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
  93. appVersionString = [NSString stringWithFormat:@"Version: %@", [infoDict objectForKey:@"CFBundleVersion"] ];
  94. NSLog(@"appVersionString: %@", appVersionString);
  95. [appVersionField setStringValue:appVersionString];
  96. // When 10.8 is released, we can not change contents of our AppBundle (where the .webapps is currently stored) without re-signing the app
  97. // Anyway, expecting the user to develop/tweak his own apps working out of the AppBundle is no-good.
  98. // 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).
  99. // Default to AppBundle/Resources/Apps/, so customer-installations will run straight away.
  100. // if no WebApplicationRoot is present in Prefs, use default location
  101. id pAppRoot = [[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey:kWebApplicationRoot];
  102. //pAppRoot = nil; // TEST
  103. if( pAppRoot ){
  104. NSLog(@"HAS webroot: %@", pAppRoot);
  105. APPS_ROOT = [NSString stringWithFormat:@"%@", pAppRoot];
  106. }else{
  107. NSString *defRoot = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/apps/"];
  108. NSLog(@"HAS NO webroot, setting default to: %@", defRoot);
  109. [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:defRoot forKey:kWebApplicationRoot];
  110. APPS_ROOT = [NSString stringWithFormat:@"%@", defRoot];
  111. }
  112. // See NSUserDefaultsController docs here https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaBindings/Concepts/NSUserDefaultsController.html
  113. /*
  114. // During dev, load all apps from Source Directory instead of the app bundle
  115. #ifdef SRC_ROOT
  116. NSLog(@"1 src->%s", MACRO_VALUE(SRC_ROOT));
  117. APPS_ROOT = [NSString stringWithFormat:@"%s", MACRO_VALUE(SRC_ROOT) ];
  118. #else
  119. APPS_ROOT = [[NSBundle mainBundle] resourcePath];
  120. #endif
  121. NSLog(@"Raw APPS_ROOT -> %@", APPS_ROOT);
  122. APPS_ROOT = [APPS_ROOT stringByAppendingString:@"/apps/"];
  123. NSLog(@"AppDelegate: APPS_ROOT -> %@", APPS_ROOT);
  124. */
  125. [self ListBundledDocRoots];
  126. bShowCursor = NO;
  127. // Configure Screen Recorder
  128. self.recorder = [[MCScreenRecorder alloc] init];
  129. [recorderMenu setAutoenablesItems:NO];
  130. // K
  131. // because of the need to share the context between the two glviews it isn't easily possibly to do this in the NIB
  132. // instead we replace a placeholder view with a glview once all is ready
  133. previewglview = [[GLView alloc] initByReplacingView:previewglview sharedContext:mainglview];
  134. /*
  135. if([[NSUserDefaults standardUserDefaults] boolForKey:kPrefsVisiblePrefsKey]) {
  136. [[previewglview window] makeKeyAndOrderFront:nil];
  137. }
  138. */
  139. // Initialize JavaScript integration
  140. JSInt = [[JavaScriptIntegration alloc] init];
  141. [JSInt initWithWebView:webView andWindow:mainWindow];
  142. // Init Communications Socket
  143. [SharedSockets UDPSend:@"SharedUDPSocket ready message"];
  144. // Start HTTP Server. Note: I have modified HTTPConnection.m with improved MIME-types
  145. httpServer = [[HTTPServer alloc] init];
  146. [httpServer setPort:HTTP_PORT];
  147. // Start 'Private' HTTP Server (to load shared/special assets)
  148. httpServerPrivate = [[HTTPServer alloc] init];
  149. [httpServerPrivate setPort:HTTP_PORT_PRIVATE];
  150. [httpServerPrivate setDocumentRoot:APPS_ROOT];
  151. NSError *error = nil;
  152. if([httpServerPrivate start:&error]){
  153. NSLog(@"httpServerPrivate: Started with document root /Users/js/Dropbox/base.io/projects/io/apps/");
  154. }else{
  155. NSLog(@"httpServerPrivate: Error: %@", error);
  156. }
  157. // Load default application
  158. NSString *app = @"api";
  159. id pSelApp = [[[NSUserDefaultsController sharedUserDefaultsController] values] valueForKey:kSelectedWebApp];
  160. NSLog(@"Prefs for pSelApp: %@", pSelApp);
  161. if( pSelApp ){
  162. app = [NSString stringWithFormat:@"%@", pSelApp];
  163. }
  164. [self LoadWebapp:app];
  165. [(CustomWindow *)[[NSApp delegate] window] SetWidth:1280 Height:720];
  166. }
  167. - (void)applicationWillTerminate:(NSNotification *)notification {
  168. // remember if prefs window was open
  169. [[NSUserDefaults standardUserDefaults] setBool:[[previewglview window] isVisible] forKey:kPrefsVisiblePrefsKey];
  170. [mainglview setPaused:YES];
  171. //[previewglview setPaused:YES];
  172. [_kinect setEnabled:NO]; // ensure it stops
  173. }
  174. #pragma mark UICode
  175. - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
  176. return YES;
  177. }
  178. BOOL bShowCursor;
  179. - (IBAction)toggleMouse:(id)sender {
  180. bShowCursor = !bShowCursor;
  181. NSLog(@"toggleMouse: %@", (bShowCursor ? @"TRUE" : @"FALSE") );
  182. if( bShowCursor ){
  183. [NSCursor hide];
  184. [sender setState:1];
  185. }else{
  186. [NSCursor unhide];
  187. [sender setState:0];
  188. }
  189. }
  190. BOOL bWebInspectorOpen;
  191. - (IBAction) toggleWebInspector:(id *)sender {
  192. if( bWebInspectorOpen ){
  193. bWebInspectorOpen = NO;
  194. [webInspector close:webView];
  195. }else{
  196. bWebInspectorOpen = YES;
  197. [webInspector detach:webView];
  198. [webInspector showConsole:webView];
  199. }
  200. }
  201. - (IBAction)showPrefsPanel:(id)sender {
  202. if( ![self.prefsPanel isVisible] ){
  203. NSLog(@"open PrefsPanel");
  204. //[self.prefsPanel delegate]
  205. [self.prefsPanel makeKeyAndOrderFront:sender];
  206. }else{
  207. NSLog(@"close PrefsPanel");
  208. //[self.prefsPanel close];
  209. [self.prefsPanel orderOut:nil];
  210. }
  211. }
  212. - (BOOL)windowShouldClose:(id)sender{
  213. NSLog(@"PREFS windowShouldClose");
  214. if( [self.prefsPanel isVisible] ){
  215. NSLog(@"PrefsPanel - ordering out");
  216. [self.prefsPanel orderOut:nil];
  217. }
  218. return NO;
  219. }
  220. -(IBAction)PSetDocRoot:(id)sender{
  221. NSString *appName = [sender title];
  222. NSLog(@"PSetDocRoot to %@", appName);
  223. [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:appName forKey:kSelectedWebApp];
  224. [self LoadWebapp:appName ];
  225. }
  226. -(void)LoadWebapp:(NSString *) appName {
  227. NSLog(@"LoadWebapp '%@'", appName );
  228. [Cache Disable];
  229. WEBAPP_ROOT = [NSString stringWithFormat:@"%@%@.webapp/", APPS_ROOT, appName];
  230. [self RestartHTTPServer];
  231. NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:%i/index.html", HTTP_PORT];
  232. [webView setMainFrameURL:url];
  233. }
  234. -(void)RestartHTTPServer {
  235. [httpServer stop];
  236. [httpServer setDocumentRoot:WEBAPP_ROOT];
  237. [httpServer setPort:HTTP_PORT];
  238. NSError *error = nil;
  239. if([httpServer start:&error]){
  240. NSLog(@"HTTPServer: (Re)started with document root: %@", WEBAPP_ROOT);
  241. }else{
  242. NSLog(@"Error starting HTTP Server: %@", error);
  243. }
  244. }
  245. -(void)ReloadWebView {
  246. //NSString *url = [NSString stringWithFormat:@"http://127.0.0.1:%i/index.html", HTTP_PORT];
  247. //[webView setMainFrameURL:url];
  248. [webView reload:nil];
  249. }
  250. // -------------------------------------------------------------------------------
  251. // Recording
  252. - (IBAction)startStopRecording:(id)sender {
  253. if (self.recorder.session.running) {
  254. [self.recorder stop];
  255. // Update UI
  256. [startStopButton setTitle:@"Start"];
  257. [pauseUnpauseButton setEnabled:NO];
  258. } else {
  259. float wx = self.window.frame.origin.x;
  260. float wy = self.window.frame.origin.y;
  261. float ww = self.window.frame.size.width;
  262. float wh = self.window.frame.size.height;
  263. NSLog(@"starting recording for Rect(%f, %f, %f, %f)", wx, wy, ww, wh);
  264. self.recorder.input.cropRect = CGRectMake(wx, wy, ww, wh);
  265. [self.recorder start];
  266. // Update UI
  267. [startStopButton setTitle:@"Stop"];
  268. [pauseUnpauseButton setEnabled:YES];
  269. [menuSeparator setHidden:NO];
  270. // Add a menu item for the new recording
  271. [recorderMenu addItem:[[MCScreenRecordingMenuItem alloc] initWithFile:self.recorder.file]];
  272. }
  273. // We always want to reset the title
  274. [pauseUnpauseButton setTitle:@"Pause"];
  275. }
  276. - (IBAction)pauseUnpauseRecording:(id)sender {
  277. if ([self.recorder toggle])
  278. [pauseUnpauseButton setTitle:@"Pause"];
  279. else
  280. [pauseUnpauseButton setTitle:@"Resume"];
  281. }
  282. // -------------------------------------------------------------------------------
  283. // Preferences
  284. // Document Root
  285. NSString *PDocumentRoot;
  286. -(void)ListBundledDocRoots {
  287. // All folders matching '$APPS_ROOT/apps/*.webapp/index.html'
  288. NSLog(@"ListBundledDocRoots LISTING: %@", APPS_ROOT);
  289. NSMutableArray *roots = [[NSMutableArray alloc] init];//WithCapacity:10];
  290. NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:APPS_ROOT error:NULL];
  291. for (NSString *f in files) {
  292. if( [f hasSuffix:@".webapp"] ){
  293. NSString *fullPath = [APPS_ROOT stringByAppendingPathComponent:f];
  294. BOOL isDir;
  295. if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir] &&isDir) {
  296. for (NSString *sf in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:fullPath error:NULL] ) {
  297. if( [sf isEqualToString:@"index.html"] ){
  298. NSString *folderName = [fullPath lastPathComponent];
  299. NSArray *fp = [folderName componentsSeparatedByString:@".webapp"];
  300. [roots addObject: [fp objectAtIndex:0] ];
  301. }
  302. }
  303. }
  304. }
  305. }
  306. NSLog(@"ListBundledDocRoots: %@", roots );
  307. [P_DocRootPopup removeAllItems];
  308. [P_DocRootPopup addItemsWithTitles: roots];
  309. }
  310. // -------------------------------------------------------------------------------
  311. // APPS_ROOT
  312. -(IBAction)ShowSelectAppsRootDialog:(id)sender {
  313. NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  314. [openPanel setCanChooseFiles:NO];
  315. [openPanel setCanCreateDirectories:NO];
  316. [openPanel setAllowsMultipleSelection: NO];
  317. [openPanel setCanChooseDirectories:YES];
  318. [openPanel setPrompt:@"Select WebApps directory"];
  319. NSString * filePath = @"~/Desktop";
  320. filePath = [filePath stringByExpandingTildeInPath];
  321. NSURL *fileURL = [NSURL fileURLWithPath:filePath];
  322. [openPanel setDirectoryURL:fileURL];
  323. [openPanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
  324. if (NSFileHandlingPanelOKButton == result) {
  325. for (NSURL *URL in [openPanel URLs]) {
  326. NSString *urlString = [URL path];
  327. NSLog(@"Chosed: %@", urlString );
  328. APPS_ROOT = [NSString stringWithFormat:@"%@/", urlString ];
  329. // Add to prefs:
  330. [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:APPS_ROOT forKey:kWebApplicationRoot];
  331. [self ListBundledDocRoots];
  332. NSString *defaultApp = [P_DocRootPopup itemTitleAtIndex:0];
  333. [self LoadWebapp:defaultApp];
  334. }
  335. }
  336. }];
  337. }
  338. // -------------------------------------------------------------------------------
  339. // K
  340. - (void)poll {
  341. // since some of the vars aren't set up for KVO (or change far too often), poll them instead
  342. NSDate *date = [NSDate date];
  343. NSTimeInterval t = -[_lastPollDate timeIntervalSinceDate:date];
  344. if(t > 0.5f) {
  345. //[_lastPollDate release];
  346. //_lastPollDate = [date retain];
  347. _lastPollDate = date;
  348. int viewCount = [mainglview frameCount];
  349. [self setViewFps:MAX(viewCount-_viewLastCount, 0)/t];
  350. _viewLastCount = viewCount;
  351. }
  352. if([_tracker enabled]) {
  353. Vector3 pos = [_tracker screenPosition];
  354. [self setTrackPosition:[NSString stringWithFormat:@"{%5.3f, %5.3f,%-6.3f}", pos.x, pos.y, pos.z]];
  355. }
  356. }
  357. - (IBAction)trackerReset:(id)sender {
  358. [_tracker reset];
  359. }
  360. - (IBAction)trackerDefaults:(id)sender {
  361. // reset defaults back to the initial registerDefaults
  362. forTrackerKeys(^(NSString *key, NSString *prefKey) {
  363. [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefKey];
  364. });
  365. }
  366. #pragma mark ensure preferences interacts nicely with fullscreen and lack of cursors
  367. - (IBAction)openPrefs:(id)sender {
  368. // since cursor is hidden during fullscreen, don't show prefs
  369. BOOL fullscreen = ([[mainglview window] styleMask]&NSFullScreenWindowMask)==NSFullScreenWindowMask;
  370. if(!fullscreen) {
  371. [[previewglview window] makeKeyAndOrderFront:nil];
  372. } else {
  373. NSBeep();
  374. }
  375. }
  376. #pragma mark glview control
  377. - (NSObject<GLViewScene>*)glViewScene:(GLView*)view {
  378. if(view == mainglview) {
  379. if(!_mainScene) {
  380. _mainScene = [[MainScene alloc] initWithKinectManager:_kinect tracker:_tracker];
  381. id defs = [NSUserDefaultsController sharedUserDefaultsController];
  382. [_mainScene bind:@"mode" toObject:defs withKeyPath:[@"values." stringByAppendingString:kModePrefKey] options:nil];
  383. [_mainScene bind:@"normals" toObject:defs withKeyPath:[@"values." stringByAppendingString:kNormalsPrefKey] options:nil];
  384. [_mainScene bind:@"mirror" toObject:defs withKeyPath:[@"values." stringByAppendingString:kMirrorPrefKey] options:nil];
  385. }
  386. return _mainScene;
  387. }
  388. if(view == previewglview) {
  389. if(!_previewScene && _mainScene && [previewglview isKindOfClass:[GLView class]]) { // iskindOf to ensure view has been replaced
  390. _previewScene = [(MainScene*)_mainScene createPreviewScene];
  391. }
  392. return _previewScene;
  393. }
  394. return nil;
  395. }
  396. @end