| #import "UnityAppController.h" | 
| #import "UnityAppController+ViewHandling.h" | 
| #import "UnityAppController+Rendering.h" | 
| #import "iPhone_Sensors.h" | 
|   | 
| #import <CoreGraphics/CoreGraphics.h> | 
| #import <QuartzCore/QuartzCore.h> | 
| #import <QuartzCore/CADisplayLink.h> | 
| #import <Availability.h> | 
|   | 
| #import <OpenGLES/EAGL.h> | 
| #import <OpenGLES/EAGLDrawable.h> | 
| #import <OpenGLES/ES2/gl.h> | 
| #import <OpenGLES/ES2/glext.h> | 
|   | 
| #include <mach/mach_time.h> | 
|   | 
| // MSAA_DEFAULT_SAMPLE_COUNT was moved to iPhone_GlesSupport.h | 
| // ENABLE_INTERNAL_PROFILER and related defines were moved to iPhone_Profiler.h | 
| // kFPS define for removed: you can use Application.targetFrameRate (30 fps by default) | 
| // DisplayLink is the only run loop mode now - all others were removed | 
|   | 
| #include "CrashReporter.h" | 
|   | 
| #include "UI/OrientationSupport.h" | 
| #include "UI/UnityView.h" | 
| #include "UI/Keyboard.h" | 
| #include "UI/SplashScreen.h" | 
| #include "Unity/InternalProfiler.h" | 
| #include "Unity/DisplayManager.h" | 
| #include "Unity/EAGLContextHelper.h" | 
| #include "Unity/GlesHelper.h" | 
| #include "Unity/ObjCRuntime.h" | 
| #include "PluginBase/AppDelegateListener.h" | 
| #include "UniversalSDK.h" | 
|   | 
| #import <SMPCQuickSDK/SMPCQuickSDK.h> | 
|   | 
|   | 
| #include <objc/objc.h> | 
| #include <objc/runtime.h> | 
|   | 
| // Set this to 1 to force single threaded rendering | 
| #define UNITY_FORCE_DIRECT_RENDERING 0 | 
|   | 
| // Standard Gesture Recognizers enabled on all iOS apps absorb touches close to the top and bottom of the screen. | 
| // This sometimes causes an ~1 second delay before the touch is handled when clicking very close to the edge. | 
| // You should enable this if you want to avoid that delay. Enabling it should not have any effect on default iOS gestures. | 
| #define DISABLE_TOUCH_DELAYS 1 | 
|   | 
| bool    _ios42orNewer           = false; | 
| bool    _ios43orNewer           = false; | 
| bool    _ios50orNewer           = false; | 
| bool    _ios60orNewer           = false; | 
| bool    _ios70orNewer           = false; | 
| bool    _ios80orNewer           = false; | 
| bool    _ios81orNewer           = false; | 
| bool    _ios82orNewer           = false; | 
| bool    _ios83orNewer           = false; | 
| bool    _ios90orNewer           = false; | 
| bool    _ios91orNewer           = false; | 
| bool    _ios100orNewer          = false; | 
|   | 
| // was unity rendering already inited: we should not touch rendering while this is false | 
| bool    _renderingInited        = false; | 
| // was unity inited: we should not touch unity api while this is false | 
| bool    _unityAppReady          = false; | 
| // see if there's a need to do internal player pause/resume handling | 
| // | 
| // Typically the trampoline code should manage this internally, but | 
| // there are use cases, videoplayer, plugin code, etc where the player | 
| // is paused before the internal handling comes relevant. Avoid | 
| // overriding externally managed player pause/resume handling by | 
| // caching the state | 
| bool    _wasPausedExternal      = false; | 
| // should we skip present on next draw: used in corner cases (like rotation) to fill both draw-buffers with some content | 
| bool    _skipPresent            = false; | 
| // was app "resigned active": some operations do not make sense while app is in background | 
| bool    _didResignActive        = false; | 
|   | 
| // was startUnity scheduled: used to make startup robust in case of locking device | 
| static bool _startUnityScheduled    = false; | 
|   | 
| bool    _supportsMSAA           = false; | 
|   | 
|   | 
| @implementation UnityAppController | 
|   | 
| @synthesize unityView               = _unityView; | 
| @synthesize unityDisplayLink        = _unityDisplayLink; | 
|   | 
| @synthesize rootView                = _rootView; | 
| @synthesize rootViewController      = _rootController; | 
| @synthesize mainDisplay             = _mainDisplay; | 
| @synthesize renderDelegate          = _renderDelegate; | 
| @synthesize quitHandler             = _quitHandler; | 
|   | 
| #if !UNITY_TVOS | 
| @synthesize interfaceOrientation    = _curOrientation; | 
| #endif | 
|   | 
| - (id)init | 
| { | 
|     if ((self = [super init])) | 
|     { | 
|         // due to clang issues with generating warning for overriding deprecated methods | 
|         // we will simply assert if deprecated methods are present | 
|         // NB: methods table is initied at load (before this call), so it is ok to check for override | 
|         NSAssert(![self respondsToSelector: @selector(createUnityViewImpl)], | 
|             @"createUnityViewImpl is deprecated and will not be called. Override createUnityView" | 
|             ); | 
|         NSAssert(![self respondsToSelector: @selector(createViewHierarchyImpl)], | 
|             @"createViewHierarchyImpl is deprecated and will not be called. Override willStartWithViewController" | 
|             ); | 
|         NSAssert(![self respondsToSelector: @selector(createViewHierarchy)], | 
|             @"createViewHierarchy is deprecated and will not be implemented. Use createUI" | 
|             ); | 
|     } | 
|     return self; | 
| } | 
|   | 
| - (void)setWindow:(id)object        {} | 
| - (UIWindow*)window                 { return _window; } | 
|   | 
|   | 
| - (void)shouldAttachRenderDelegate  {} | 
| - (void)preStartUnity               {} | 
|   | 
|   | 
| - (void)startUnity:(UIApplication*)application | 
| { | 
|     NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized"); | 
|   | 
|     UnityInitApplicationGraphics(UNITY_FORCE_DIRECT_RENDERING); | 
|   | 
|     // we make sure that first level gets correct display list and orientation | 
|     [[DisplayManager Instance] updateDisplayListInUnity]; | 
|   | 
|     UnityLoadApplication(); | 
|     Profiler_InitProfiler(); | 
|   | 
|     [self showGameUI]; | 
|     [self createDisplayLink]; | 
|   | 
|     UnitySetPlayerFocus(1); | 
| } | 
|   | 
| extern "C" void UnityDestroyDisplayLink() | 
| { | 
|     [GetAppController() destroyDisplayLink]; | 
| } | 
|   | 
| extern "C" void UnityRequestQuit() | 
| { | 
|     _didResignActive = true; | 
|     if (GetAppController().quitHandler) | 
|         GetAppController().quitHandler(); | 
|     else | 
|         exit(0); | 
| } | 
|   | 
| #if !UNITY_TVOS | 
| - (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window | 
| { | 
|     // UIInterfaceOrientationMaskAll | 
|     // it is the safest way of doing it: | 
|     // - GameCenter and some other services might have portrait-only variant | 
|     //     and will throw exception if portrait is not supported here | 
|     // - When you change allowed orientations if you end up forbidding current one | 
|     //     exception will be thrown | 
|     // Anyway this is intersected with values provided from UIViewController, so we are good | 
|      | 
|     [[SMPCQuickSDK defaultInstance] application:application supportedInterfaceOrientationsForWindow:window]; | 
|      | 
|     return (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationPortraitUpsideDown) | 
|         | (1 << UIInterfaceOrientationLandscapeRight) | (1 << UIInterfaceOrientationLandscapeLeft); | 
| } | 
|   | 
| #endif | 
|   | 
| #if !UNITY_TVOS | 
| - (void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification | 
| { | 
|     AppController_SendNotificationWithArg(kUnityDidReceiveLocalNotification, notification); | 
|     UnitySendLocalNotification(notification); | 
| } | 
|   | 
| #endif | 
|   | 
| #if UNITY_USES_REMOTE_NOTIFICATIONS | 
| - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo | 
| { | 
|     AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo); | 
|     UnitySendRemoteNotification(userInfo); | 
|     [[SMPCQuickSDK defaultInstance] application:application didReceiveRemoteNotification:userInfo]; | 
| } | 
|   | 
| - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken | 
| { | 
|     AppController_SendNotificationWithArg(kUnityDidRegisterForRemoteNotificationsWithDeviceToken, deviceToken); | 
|     UnitySendDeviceToken(deviceToken); | 
|     [[SMPCQuickSDK defaultInstance] application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; | 
| } | 
|   | 
| #if !UNITY_TVOS | 
| - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler | 
| { | 
|     AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo); | 
|     UnitySendRemoteNotification(userInfo); | 
|   | 
|     if (handler) | 
|     { | 
|         handler(UIBackgroundFetchResultNoData); | 
|     } | 
| } | 
|   | 
| #endif | 
|   | 
| - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error | 
| { | 
|     AppController_SendNotificationWithArg(kUnityDidFailToRegisterForRemoteNotificationsWithError, error); | 
|     UnitySendRemoteNotificationError(error); | 
|     [[SMPCQuickSDK defaultInstance] application:application didFailToRegisterForRemoteNotificationsWithError:error]; | 
| } | 
|   | 
| #endif | 
|   | 
| - (BOOL)application:(UIApplication*)application openURL:(NSURL*)url sourceApplication:(NSString*)sourceApplication annotation:(id)annotation | 
| { | 
|     [[SMPCQuickSDK defaultInstance] openURL:url sourceApplication:sourceApplication application:application annotation:annotation]; | 
|     NSMutableArray* keys    = [NSMutableArray arrayWithCapacity: 3]; | 
|     NSMutableArray* values  = [NSMutableArray arrayWithCapacity: 3]; | 
|   | 
|     #define ADD_ITEM(item)  do{ if(item) {[keys addObject:@#item]; [values addObject:item];} }while(0) | 
|   | 
|     ADD_ITEM(url); | 
|     ADD_ITEM(sourceApplication); | 
|     ADD_ITEM(annotation); | 
|   | 
|     #undef ADD_ITEM | 
|   | 
|     NSDictionary* notifData = [NSDictionary dictionaryWithObjects: values forKeys: keys]; | 
|     AppController_SendNotificationWithArg(kUnityOnOpenURL, notifData); | 
|     return YES; | 
| } | 
|   | 
| - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions | 
| { | 
|     return YES; | 
| } | 
|   | 
| - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions | 
| { | 
|     ::printf("-> applicationDidFinishLaunching()\n"); | 
|   | 
|     // send notfications | 
| #if !UNITY_TVOS | 
|     if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey]) | 
|         UnitySendLocalNotification(notification); | 
|   | 
|     if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO) | 
|         [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; | 
| #endif | 
|   | 
|     UnityInitApplicationNoGraphics([[[NSBundle mainBundle] bundlePath] UTF8String]); | 
|   | 
|     [self selectRenderingAPI]; | 
|     [UnityRenderingView InitializeForAPI: self.renderingAPI]; | 
|   | 
|         CGRect _winSize = [UIScreen mainScreen].bounds; | 
|     if(KIsiPhoneX){ | 
|         _winSize.origin.x = 40; | 
|         _winSize.size.width -= 92; | 
|         _winSize.size.height -= 15; | 
|     } | 
|     _window = [[UIWindow alloc] initWithFrame:_winSize]; | 
|   | 
|     _unityView      = [self createUnityView]; | 
|   | 
|     [DisplayManager Initialize]; | 
|     _mainDisplay    = [DisplayManager Instance].mainDisplay; | 
|     [_mainDisplay createWithWindow: _window andView: _unityView]; | 
|   | 
|     [self createUI]; | 
|     [self preStartUnity]; | 
|   | 
|     // if you wont use keyboard you may comment it out at save some memory | 
|     [KeyboardDelegate Initialize]; | 
|   | 
|     _universalSDK = [[UniversalSDK alloc] init]; | 
|     [_universalSDK QuickSDKInit:application didFinishLaunchingWithOptions:launchOptions]; | 
|   | 
|   | 
| #if !PLATFORM_TVOS && DISABLE_TOUCH_DELAYS | 
|     for (UIGestureRecognizer *g in _window.gestureRecognizers) | 
|     { | 
|         g.delaysTouchesBegan = false; | 
|     } | 
| #endif | 
|   | 
|     return YES; | 
| } | 
|   | 
| - (void)applicationDidEnterBackground:(UIApplication*)application | 
| { | 
|     ::printf("-> applicationDidEnterBackground()\n"); | 
|     [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; | 
|     [[SMPCQuickSDK defaultInstance] applicationDidEnterBackground:application]; | 
| } | 
|   | 
| - (void)applicationWillEnterForeground:(UIApplication*)application | 
| { | 
|     ::printf("-> applicationWillEnterForeground()\n"); | 
|     [application setApplicationIconBadgeNumber:0]; | 
|     [application cancelAllLocalNotifications]; | 
|     [[SMPCQuickSDK defaultInstance] applicationWillEnterForeground:application]; | 
|   | 
|     // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup) | 
|     if (_unityAppReady) | 
|     { | 
|         // if we were showing video before going to background - the view size may be changed while we are in background | 
|         [GetAppController().unityView recreateGLESSurfaceIfNeeded]; | 
|     } | 
| } | 
|   | 
| - (void)applicationDidBecomeActive:(UIApplication*)application | 
| { | 
|     ::printf("-> applicationDidBecomeActive()\n"); | 
|   | 
|     [self removeSnapshotView]; | 
|   | 
|     if (_unityAppReady) | 
|     { | 
|         if (UnityIsPaused() && _wasPausedExternal == false) | 
|         { | 
|             UnityWillResume(); | 
|             UnityPause(0); | 
|         } | 
|         if (_wasPausedExternal) | 
|         { | 
|             if (UnityIsFullScreenPlaying()) | 
|                 TryResumeFullScreenVideo(); | 
|         } | 
|         UnitySetPlayerFocus(1); | 
|     } | 
|     else if (!_startUnityScheduled) | 
|     { | 
|         _startUnityScheduled = true; | 
|         [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0]; | 
|     } | 
|   | 
|     _didResignActive = false; | 
|     [[SMPCQuickSDK defaultInstance] applicationDidBecomeActive:application]; | 
| } | 
|   | 
| - (void)removeSnapshotView | 
| { | 
|     // do this on the main queue async so that if we try to create one | 
|     // and remove in the same frame, this always happens after in the same queue | 
|     dispatch_async(dispatch_get_main_queue(), ^{ | 
|         if (_snapshotView) | 
|         { | 
|             [_snapshotView removeFromSuperview]; | 
|             _snapshotView = nil; | 
|         } | 
|     }); | 
| } | 
|   | 
| - (void)applicationWillResignActive:(UIApplication*)application | 
| { | 
|     ::printf("-> applicationWillResignActive()\n"); | 
|   | 
|     if (_unityAppReady) | 
|     { | 
|         UnitySetPlayerFocus(0); | 
|   | 
|         _wasPausedExternal = UnityIsPaused(); | 
|         if (_wasPausedExternal == false) | 
|         { | 
|             // do pause unity only if we dont need special background processing | 
|             // otherwise batched player loop can be called to run user scripts | 
|             int bgBehavior = UnityGetAppBackgroundBehavior(); | 
|             if (bgBehavior == appbgSuspend || bgBehavior == appbgExit) | 
|             { | 
|                 // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager. | 
|                 // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop) | 
|                 // NB: We will actually pause after the loop (when calling UnityPause). | 
|                 UnityWillPause(); | 
|                 [self repaint]; | 
|                 UnityPause(1); | 
|   | 
|                 // this is done on the next frame so that | 
|                 // in the case where unity is paused while going | 
|                 // into the background and an input is deactivated | 
|                 // we don't mess with the view hierarchy while taking | 
|                 // a view snapshot (case 760747). | 
|                 dispatch_async(dispatch_get_main_queue(), ^{ | 
|                     // if we are active again, we don't need to do this anymore | 
|                     if (!_didResignActive) | 
|                     { | 
|                         return; | 
|                     } | 
|   | 
|                     _snapshotView = [self createSnapshotView]; | 
|                     if (_snapshotView) | 
|                         [_rootView addSubview: _snapshotView]; | 
|                 }); | 
|             } | 
|         } | 
|     } | 
|   | 
|     _didResignActive = true; | 
|      | 
|     [[SMPCQuickSDK defaultInstance] applicationWillResignActive:application]; | 
| } | 
|   | 
| - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application | 
| { | 
|     ::printf("WARNING -> applicationDidReceiveMemoryWarning()\n"); | 
|     UnityLowMemory(); | 
| } | 
|   | 
| - (void)applicationWillTerminate:(UIApplication*)application | 
| { | 
|     ::printf("-> applicationWillTerminate()\n"); | 
|     [[SMPCQuickSDK defaultInstance] applicationWillTerminate:application]; | 
|      | 
|     Profiler_UninitProfiler(); | 
|     UnityCleanup(); | 
|   | 
|     extern void SensorsCleanup(); | 
|     SensorsCleanup(); | 
| } | 
|   | 
| @end | 
|   | 
|   | 
| void AppController_SendNotification(NSString* name) | 
| { | 
|     [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController()]; | 
| } | 
|   | 
| void AppController_SendNotificationWithArg(NSString* name, id arg) | 
| { | 
|     [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg]; | 
| } | 
|   | 
| void AppController_SendUnityViewControllerNotification(NSString* name) | 
| { | 
|     [[NSNotificationCenter defaultCenter] postNotificationName: name object: UnityGetGLViewController()]; | 
| } | 
|   | 
| extern "C" UIWindow*            UnityGetMainWindow()        { | 
|     return GetAppController().mainDisplay.window; | 
| } | 
| extern "C" UIViewController*    UnityGetGLViewController()  { | 
|     return GetAppController().rootViewController; | 
| } | 
| extern "C" UIView*              UnityGetGLView()            { | 
|     return GetAppController().unityView; | 
| } | 
| extern "C" ScreenOrientation    UnityCurrentOrientation()   { return GetAppController().unityView.contentOrientation; } | 
|   | 
| extern "C" void IOSMessageHandle(const char* jsonString) { | 
|     [GetAppController().universalSDK HandleUnityMessage:[NSString stringWithUTF8String:jsonString]]; | 
| } | 
|   | 
|   | 
|   | 
| bool LogToNSLogHandler(LogType logType, const char* log, va_list list) | 
| { | 
|     NSLogv([NSString stringWithUTF8String: log], list); | 
|     return true; | 
| } | 
|   | 
| void UnityInitTrampoline() | 
| { | 
| #if ENABLE_CRASH_REPORT_SUBMISSION | 
|     SubmitCrashReportsAsync(); | 
| #endif | 
|     InitCrashHandling(); | 
|   | 
|     NSString* version = [[UIDevice currentDevice] systemVersion]; | 
|   | 
|     // keep native plugin developers happy and keep old bools around | 
|     _ios42orNewer = true; | 
|     _ios43orNewer = true; | 
|     _ios50orNewer = true; | 
|     _ios60orNewer = true; | 
|     _ios70orNewer = [version compare: @"7.0" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios80orNewer = [version compare: @"8.0" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios81orNewer = [version compare: @"8.1" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios82orNewer = [version compare: @"8.2" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios83orNewer = [version compare: @"8.3" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios90orNewer = [version compare: @"9.0" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios91orNewer = [version compare: @"9.1" options: NSNumericSearch] != NSOrderedAscending; | 
|     _ios100orNewer = [version compare: @"10.0" options: NSNumericSearch] != NSOrderedAscending; | 
|   | 
|     // Try writing to console and if it fails switch to NSLog logging | 
|     ::fprintf(stdout, "\n"); | 
|     if (::ftell(stdout) < 0) | 
|         UnitySetLogEntryHandler(LogToNSLogHandler); | 
|   | 
|     if (![[UIView class] instancesRespondToSelector: @selector(safeAreaInsets)]) | 
|     { | 
|         IMP UIView_SafeAreaInsets_IMP = imp_implementationWithBlock(^UIEdgeInsets(id _self) { | 
|             return UIEdgeInsetsZero; | 
|         }); | 
|         class_replaceMethod([UIView class], @selector(safeAreaInsets), UIView_SafeAreaInsets_IMP, UIView_safeAreaInsets_Enc); | 
|     } | 
| } |