1/*************************************************************************/ 2/* app_delegate.mm */ 3/*************************************************************************/ 4/* This file is part of: */ 5/* GODOT ENGINE */ 6/* https://godotengine.org */ 7/*************************************************************************/ 8/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ 9/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ 10/* */ 11/* Permission is hereby granted, free of charge, to any person obtaining */ 12/* a copy of this software and associated documentation files (the */ 13/* "Software"), to deal in the Software without restriction, including */ 14/* without limitation the rights to use, copy, modify, merge, publish, */ 15/* distribute, sublicense, and/or sell copies of the Software, and to */ 16/* permit persons to whom the Software is furnished to do so, subject to */ 17/* the following conditions: */ 18/* */ 19/* The above copyright notice and this permission notice shall be */ 20/* included in all copies or substantial portions of the Software. */ 21/* */ 22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ 23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ 24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ 25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ 26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ 27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ 28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 29/*************************************************************************/ 30#import "app_delegate.h" 31 32#include "audio_driver_iphone.h" 33#include "core/globals.h" 34#import "gl_view.h" 35#include "main/main.h" 36#include "os_iphone.h" 37 38#import "GameController/GameController.h" 39 40#define kFilteringFactor 0.1 41#define kRenderingFrequency 60 42#define kAccelerometerFrequency 100.0 // Hz 43 44Error _shell_open(String); 45void _set_keep_screen_on(bool p_enabled); 46 47Error _shell_open(String p_uri) { 48 NSString *url = [[NSString alloc] initWithUTF8String:p_uri.utf8().get_data()]; 49 50 if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]]) 51 return ERR_CANT_OPEN; 52 53 printf("opening url %ls\n", p_uri.c_str()); 54 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; 55 [url release]; 56 return OK; 57}; 58 59void _set_keep_screen_on(bool p_enabled) { 60 [[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled]; 61}; 62 63@implementation AppDelegate 64 65@synthesize window; 66 67extern int gargc; 68extern char **gargv; 69extern int iphone_main(int, int, int, char **); 70extern void iphone_finish(); 71 72CMMotionManager *motionManager; 73bool motionInitialised; 74 75static ViewController *mainViewController = nil; 76+ (ViewController *)getViewController { 77 return mainViewController; 78} 79 80NSMutableDictionary *ios_joysticks = nil; 81 82- (GCControllerPlayerIndex)getFreePlayerIndex { 83 bool have_player_1 = false; 84 bool have_player_2 = false; 85 bool have_player_3 = false; 86 bool have_player_4 = false; 87 88 if (ios_joysticks == nil) { 89 NSArray *keys = [ios_joysticks allKeys]; 90 for (NSNumber *key in keys) { 91 GCController *controller = [ios_joysticks objectForKey:key]; 92 if (controller.playerIndex == GCControllerPlayerIndex1) { 93 have_player_1 = true; 94 } else if (controller.playerIndex == GCControllerPlayerIndex2) { 95 have_player_2 = true; 96 } else if (controller.playerIndex == GCControllerPlayerIndex3) { 97 have_player_3 = true; 98 } else if (controller.playerIndex == GCControllerPlayerIndex4) { 99 have_player_4 = true; 100 }; 101 }; 102 }; 103 104 if (!have_player_1) { 105 return GCControllerPlayerIndex1; 106 } else if (!have_player_2) { 107 return GCControllerPlayerIndex2; 108 } else if (!have_player_3) { 109 return GCControllerPlayerIndex3; 110 } else if (!have_player_4) { 111 return GCControllerPlayerIndex4; 112 } else { 113 return GCControllerPlayerIndexUnset; 114 }; 115}; 116 117- (void)controllerWasConnected:(NSNotification *)notification { 118 // create our dictionary if we don't have one yet 119 if (ios_joysticks == nil) { 120 ios_joysticks = [[NSMutableDictionary alloc] init]; 121 }; 122 123 // get our controller 124 GCController *controller = (GCController *)notification.object; 125 if (controller == nil) { 126 printf("Couldn't retrieve new controller\n"); 127 } else if ([[ios_joysticks allKeysForObject:controller] count] != 0) { 128 printf("Controller is already registered\n"); 129 } else { 130 // get a new id for our controller 131 int joy_id = OSIPhone::get_singleton()->get_unused_joy_id(); 132 if (joy_id != -1) { 133 // assign our player index 134 if (controller.playerIndex == GCControllerPlayerIndexUnset) { 135 controller.playerIndex = [self getFreePlayerIndex]; 136 }; 137 138 // tell Godot about our new controller 139 OSIPhone::get_singleton()->joy_connection_changed( 140 joy_id, true, [controller.vendorName UTF8String]); 141 142 // add it to our dictionary, this will retain our controllers 143 [ios_joysticks setObject:controller 144 forKey:[NSNumber numberWithInt:joy_id]]; 145 146 // set our input handler 147 [self setControllerInputHandler:controller]; 148 } else { 149 printf("Couldn't retrieve new joy id\n"); 150 }; 151 }; 152}; 153 154- (void)controllerWasDisconnected:(NSNotification *)notification { 155 if (ios_joysticks != nil) { 156 // find our joystick, there should be only one in our dictionary 157 GCController *controller = (GCController *)notification.object; 158 NSArray *keys = [ios_joysticks allKeysForObject:controller]; 159 for (NSNumber *key in keys) { 160 // tell Godot this joystick is no longer there 161 int joy_id = [key intValue]; 162 OSIPhone::get_singleton()->joy_connection_changed(joy_id, false, ""); 163 164 // and remove it from our dictionary 165 [ios_joysticks removeObjectForKey:key]; 166 }; 167 }; 168}; 169 170- (int)getJoyIdForController:(GCController *)controller { 171 if (ios_joysticks != nil) { 172 // find our joystick, there should be only one in our dictionary 173 NSArray *keys = [ios_joysticks allKeysForObject:controller]; 174 for (NSNumber *key in keys) { 175 int joy_id = [key intValue]; 176 return joy_id; 177 }; 178 }; 179 180 return -1; 181}; 182 183- (void)setControllerInputHandler:(GCController *)controller { 184 // Hook in the callback handler for the correct gamepad profile. 185 // This is a bit of a weird design choice on Apples part. 186 // You need to select the most capable gamepad profile for the 187 // gamepad attached. 188 if (controller.extendedGamepad != nil) { 189 // The extended gamepad profile has all the input you could possibly find on 190 // a gamepad but will only be active if your gamepad actually has all of 191 // these... 192 controller.extendedGamepad.valueChangedHandler = ^( 193 GCExtendedGamepad *gamepad, GCControllerElement *element) { 194 int joy_id = [self getJoyIdForController:controller]; 195 196 if (element == gamepad.buttonA) { 197 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0, 198 gamepad.buttonA.isPressed); 199 } else if (element == gamepad.buttonB) { 200 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_1, 201 gamepad.buttonB.isPressed); 202 } else if (element == gamepad.buttonX) { 203 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_2, 204 gamepad.buttonX.isPressed); 205 } else if (element == gamepad.buttonY) { 206 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_3, 207 gamepad.buttonY.isPressed); 208 } else if (element == gamepad.leftShoulder) { 209 OSIPhone::get_singleton()->joy_button(joy_id, JOY_L, 210 gamepad.leftShoulder.isPressed); 211 } else if (element == gamepad.rightShoulder) { 212 OSIPhone::get_singleton()->joy_button(joy_id, JOY_R, 213 gamepad.rightShoulder.isPressed); 214 } else if (element == gamepad.leftTrigger) { 215 OSIPhone::get_singleton()->joy_button(joy_id, JOY_L2, 216 gamepad.leftTrigger.isPressed); 217 } else if (element == gamepad.rightTrigger) { 218 OSIPhone::get_singleton()->joy_button(joy_id, JOY_R2, 219 gamepad.rightTrigger.isPressed); 220 } else if (element == gamepad.dpad) { 221 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_UP, 222 gamepad.dpad.up.isPressed); 223 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN, 224 gamepad.dpad.down.isPressed); 225 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT, 226 gamepad.dpad.left.isPressed); 227 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT, 228 gamepad.dpad.right.isPressed); 229 }; 230 231 InputDefault::JoyAxis jx; 232 jx.min = -1; 233 if (element == gamepad.leftThumbstick) { 234 jx.value = gamepad.leftThumbstick.xAxis.value; 235 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_0_X, jx); 236 jx.value = -gamepad.leftThumbstick.yAxis.value; 237 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_0_Y, jx); 238 } else if (element == gamepad.rightThumbstick) { 239 jx.value = gamepad.rightThumbstick.xAxis.value; 240 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_1_X, jx); 241 jx.value = -gamepad.rightThumbstick.yAxis.value; 242 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_1_Y, jx); 243 } else if (element == gamepad.leftTrigger) { 244 jx.value = gamepad.leftTrigger.value; 245 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_L2, jx); 246 } else if (element == gamepad.rightTrigger) { 247 jx.value = gamepad.rightTrigger.value; 248 OSIPhone::get_singleton()->joy_axis(joy_id, JOY_ANALOG_R2, jx); 249 }; 250 }; 251 } else if (controller.gamepad != nil) { 252 // gamepad is the standard profile with 4 buttons, shoulder buttons and a 253 // D-pad 254 controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad, 255 GCControllerElement *element) { 256 int joy_id = [self getJoyIdForController:controller]; 257 258 if (element == gamepad.buttonA) { 259 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0, 260 gamepad.buttonA.isPressed); 261 } else if (element == gamepad.buttonB) { 262 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_1, 263 gamepad.buttonB.isPressed); 264 } else if (element == gamepad.buttonX) { 265 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_2, 266 gamepad.buttonX.isPressed); 267 } else if (element == gamepad.buttonY) { 268 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_3, 269 gamepad.buttonY.isPressed); 270 } else if (element == gamepad.leftShoulder) { 271 OSIPhone::get_singleton()->joy_button(joy_id, JOY_L, 272 gamepad.leftShoulder.isPressed); 273 } else if (element == gamepad.rightShoulder) { 274 OSIPhone::get_singleton()->joy_button(joy_id, JOY_R, 275 gamepad.rightShoulder.isPressed); 276 } else if (element == gamepad.dpad) { 277 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_UP, 278 gamepad.dpad.up.isPressed); 279 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN, 280 gamepad.dpad.down.isPressed); 281 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT, 282 gamepad.dpad.left.isPressed); 283 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT, 284 gamepad.dpad.right.isPressed); 285 }; 286 }; 287#ifdef ADD_MICRO_GAMEPAD // disabling this for now, only available on iOS 9+ and we're still compiling for iOS 7+ 288 } else if (controller.microGamepad != nil) { 289 // micro gamepads were added in OS 9 and feature just 2 buttons and a d-pad 290 controller.microGamepad.valueChangedHandler = ^(GCMicroGamepad *gamepad, GCControllerElement *element) { 291 int joy_id = [self getJoyIdForController:controller]; 292 293 if (element == gamepad.buttonA) { 294 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_0, 295 gamepad.buttonA.isPressed); 296 } else if (element == gamepad.buttonX) { 297 OSIPhone::get_singleton()->joy_button(joy_id, JOY_BUTTON_2, 298 gamepad.buttonX.isPressed); 299 } else if (element == gamepad.dpad) { 300 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_UP, 301 gamepad.dpad.up.isPressed); 302 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_DOWN, 303 gamepad.dpad.down.isPressed); 304 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_LEFT, 305 gamepad.dpad.left.isPressed); 306 OSIPhone::get_singleton()->joy_button(joy_id, JOY_DPAD_RIGHT, 307 gamepad.dpad.right.isPressed); 308 }; 309 }; 310#endif 311 }; 312 313 ///@TODO need to add support for controller.motion which gives us access to 314 /// the orientation of the device (if supported) 315 316 ///@TODO need to add support for controllerPausedHandler which should be a 317 /// toggle 318}; 319 320- (void)initGameControllers { 321 // get told when controllers connect, this will be called right away for 322 // already connected controllers 323 [[NSNotificationCenter defaultCenter] 324 addObserver:self 325 selector:@selector(controllerWasConnected:) 326 name:GCControllerDidConnectNotification 327 object:nil]; 328 329 // get told when controllers disconnect 330 [[NSNotificationCenter defaultCenter] 331 addObserver:self 332 selector:@selector(controllerWasDisconnected:) 333 name:GCControllerDidDisconnectNotification 334 object:nil]; 335}; 336 337- (void)deinitGameControllers { 338 [[NSNotificationCenter defaultCenter] 339 removeObserver:self 340 name:GCControllerDidConnectNotification 341 object:nil]; 342 [[NSNotificationCenter defaultCenter] 343 removeObserver:self 344 name:GCControllerDidDisconnectNotification 345 object:nil]; 346 347 if (ios_joysticks != nil) { 348 [ios_joysticks dealloc]; 349 ios_joysticks = nil; 350 }; 351}; 352 353static int frame_count = 0; 354- (void)drawView:(GLView *)view; 355{ 356 357 switch (frame_count) { 358 case 0: { 359 int backingWidth; 360 int backingHeight; 361 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 362 GL_RENDERBUFFER_WIDTH_OES, &backingWidth); 363 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 364 GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); 365 366 OS::VideoMode vm; 367 vm.fullscreen = true; 368 vm.width = backingWidth; 369 vm.height = backingHeight; 370 vm.resizable = false; 371 OS::get_singleton()->set_video_mode(vm); 372 373 if (!OS::get_singleton()) { 374 exit(0); 375 }; 376 ++frame_count; 377 378 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 379 NSUserDomainMask, YES); 380 NSString *documentsDirectory = [paths objectAtIndex:0]; 381 // NSString *documentsDirectory = [[[NSFileManager defaultManager] 382 // URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] 383 // lastObject]; 384 OSIPhone::get_singleton()->set_data_dir( 385 String::utf8([documentsDirectory UTF8String])); 386 387 NSString *locale_code = [[NSLocale currentLocale] localeIdentifier]; 388 OSIPhone::get_singleton()->set_locale( 389 String::utf8([locale_code UTF8String])); 390 391 NSString *uuid; 392 if ([[UIDevice currentDevice] 393 respondsToSelector:@selector(identifierForVendor)]) { 394 uuid = [UIDevice currentDevice].identifierForVendor.UUIDString; 395 } else { 396 397 // before iOS 6, so just generate an identifier and store it 398 uuid = [[NSUserDefaults standardUserDefaults] 399 objectForKey:@"identiferForVendor"]; 400 if (!uuid) { 401 CFUUIDRef cfuuid = CFUUIDCreate(NULL); 402 uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, cfuuid); 403 CFRelease(cfuuid); 404 [[NSUserDefaults standardUserDefaults] 405 setObject:uuid 406 forKey:@"identifierForVendor"]; 407 } 408 } 409 410 OSIPhone::get_singleton()->set_unique_ID(String::utf8([uuid UTF8String])); 411 412 }; break; 413 414 case 1: { 415 416 Main::setup2(); 417 ++frame_count; 418 419 // this might be necessary before here 420 NSDictionary *dict = [[NSBundle mainBundle] infoDictionary]; 421 for (NSString *key in dict) { 422 NSObject *value = [dict objectForKey:key]; 423 String ukey = String::utf8([key UTF8String]); 424 425 // we need a NSObject to Variant conversor 426 427 if ([value isKindOfClass:[NSString class]]) { 428 NSString *str = (NSString *)value; 429 String uval = String::utf8([str UTF8String]); 430 431 Globals::get_singleton()->set("Info.plist/" + ukey, uval); 432 433 } else if ([value isKindOfClass:[NSNumber class]]) { 434 435 NSNumber *n = (NSNumber *)value; 436 double dval = [n doubleValue]; 437 438 Globals::get_singleton()->set("Info.plist/" + ukey, dval); 439 }; 440 // do stuff 441 } 442 443 }; break; 444 445 case 2: { 446 447 Main::start(); 448 ++frame_count; 449 450 }; break; // no fallthrough 451 452 default: { 453 if (OSIPhone::get_singleton()) { 454 if (motionInitialised) { 455 // Just using polling approach for now, we can set this up so it sends 456 // data to us in intervals, might be better. See Apple reference pages 457 // for more details: 458 // https://developer.apple.com/reference/coremotion/cmmotionmanager?language=objc 459 460 // Apple splits our accelerometer date into a gravity and user movement 461 // component. We add them back together 462 CMAcceleration gravity = motionManager.deviceMotion.gravity; 463 CMAcceleration acceleration = 464 motionManager.deviceMotion.userAcceleration; 465 466 ///@TODO We don't seem to be getting data here, is my device broken or 467 /// is this code incorrect? 468 CMMagneticField magnetic = 469 motionManager.deviceMotion.magneticField.field; 470 471 ///@TODO we can access rotationRate as a CMRotationRate variable 472 ///(processed date) or CMGyroData (raw data), have to see what works 473 /// best 474 CMRotationRate rotation = motionManager.deviceMotion.rotationRate; 475 476 // Adjust for screen orientation. 477 // [[UIDevice currentDevice] orientation] changes even if we've fixed 478 // our orientation which is not a good thing when you're trying to get 479 // your user to move the screen in all directions and want consistent 480 // output 481 482 ///@TODO Using [[UIApplication sharedApplication] statusBarOrientation] 483 /// is a bit of a hack. Godot obviously knows the orientation so maybe 484 /// we 485 // can use that instead? (note that left and right seem swapped) 486 487 switch ([[UIApplication sharedApplication] statusBarOrientation]) { 488 case UIDeviceOrientationLandscapeLeft: { 489 OSIPhone::get_singleton()->update_gravity(-gravity.y, gravity.x, 490 gravity.z); 491 OSIPhone::get_singleton()->update_accelerometer( 492 -(acceleration.y + gravity.y), (acceleration.x + gravity.x), 493 acceleration.z + gravity.z); 494 OSIPhone::get_singleton()->update_magnetometer( 495 -magnetic.y, magnetic.x, magnetic.z); 496 OSIPhone::get_singleton()->update_gyroscope(-rotation.y, rotation.x, 497 rotation.z); 498 }; break; 499 case UIDeviceOrientationLandscapeRight: { 500 OSIPhone::get_singleton()->update_gravity(gravity.y, -gravity.x, 501 gravity.z); 502 OSIPhone::get_singleton()->update_accelerometer( 503 (acceleration.y + gravity.y), -(acceleration.x + gravity.x), 504 acceleration.z + gravity.z); 505 OSIPhone::get_singleton()->update_magnetometer( 506 magnetic.y, -magnetic.x, magnetic.z); 507 OSIPhone::get_singleton()->update_gyroscope(rotation.y, -rotation.x, 508 rotation.z); 509 }; break; 510 case UIDeviceOrientationPortraitUpsideDown: { 511 OSIPhone::get_singleton()->update_gravity(-gravity.x, gravity.y, 512 gravity.z); 513 OSIPhone::get_singleton()->update_accelerometer( 514 -(acceleration.x + gravity.x), (acceleration.y + gravity.y), 515 acceleration.z + gravity.z); 516 OSIPhone::get_singleton()->update_magnetometer( 517 -magnetic.x, magnetic.y, magnetic.z); 518 OSIPhone::get_singleton()->update_gyroscope(-rotation.x, rotation.y, 519 rotation.z); 520 }; break; 521 default: { // assume portrait 522 OSIPhone::get_singleton()->update_gravity(gravity.x, gravity.y, 523 gravity.z); 524 OSIPhone::get_singleton()->update_accelerometer( 525 acceleration.x + gravity.x, acceleration.y + gravity.y, 526 acceleration.z + gravity.z); 527 OSIPhone::get_singleton()->update_magnetometer(magnetic.x, magnetic.y, 528 magnetic.z); 529 OSIPhone::get_singleton()->update_gyroscope(rotation.x, rotation.y, 530 rotation.z); 531 }; break; 532 }; 533 } 534 535 bool quit_request = OSIPhone::get_singleton()->iterate(); 536 }; 537 }; 538 }; 539}; 540 541- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application { 542 OS::get_singleton()->get_main_loop()->notification( 543 MainLoop::NOTIFICATION_OS_MEMORY_WARNING); 544}; 545 546- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 547 CGRect rect = [[UIScreen mainScreen] bounds]; 548 549 [application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; 550 // disable idle timer 551 // application.idleTimerDisabled = YES; 552 553 // Create a full-screen window 554 window = [[UIWindow alloc] initWithFrame:rect]; 555 // window.autoresizesSubviews = YES; 556 //[window setAutoresizingMask:UIViewAutoresizingFlexibleWidth | 557 // UIViewAutoresizingFlexibleWidth]; 558 559 // Create the OpenGL ES view and add it to the window 560 GLView *glView = [[GLView alloc] initWithFrame:rect]; 561 printf("glview is %p\n", glView); 562 //[window addSubview:glView]; 563 glView.delegate = self; 564 // glView.autoresizesSubviews = YES; 565 //[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | 566 // UIViewAutoresizingFlexibleWidth]; 567 568 int backingWidth; 569 int backingHeight; 570 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 571 GL_RENDERBUFFER_WIDTH_OES, &backingWidth); 572 glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, 573 GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); 574 575 iphone_main(backingWidth, backingHeight, gargc, gargv); 576 577 view_controller = [[ViewController alloc] init]; 578 view_controller.view = glView; 579 window.rootViewController = view_controller; 580 581 _set_keep_screen_on(bool(GLOBAL_DEF("display/keep_screen_on", true)) ? YES : NO); 582 glView.useCADisplayLink = 583 bool(GLOBAL_DEF("display.iOS/use_cadisplaylink", true)) ? YES : NO; 584 printf("cadisaplylink: %d", glView.useCADisplayLink); 585 glView.animationInterval = 1.0 / kRenderingFrequency; 586 [glView startAnimation]; 587 588 // Show the window 589 [window makeKeyAndVisible]; 590 591 if (!motionInitialised) { 592 motionManager = [[CMMotionManager alloc] init]; 593 if (motionManager.deviceMotionAvailable) { 594 motionManager.deviceMotionUpdateInterval = 1.0 / 70.0; 595 [motionManager startDeviceMotionUpdatesUsingReferenceFrame: 596 CMAttitudeReferenceFrameXMagneticNorthZVertical]; 597 motionInitialised = YES; 598 }; 599 }; 600 601 [self initGameControllers]; 602 603 // OSIPhone::screen_width = rect.size.width - rect.origin.x; 604 // OSIPhone::screen_height = rect.size.height - rect.origin.y; 605 606 mainViewController = view_controller; 607 608 // prevent to stop music in another background app 609 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; 610 611 return TRUE; 612}; 613 614- (void)applicationWillTerminate:(UIApplication *)application { 615 [self deinitGameControllers]; 616 617 if (motionInitialised) { 618 ///@TODO is this the right place to clean this up? 619 [motionManager stopDeviceMotionUpdates]; 620 [motionManager release]; 621 motionManager = nil; 622 motionInitialised = NO; 623 }; 624 625 iphone_finish(); 626}; 627 628- (void)applicationDidEnterBackground:(UIApplication *)application { 629 ///@TODO maybe add pause motionManager? and where would we unpause it? 630 631 if (OS::get_singleton()->get_main_loop()) 632 OS::get_singleton()->get_main_loop()->notification( 633 MainLoop::NOTIFICATION_WM_FOCUS_OUT); 634 635 [view_controller.view stopAnimation]; 636 637 if (OS::get_singleton()->native_video_is_playing()) { 638 OSIPhone::get_singleton()->native_video_focus_out(); 639 }; 640} 641 642- (void)applicationWillEnterForeground:(UIApplication *)application { 643 // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); 644 [view_controller.view startAnimation]; 645} 646 647- (void)applicationWillResignActive:(UIApplication *)application { 648 // OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); 649 [view_controller.view 650 stopAnimation]; // FIXME: pause seems to be recommended elsewhere 651} 652 653- (void)applicationDidBecomeActive:(UIApplication *)application { 654 if (OS::get_singleton()->get_main_loop()) 655 OS::get_singleton()->get_main_loop()->notification( 656 MainLoop::NOTIFICATION_WM_FOCUS_IN); 657 658 [view_controller.view 659 startAnimation]; // FIXME: resume seems to be recommended elsewhere 660 661 if (OSIPhone::get_singleton()->native_video_is_playing()) { 662 OSIPhone::get_singleton()->native_video_unpause(); 663 }; 664 665 // Fixed audio can not resume if it is interrupted cause by an incoming phone call 666 if (AudioDriverIphone::get_singleton() != NULL) 667 AudioDriverIphone::get_singleton()->start(); 668} 669 670- (void)dealloc { 671 [window release]; 672 [super dealloc]; 673} 674 675@end 676