UnityAppController.mm 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. #import "UnityAppController.h"
  2. #import "UnityAppController+ViewHandling.h"
  3. #import "UnityAppController+Rendering.h"
  4. #import "iPhone_Sensors.h"
  5. #import <CoreGraphics/CoreGraphics.h>
  6. #import <QuartzCore/QuartzCore.h>
  7. #import <QuartzCore/CADisplayLink.h>
  8. #import <Availability.h>
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <SMSDK/SMSDK.h>
  11. #import <QsdkMgr.h>
  12. #include <mach/mach_time.h>
  13. // MSAA_DEFAULT_SAMPLE_COUNT was removed
  14. // ENABLE_INTERNAL_PROFILER and related defines were moved to iPhone_Profiler.h
  15. // kFPS define for removed: you can use Application.targetFrameRate (30 fps by default)
  16. // DisplayLink is the only run loop mode now - all others were removed
  17. #include "CrashReporter.h"
  18. #include "UI/OrientationSupport.h"
  19. #include "UI/UnityView.h"
  20. #include "UI/Keyboard.h"
  21. #include "UI/UnityViewControllerBase.h"
  22. #include "Unity/InternalProfiler.h"
  23. #include "Unity/DisplayManager.h"
  24. #include "Unity/ObjCRuntime.h"
  25. #include "PluginBase/AppDelegateListener.h"
  26. #include <assert.h>
  27. #include <stdbool.h>
  28. #include <sys/types.h>
  29. #include <unistd.h>
  30. #include <sys/sysctl.h>
  31. // we assume that app delegate is never changed and we can cache it, instead of re-query UIApplication every time
  32. UnityAppController* _UnityAppController = nil;
  33. UnityAppController* GetAppController()
  34. {
  35. return _UnityAppController;
  36. }
  37. // we keep old bools around to support "old" code that might have used them
  38. bool _ios81orNewer = false, _ios82orNewer = false, _ios83orNewer = false, _ios90orNewer = false, _ios91orNewer = false;
  39. bool _ios100orNewer = false, _ios101orNewer = false, _ios102orNewer = false, _ios103orNewer = false;
  40. bool _ios110orNewer = false, _ios111orNewer = false, _ios112orNewer = false;
  41. bool _ios130orNewer = false, _ios140orNewer = false, _ios150orNewer = false, _ios160orNewer = false;
  42. // minimal Unity initialization done, enough to do calls to provide data like URL launch
  43. bool _unityEngineLoaded = false;
  44. // was core of Unity loaded (non-graphics part prior to loading first scene)
  45. bool _unityEngineInitialized = false;
  46. // was unity rendering already inited: we should not touch rendering while this is false
  47. bool _renderingInited = false;
  48. // was unity inited: we should not touch unity api while this is false
  49. bool _unityAppReady = false;
  50. // see if there's a need to do internal player pause/resume handling
  51. //
  52. // Typically the trampoline code should manage this internally, but
  53. // there are use cases, videoplayer, plugin code, etc where the player
  54. // is paused before the internal handling comes relevant. Avoid
  55. // overriding externally managed player pause/resume handling by
  56. // caching the state
  57. bool _wasPausedExternal = false;
  58. // should we skip present on next draw: used in corner cases (like rotation) to fill both draw-buffers with some content
  59. bool _skipPresent = false;
  60. // was app "resigned active": some operations do not make sense while app is in background
  61. bool _didResignActive = false;
  62. #if UNITY_SUPPORT_ROTATION
  63. // Required to enable specific orientation for some presentation controllers: see supportedInterfaceOrientationsForWindow below for details
  64. NSInteger _forceInterfaceOrientationMask = 0;
  65. #endif
  66. @implementation UnityAppController
  67. @synthesize unityView = _unityView;
  68. @synthesize unityDisplayLink = _displayLink;
  69. @synthesize rootView = _rootView;
  70. @synthesize rootViewController = _rootController;
  71. @synthesize mainDisplay = _mainDisplay;
  72. @synthesize renderDelegate = _renderDelegate;
  73. @synthesize quitHandler = _quitHandler;
  74. #if UNITY_SUPPORT_ROTATION
  75. @synthesize interfaceOrientation = _curOrientation;
  76. #endif
  77. - (id)init
  78. {
  79. if ((self = _UnityAppController = [super init]))
  80. {
  81. // due to clang issues with generating warning for overriding deprecated methods
  82. // we will simply assert if deprecated methods are present
  83. // NB: methods table is initied at load (before this call), so it is ok to check for override
  84. NSAssert(![self respondsToSelector: @selector(createUnityViewImpl)],
  85. @"createUnityViewImpl is deprecated and will not be called. Override createUnityView"
  86. );
  87. NSAssert(![self respondsToSelector: @selector(createViewHierarchyImpl)],
  88. @"createViewHierarchyImpl is deprecated and will not be called. Override willStartWithViewController"
  89. );
  90. NSAssert(![self respondsToSelector: @selector(createViewHierarchy)],
  91. @"createViewHierarchy is deprecated and will not be implemented. Use createUI"
  92. );
  93. }
  94. return self;
  95. }
  96. - (void)setWindow:(id)object {}
  97. - (UIWindow*)window { return _window; }
  98. - (void)shouldAttachRenderDelegate {}
  99. - (void)preStartUnity {}
  100. - (void)startUnity:(UIApplication*)application
  101. {
  102. NSAssert(_unityAppReady == NO, @"[UnityAppController startUnity:] called after Unity has been initialized");
  103. UnityInitApplicationGraphics();
  104. // we make sure that first level gets correct display list and orientation
  105. [[DisplayManager Instance] updateDisplayListCacheInUnity];
  106. UnityLoadApplication();
  107. Profiler_InitProfiler();
  108. [self showGameUI];
  109. [self createDisplayLink];
  110. UnitySetPlayerFocus(1);
  111. AVAudioSession* audioSession = [AVAudioSession sharedInstance];
  112. // If Unity audio is disabled, we set the category to ambient to make sure we don't mute other app's audio. We set the audio session
  113. // to active so we can get outputVolume callbacks. If Unity audio is enabled, FMOD should have already handled all of this AVAudioSession init.
  114. if (!UnityIsAudioManagerAvailableAndEnabled())
  115. {
  116. [audioSession setCategory: AVAudioSessionCategoryAmbient error: nil];
  117. [audioSession setActive: YES error: nil];
  118. }
  119. [audioSession addObserver: self forKeyPath: @"outputVolume" options: 0 context: nil];
  120. UnityUpdateMuteState([audioSession outputVolume] < 0.01f ? 1 : 0);
  121. #if UNITY_REPLAY_KIT_AVAILABLE
  122. void InitUnityReplayKit(); // Classes/Unity/UnityReplayKit.mm
  123. InitUnityReplayKit();
  124. #endif
  125. }
  126. extern "C" void UnityDestroyDisplayLink()
  127. {
  128. [GetAppController() destroyDisplayLink];
  129. }
  130. extern "C" void UnityRequestQuit()
  131. {
  132. _didResignActive = true;
  133. if (GetAppController().quitHandler)
  134. GetAppController().quitHandler();
  135. else
  136. exit(0);
  137. }
  138. extern void SensorsCleanup();
  139. extern "C" void UnityCleanupTrampoline()
  140. {
  141. // Unity view and viewController will not necessary be destroyed right after this function execution.
  142. // We need to ensure that these objects will not receive any callbacks from system during that time.
  143. [_UnityAppController window].rootViewController = nil;
  144. [[_UnityAppController unityView] removeFromSuperview];
  145. // Prevent multiple cleanups
  146. if (_UnityAppController == nil)
  147. return;
  148. [KeyboardDelegate Destroy];
  149. SensorsCleanup();
  150. Profiler_UninitProfiler();
  151. [DisplayManager Destroy];
  152. UnityDestroyDisplayLink();
  153. _UnityAppController = nil;
  154. }
  155. #if UNITY_SUPPORT_ROTATION
  156. - (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
  157. {
  158. // No rootViewController is set because we are switching from one view controller to another, all orientations should be enabled
  159. if ([window rootViewController] == nil)
  160. return UIInterfaceOrientationMaskAll;
  161. // During splash screen show phase no forced orientations should be allowed.
  162. // This will prevent unwanted rotation while splash screen is on and application is not yet ready to present (Ex. Fogbugz cases: 1190428, 1269547).
  163. if (!_unityAppReady)
  164. return [_rootController supportedInterfaceOrientations];
  165. // Some presentation controllers (e.g. UIImagePickerController) require portrait orientation and will throw exception if it is not supported.
  166. // At the same time enabling all orientations by returning UIInterfaceOrientationMaskAll might cause unwanted orientation change
  167. // (e.g. when using UIActivityViewController to "share to" another application, iOS will use supportedInterfaceOrientations to possibly reorient).
  168. // So to avoid exception we are returning combination of constraints for root view controller and orientation requested by iOS.
  169. // _forceInterfaceOrientationMask is updated in willChangeStatusBarOrientation, which is called if some presentation controller insists on orientation change.
  170. return [[window rootViewController] supportedInterfaceOrientations] | _forceInterfaceOrientationMask;
  171. }
  172. - (void)application:(UIApplication*)application willChangeStatusBarOrientation:(UIInterfaceOrientation)newStatusBarOrientation duration:(NSTimeInterval)duration
  173. {
  174. // Setting orientation mask which is requested by iOS: see supportedInterfaceOrientationsForWindow above for details
  175. _forceInterfaceOrientationMask = 1 << newStatusBarOrientation;
  176. }
  177. #endif
  178. #if !PLATFORM_TVOS
  179. #pragma clang diagnostic push
  180. #pragma clang diagnostic ignored "-Wdeprecated-implementations"
  181. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  182. - (void)application:(UIApplication*)application didReceiveLocalNotification:(UILocalNotification*)notification
  183. {
  184. AppController_SendNotificationWithArg(kUnityDidReceiveLocalNotification, notification);
  185. UnitySendLocalNotification(notification);
  186. }
  187. #pragma clang diagnostic pop
  188. #endif
  189. #if UNITY_USES_REMOTE_NOTIFICATIONS
  190. - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
  191. {
  192. AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
  193. UnitySendRemoteNotification(userInfo);
  194. }
  195. - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
  196. {
  197. AppController_SendNotificationWithArg(kUnityDidRegisterForRemoteNotificationsWithDeviceToken, deviceToken);
  198. UnitySendDeviceToken(deviceToken);
  199. }
  200. #if !PLATFORM_TVOS
  201. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
  202. {
  203. AppController_SendNotificationWithArg(kUnityDidReceiveRemoteNotification, userInfo);
  204. UnitySendRemoteNotification(userInfo);
  205. if (handler)
  206. {
  207. handler(UIBackgroundFetchResultNoData);
  208. }
  209. }
  210. #endif
  211. - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
  212. {
  213. AppController_SendNotificationWithArg(kUnityDidFailToRegisterForRemoteNotificationsWithError, error);
  214. UnitySendRemoteNotificationError(error);
  215. // alas people do not check remote notification error through api (which is clunky, i agree) so log here to have at least some visibility
  216. ::printf("\nFailed to register for remote notifications:\n%s\n\n", [[error localizedDescription] UTF8String]);
  217. }
  218. #endif
  219. // UIApplicationOpenURLOptionsKey was added only in ios10 sdk, while we still support ios9 sdk
  220. - (BOOL)application:(UIApplication*)app openURL:(NSURL*)url options:(NSDictionary<NSString*, id>*)options
  221. {
  222. id sourceApplication = options[UIApplicationOpenURLOptionsSourceApplicationKey], annotation = options[UIApplicationOpenURLOptionsAnnotationKey];
  223. NSMutableDictionary<NSString*, id>* notifData = [NSMutableDictionary dictionaryWithCapacity: 3];
  224. if (url)
  225. {
  226. notifData[@"url"] = url;
  227. UnitySetAbsoluteURL(url.absoluteString.UTF8String);
  228. }
  229. if (sourceApplication) notifData[@"sourceApplication"] = sourceApplication;
  230. if (annotation) notifData[@"annotation"] = annotation;
  231. AppController_SendNotificationWithArg(kUnityOnOpenURL, notifData);
  232. return [SMSDK handleApplication:app openURL:url
  233. sourceApplication:[options valueForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"]
  234. annotation:[options valueForKey:@"UIApplicationOpenURLOptionsAnnotationKey"]];
  235. }
  236. - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
  237. #if defined(__IPHONE_12_0) || defined(__TVOS_12_0)
  238. restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring> > * _Nullable restorableObjects))restorationHandler
  239. #else
  240. restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
  241. #endif
  242. {
  243. NSURL* url = userActivity.webpageURL;
  244. if (url)
  245. UnitySetAbsoluteURL(url.absoluteString.UTF8String);
  246. return YES;
  247. }
  248. - (BOOL)application:(UIApplication*)application willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
  249. {
  250. AppController_SendNotificationWithArg(kUnityWillFinishLaunchingWithOptions, launchOptions);
  251. NSURL* url = launchOptions[UIApplicationLaunchOptionsURLKey];
  252. if (url != nil)
  253. {
  254. [self initUnityApplicationNoGraphics];
  255. UnitySetAbsoluteURL(url.absoluteString.UTF8String);
  256. }
  257. return YES;
  258. }
  259. #if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
  260. - (UIWindowScene*)pickStartupWindowScene:(NSSet<UIScene*>*)scenes API_AVAILABLE(ios(13.0), tvos(13.0))
  261. {
  262. // if we have scene with UISceneActivationStateForegroundActive - pick it
  263. // otherwise UISceneActivationStateForegroundInactive will work
  264. // it will be the scene going into active state
  265. // if there were no active/inactive scenes (only background) we should allow background scene
  266. // this might happen in some cases with native plugins doing "things"
  267. UIWindowScene *foregroundScene = nil, *backgroundScene = nil;
  268. for (UIScene* scene in scenes)
  269. {
  270. if (![scene isKindOfClass: [UIWindowScene class]])
  271. continue;
  272. UIWindowScene* windowScene = (UIWindowScene*)scene;
  273. if (scene.activationState == UISceneActivationStateForegroundActive)
  274. return windowScene;
  275. if (scene.activationState == UISceneActivationStateForegroundInactive)
  276. foregroundScene = windowScene;
  277. else if (scene.activationState == UISceneActivationStateBackground)
  278. backgroundScene = windowScene;
  279. }
  280. return foregroundScene ? foregroundScene : backgroundScene;
  281. }
  282. #endif
  283. - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
  284. {
  285. ::printf("-> applicationDidFinishLaunching()\n");
  286. [[my_sdk_mgr Instance] InitSDK];
  287. // send notfications
  288. #if !PLATFORM_TVOS
  289. #pragma clang diagnostic push
  290. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  291. if (UILocalNotification* notification = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey])
  292. UnitySendLocalNotification(notification);
  293. if ([UIDevice currentDevice].generatesDeviceOrientationNotifications == NO)
  294. [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
  295. #pragma clang diagnostic pop
  296. #endif
  297. if ([self isBackgroundLaunchOptions: launchOptions])
  298. return YES;
  299. [self initUnityWithApplication: application];
  300. return YES;
  301. }
  302. - (BOOL)isBackgroundLaunchOptions:(NSDictionary*)launchOptions
  303. {
  304. if (launchOptions.count == 0)
  305. return NO;
  306. // launch due to location event, the app likely will stay in background
  307. BOOL locationLaunch = [[launchOptions valueForKey: UIApplicationLaunchOptionsLocationKey] boolValue];
  308. if (locationLaunch)
  309. return YES;
  310. return NO;
  311. }
  312. - (void)initUnityApplicationNoGraphics
  313. {
  314. if (_unityEngineLoaded)
  315. return;
  316. _unityEngineLoaded = true;
  317. UnityInitApplicationNoGraphics(UnityDataBundleDir());
  318. }
  319. - (void)initUnityWithApplication:(UIApplication*)application
  320. {
  321. if (_unityEngineInitialized)
  322. return;
  323. _unityEngineInitialized = true;
  324. // basic unity init
  325. [self initUnityApplicationNoGraphics];
  326. [self selectRenderingAPI];
  327. [UnityRenderingView InitializeForAPI: self.renderingAPI];
  328. #if (PLATFORM_IOS && defined(__IPHONE_13_0)) || (PLATFORM_TVOS && defined(__TVOS_13_0))
  329. if (@available(iOS 13, tvOS 13, *))
  330. _window = [[UIWindow alloc] initWithWindowScene: [self pickStartupWindowScene: application.connectedScenes]];
  331. else
  332. #endif
  333. _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
  334. _unityView = [self createUnityView];
  335. [DisplayManager Initialize];
  336. _mainDisplay = [DisplayManager Instance].mainDisplay;
  337. [_mainDisplay createWithWindow: _window andView: _unityView];
  338. [self createUI];
  339. [self preStartUnity];
  340. // if you wont use keyboard you may comment it out at save some memory
  341. [KeyboardDelegate Initialize];
  342. #if UNITY_DEVELOPER_BUILD
  343. // Causes a black screen after splash screen, but would deadlock if waiting for manged debugger otherwise
  344. [self performSelector: @selector(startUnity:) withObject: application afterDelay: 0];
  345. #else
  346. [self startUnity: application];
  347. #endif
  348. }
  349. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
  350. {
  351. if ([keyPath isEqual: @"outputVolume"])
  352. {
  353. UnityUpdateMuteState([[AVAudioSession sharedInstance] outputVolume] < 0.01f ? 1 : 0);
  354. }
  355. }
  356. - (void)applicationDidEnterBackground:(UIApplication*)application
  357. {
  358. ::printf("-> applicationDidEnterBackground()\n");
  359. }
  360. - (void)applicationWillEnterForeground:(UIApplication*)application
  361. {
  362. ::printf("-> applicationWillEnterForeground()\n");
  363. // applicationWillEnterForeground: might sometimes arrive *before* actually initing unity (e.g. locking on startup)
  364. if (_unityAppReady)
  365. {
  366. // if we were showing video before going to background - the view size may be changed while we are in background
  367. [GetAppController().unityView recreateRenderingSurfaceIfNeeded];
  368. }
  369. }
  370. - (void)applicationDidBecomeActive:(UIApplication*)application
  371. {
  372. ::printf("-> applicationDidBecomeActive()\n");
  373. [self removeSnapshotViewController];
  374. if (_unityAppReady)
  375. {
  376. if (UnityIsPaused() && _wasPausedExternal == false)
  377. {
  378. UnityWillResume();
  379. UnityPause(0);
  380. }
  381. if (_wasPausedExternal)
  382. {
  383. if (UnityIsFullScreenPlaying())
  384. TryResumeFullScreenVideo();
  385. }
  386. // need to do this with delay because FMOD restarts audio in AVAudioSessionInterruptionNotification handler
  387. [self performSelector: @selector(updateUnityAudioOutput) withObject: nil afterDelay: 0.1];
  388. UnitySetPlayerFocus(1);
  389. }
  390. else
  391. {
  392. [self initUnityWithApplication: application];
  393. }
  394. _didResignActive = false;
  395. }
  396. - (void)updateUnityAudioOutput
  397. {
  398. UnityUpdateMuteState([[AVAudioSession sharedInstance] outputVolume] < 0.01f ? 1 : 0);
  399. }
  400. - (void)addSnapshotViewController
  401. {
  402. if (!_didResignActive || self->_snapshotViewController)
  403. {
  404. return;
  405. }
  406. UIView* snapshotView = [self createSnapshotView];
  407. if (snapshotView != nil)
  408. {
  409. UIViewController* snapshotViewController = [AllocUnityViewController() init];
  410. snapshotViewController.modalPresentationStyle = UIModalPresentationFullScreen;
  411. snapshotViewController.view = snapshotView;
  412. [self->_rootController presentViewController: snapshotViewController animated: false completion: nil];
  413. self->_snapshotViewController = snapshotViewController;
  414. }
  415. }
  416. - (void)removeSnapshotViewController
  417. {
  418. // do this on the main queue async so that if we try to create one
  419. // and remove in the same frame, this always happens after in the same queue
  420. dispatch_async(dispatch_get_main_queue(), ^{
  421. if (self->_snapshotViewController)
  422. {
  423. // we've got a view on top of the snapshot view (3rd party plugin/social media login etc).
  424. if (self->_snapshotViewController.presentedViewController)
  425. {
  426. [self performSelector: @selector(removeSnapshotViewController) withObject: nil afterDelay: 0.05];
  427. return;
  428. }
  429. [self->_snapshotViewController dismissViewControllerAnimated: NO completion: nil];
  430. self->_snapshotViewController = nil;
  431. // Make sure that the keyboard input field regains focus after the application becomes active.
  432. [[KeyboardDelegate Instance] becomeFirstResponder];
  433. }
  434. });
  435. }
  436. - (void)applicationWillResignActive:(UIApplication*)application
  437. {
  438. ::printf("-> applicationWillResignActive()\n");
  439. if (_unityAppReady)
  440. {
  441. UnitySetPlayerFocus(0);
  442. // signal unity that the frame rendering have ended
  443. // as we will not get the callback from the display link current frame
  444. UnityDisplayLinkCallback(0);
  445. _wasPausedExternal = UnityIsPaused();
  446. if (_wasPausedExternal == false)
  447. {
  448. // Pause Unity only if we don't need special background processing
  449. // otherwise batched player loop can be called to run user scripts.
  450. if (!UnityGetUseCustomAppBackgroundBehavior())
  451. {
  452. #if UNITY_SNAPSHOT_VIEW_ON_APPLICATION_PAUSE
  453. // Force player to do one more frame, so scripts get a chance to render custom screen for minimized app in task manager.
  454. // NB: UnityWillPause will schedule OnApplicationPause message, which will be sent normally inside repaint (unity player loop)
  455. // NB: We will actually pause after the loop (when calling UnityPause).
  456. UnityWillPause();
  457. [self repaint];
  458. UnityWaitForFrame();
  459. [self addSnapshotViewController];
  460. #endif
  461. UnityPause(1);
  462. }
  463. }
  464. }
  465. _didResignActive = true;
  466. }
  467. - (void)applicationDidReceiveMemoryWarning:(UIApplication*)application
  468. {
  469. // We handle it by subscribing for notification, this one is only a placeholder for stuff like forwarding in UaaL setups
  470. }
  471. - (void)applicationWillTerminate:(UIApplication*)application
  472. {
  473. ::printf("-> applicationWillTerminate()\n");
  474. // Only clean up if Unity has finished initializing, else the clean up process will crash,
  475. // this happens if the app is force closed immediately after opening it.
  476. if (_unityAppReady)
  477. {
  478. UnityCleanup();
  479. UnityCleanupTrampoline();
  480. }
  481. }
  482. - (void)application:(UIApplication*)application handleEventsForBackgroundURLSession:(nonnull NSString *)identifier completionHandler:(nonnull void (^)())completionHandler
  483. {
  484. NSDictionary* arg = @{identifier: completionHandler};
  485. AppController_SendNotificationWithArg(kUnityHandleEventsForBackgroundURLSession, arg);
  486. }
  487. //主要是为了处理支付宝回调数据
  488. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  489. return [SMSDK handleApplication:application openURL:url sourceApplication:sourceApplication annotation:annotation];
  490. }
  491. @end
  492. void AppController_SendNotification(NSString* name)
  493. {
  494. [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController()];
  495. }
  496. void AppController_SendNotificationWithArg(NSString* name, id arg)
  497. {
  498. [[NSNotificationCenter defaultCenter] postNotificationName: name object: GetAppController() userInfo: arg];
  499. }
  500. void AppController_SendUnityViewControllerNotification(NSString* name)
  501. {
  502. [[NSNotificationCenter defaultCenter] postNotificationName: name object: UnityGetGLViewController()];
  503. }
  504. extern "C" UIWindow* UnityGetMainWindow()
  505. {
  506. return GetAppController().mainDisplay.window;
  507. }
  508. extern "C" UIViewController* UnityGetGLViewController()
  509. {
  510. return GetAppController().rootViewController;
  511. }
  512. extern "C" UIView* UnityGetGLView()
  513. {
  514. return GetAppController().unityView;
  515. }
  516. extern "C" ScreenOrientation UnityCurrentOrientation() { return GetAppController().unityView.contentOrientation; }
  517. bool LogToNSLogHandler(LogType logType, const char* log, va_list list)
  518. {
  519. NSLogv([NSString stringWithUTF8String: log], list);
  520. return true;
  521. }
  522. static void AddNewAPIImplIfNeeded();
  523. // From https://stackoverflow.com/questions/4744826/detecting-if-ios-app-is-run-in-debugger
  524. static bool isDebuggerAttachedToConsole(void)
  525. // Returns true if the current process is being debugged (either
  526. // running under the debugger or has a debugger attached post facto).
  527. {
  528. int junk;
  529. int mib[4];
  530. struct kinfo_proc info;
  531. size_t size;
  532. // Initialize the flags so that, if sysctl fails for some bizarre
  533. // reason, we get a predictable result.
  534. info.kp_proc.p_flag = 0;
  535. // Initialize mib, which tells sysctl the info we want, in this case
  536. // we're looking for information about a specific process ID.
  537. mib[0] = CTL_KERN;
  538. mib[1] = KERN_PROC;
  539. mib[2] = KERN_PROC_PID;
  540. mib[3] = getpid();
  541. // Call sysctl.
  542. size = sizeof(info);
  543. junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
  544. assert(junk == 0);
  545. // We're being debugged if the P_TRACED flag is set.
  546. return ((info.kp_proc.p_flag & P_TRACED) != 0);
  547. }
  548. void UnityInitTrampoline()
  549. {
  550. InitCrashHandling();
  551. NSString* version = [[UIDevice currentDevice] systemVersion];
  552. #define CHECK_VER(s) [version compare: s options: NSNumericSearch] != NSOrderedAscending
  553. _ios81orNewer = CHECK_VER(@"8.1"); _ios82orNewer = CHECK_VER(@"8.2"); _ios83orNewer = CHECK_VER(@"8.3");
  554. _ios90orNewer = CHECK_VER(@"9.0"); _ios91orNewer = CHECK_VER(@"9.1");
  555. _ios100orNewer = CHECK_VER(@"10.0"); _ios101orNewer = CHECK_VER(@"10.1"); _ios102orNewer = CHECK_VER(@"10.2"); _ios103orNewer = CHECK_VER(@"10.3");
  556. _ios110orNewer = CHECK_VER(@"11.0"); _ios111orNewer = CHECK_VER(@"11.1"); _ios112orNewer = CHECK_VER(@"11.2");
  557. _ios130orNewer = CHECK_VER(@"13.0"); _ios140orNewer = CHECK_VER(@"14.0"); _ios150orNewer = CHECK_VER(@"15.0");
  558. _ios160orNewer = CHECK_VER(@"16.0");
  559. #undef CHECK_VER
  560. AddNewAPIImplIfNeeded();
  561. #if !TARGET_IPHONE_SIMULATOR
  562. // Use NSLog logging if a debugger is not attached, otherwise we write to stdout.
  563. if (!isDebuggerAttachedToConsole())
  564. UnitySetLogEntryHandler(LogToNSLogHandler);
  565. #endif
  566. }
  567. extern "C" bool UnityiOS81orNewer() { return _ios81orNewer; }
  568. extern "C" bool UnityiOS82orNewer() { return _ios82orNewer; }
  569. extern "C" bool UnityiOS90orNewer() { return _ios90orNewer; }
  570. extern "C" bool UnityiOS91orNewer() { return _ios91orNewer; }
  571. extern "C" bool UnityiOS100orNewer() { return _ios100orNewer; }
  572. extern "C" bool UnityiOS101orNewer() { return _ios101orNewer; }
  573. extern "C" bool UnityiOS102orNewer() { return _ios102orNewer; }
  574. extern "C" bool UnityiOS103orNewer() { return _ios103orNewer; }
  575. extern "C" bool UnityiOS110orNewer() { return _ios110orNewer; }
  576. extern "C" bool UnityiOS111orNewer() { return _ios111orNewer; }
  577. extern "C" bool UnityiOS112orNewer() { return _ios112orNewer; }
  578. extern "C" bool UnityiOS130orNewer() { return _ios130orNewer; }
  579. extern "C" bool UnityiOS140orNewer() { return _ios140orNewer; }
  580. extern "C" bool UnityiOS150orNewer() { return _ios150orNewer; }
  581. extern "C" bool UnityiOS160orNewer() { return _ios160orNewer; }
  582. // sometimes apple adds new api with obvious fallback on older ios.
  583. // in that case we simply add these functions ourselves to simplify code
  584. static void AddNewAPIImplIfNeeded()
  585. {
  586. if (![[UIScreen class] instancesRespondToSelector: @selector(maximumFramesPerSecond)])
  587. {
  588. IMP UIScreen_MaximumFramesPerSecond_IMP = imp_implementationWithBlock(^NSInteger(id _self) {
  589. return 60;
  590. });
  591. class_replaceMethod([UIScreen class], @selector(maximumFramesPerSecond), UIScreen_MaximumFramesPerSecond_IMP, UIScreen_maximumFramesPerSecond_Enc);
  592. }
  593. if (![[UIView class] instancesRespondToSelector: @selector(safeAreaInsets)])
  594. {
  595. IMP UIView_SafeAreaInsets_IMP = imp_implementationWithBlock(^UIEdgeInsets(id _self) {
  596. return UIEdgeInsetsZero;
  597. });
  598. class_replaceMethod([UIView class], @selector(safeAreaInsets), UIView_SafeAreaInsets_IMP, UIView_safeAreaInsets_Enc);
  599. }
  600. }
  601. // xcode11 uses new compiler-rt lib
  602. // if we build unity player lib with xcode11 and then user links final project with older xcode
  603. // the link fails with Undefined Symbol ___isPlatformVersionAtLeast
  604. // hence we add this as a temporary hack until we start requiring xcode11
  605. #if __clang_major__ < 11
  606. extern "C" int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor);
  607. extern "C" int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major, uint32_t Minor, uint32_t Subminor)
  608. {
  609. return __isOSVersionAtLeast(Major, Minor, Subminor);
  610. }
  611. #endif
  612. // starting with xcode 11.4 apple changed FD_SET and related macro to use weakly imported __darwin_check_fd_set_overflow
  613. // alas if we build xcode project with OLDER xcode this function is missing
  614. // and we build unity lib with xcode11+, thus producing linker error
  615. // we mimic the logic of apple sdk itself (this part is open sourced):
  616. // if __darwin_check_fd_set_overflow is not present the caller returns 1, so do we
  617. #ifndef __IPHONE_13_4
  618. extern "C" int __darwin_check_fd_set_overflow(int, const void *, int)
  619. {
  620. return 1;
  621. }
  622. #endif