CCApplication-ios.mm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /****************************************************************************
  2. Copyright (c) 2010-2012 cocos2d-x.org
  3. Copyright (c) 2013-2017 Chukong Technologies Inc.
  4. http://www.cocos2d-x.org
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11. The above copyright notice and this permission notice shall be included in
  12. all copies or substantial portions of the Software.
  13. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. THE SOFTWARE.
  20. ****************************************************************************/
  21. #import "CCApplication.h"
  22. #import <UIKit/UIKit.h>
  23. #include "base/CCScheduler.h"
  24. #include "base/CCAutoreleasePool.h"
  25. #include "base/CCGLUtils.h"
  26. #include "base/CCConfiguration.h"
  27. #include "renderer/gfx/DeviceGraphics.h"
  28. #include "scripting/js-bindings/event/EventDispatcher.h"
  29. #include "scripting/js-bindings/jswrapper/SeApi.h"
  30. #include "CCEAGLView-ios.h"
  31. #include "base/CCGLUtils.h"
  32. #include "audio/include/AudioEngine.h"
  33. #include "platform/CCDevice.h"
  34. #include "cocos/renderer/gfx/DeviceGraphics.h"
  35. namespace
  36. {
  37. bool setCanvasCallback(se::Object* global)
  38. {
  39. auto &viewSize = cocos2d::Application::getInstance()->getViewSize();
  40. se::ScriptEngine* se = se::ScriptEngine::getInstance();
  41. uint8_t devicePixelRatio = cocos2d::Application::getInstance()->getDevicePixelRatio();
  42. int screenScale = cocos2d::Device::getDevicePixelRatio();
  43. char commandBuf[200] = {0};
  44. //set window.innerWidth/innerHeight in CSS pixel units, not physical pixel units.
  45. sprintf(commandBuf, "window.innerWidth = %d; window.innerHeight = %d;",
  46. (int)(viewSize.x / screenScale / devicePixelRatio),
  47. (int)(viewSize.y / screenScale / devicePixelRatio));
  48. se->evalString(commandBuf);
  49. glDepthMask(GL_TRUE);
  50. return true;
  51. }
  52. }
  53. @interface MainLoop : NSObject
  54. {
  55. id _displayLink;
  56. int _fps;
  57. float _systemVersion;
  58. BOOL _isAppActive;
  59. cocos2d::Device::Rotation _lastRotation;
  60. cocos2d::Application* _application;
  61. std::shared_ptr<cocos2d::Scheduler> _scheduler;
  62. }
  63. -(void) startMainLoop;
  64. -(void) stopMainLoop;
  65. -(void) doCaller: (id) sender;
  66. -(void) setPreferredFPS:(int)fps;
  67. -(void) firstStart:(id) view;
  68. @end
  69. @implementation MainLoop
  70. - (instancetype)initWithApplication:(cocos2d::Application*) application
  71. {
  72. self = [super init];
  73. if (self)
  74. {
  75. _fps = 60;
  76. _systemVersion = [[UIDevice currentDevice].systemVersion floatValue];
  77. _application = application;
  78. _scheduler = _application->getScheduler();
  79. _lastRotation = cocos2d::Device::getDeviceRotation();
  80. _isAppActive = [UIApplication sharedApplication].applicationState == UIApplicationStateActive;
  81. NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  82. [nc addObserver:self selector:@selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  83. [nc addObserver:self selector:@selector(appDidBecomeInactive) name:UIApplicationWillResignActiveNotification object:nil];
  84. [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
  85. [nc addObserver:self selector:@selector(statusBarOrientationChanged:)name:UIApplicationDidChangeStatusBarOrientationNotification
  86. object:nil];
  87. }
  88. return self;
  89. }
  90. - (void) statusBarOrientationChanged:(NSNotification *)note
  91. {
  92. cocos2d::Device::Rotation rotation = cocos2d::Device::Rotation::_0;
  93. UIDevice * device = [UIDevice currentDevice];
  94. // NOTE: https://developer.apple.com/documentation/uikit/uideviceorientation
  95. // when the device rotates to LandscapeLeft, device.orientation returns UIDeviceOrientationLandscapeRight
  96. // when the device rotates to LandscapeRight, device.orientation returns UIDeviceOrientationLandscapeLeft
  97. switch(device.orientation)
  98. {
  99. case UIDeviceOrientationPortrait:
  100. rotation = cocos2d::Device::Rotation::_0;
  101. break;
  102. case UIDeviceOrientationLandscapeLeft:
  103. rotation = cocos2d::Device::Rotation::_90;
  104. break;
  105. case UIDeviceOrientationPortraitUpsideDown:
  106. rotation = cocos2d::Device::Rotation::_180;
  107. break;
  108. case UIDeviceOrientationLandscapeRight:
  109. rotation = cocos2d::Device::Rotation::_270;
  110. break;
  111. default:
  112. break;
  113. };
  114. if(_lastRotation != rotation){
  115. cocos2d::EventDispatcher::dispatchOrientationChangeEvent((int) rotation);
  116. _lastRotation = rotation;
  117. }
  118. CGRect bounds = [UIScreen mainScreen].bounds;
  119. float scale = [[UIScreen mainScreen] scale];
  120. float width = bounds.size.width * scale;
  121. float height = bounds.size.height * scale;
  122. cocos2d::Application::getInstance()->updateViewSize(width, height);
  123. }
  124. -(void) dealloc
  125. {
  126. [[NSNotificationCenter defaultCenter] removeObserver:self];
  127. [_displayLink release];
  128. [super dealloc];
  129. }
  130. - (void)appDidBecomeActive
  131. {
  132. _isAppActive = YES;
  133. }
  134. - (void)appDidBecomeInactive
  135. {
  136. _isAppActive = NO;
  137. }
  138. -(void) firstStart:(id) view
  139. {
  140. if ([view isReady])
  141. {
  142. auto scheduler = _application->getScheduler();
  143. scheduler->removeAllFunctionsToBePerformedInCocosThread();
  144. scheduler->unscheduleAll();
  145. se::ScriptEngine::getInstance()->cleanup();
  146. cocos2d::PoolManager::getInstance()->getCurrentPool()->clear();
  147. cocos2d::EventDispatcher::init();
  148. cocos2d::ccInvalidateStateCache();
  149. se::ScriptEngine* se = se::ScriptEngine::getInstance();
  150. se->addRegisterCallback(setCanvasCallback);
  151. if(!_application->applicationDidFinishLaunching())
  152. return;
  153. [self startMainLoop];
  154. }
  155. else
  156. [self performSelector:@selector(firstStart:) withObject:view afterDelay:0];
  157. }
  158. -(void) startMainLoop
  159. {
  160. [self stopMainLoop];
  161. _displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
  162. if (_systemVersion >= 10.0f)
  163. [_displayLink setPreferredFramesPerSecond: _fps];
  164. else
  165. [_displayLink setFrameInterval: 60 / _fps];
  166. [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  167. }
  168. -(void) stopMainLoop
  169. {
  170. if (_displayLink != nil)
  171. {
  172. [_displayLink invalidate];
  173. _displayLink = nil;
  174. }
  175. }
  176. -(void) setPreferredFPS:(int)fps
  177. {
  178. _fps = fps;
  179. [self startMainLoop];
  180. }
  181. -(void) doCaller: (id) sender
  182. {
  183. static std::chrono::steady_clock::time_point prevTime;
  184. static std::chrono::steady_clock::time_point now;
  185. static float dt = 0.f;
  186. if (_isAppActive)
  187. {
  188. EAGLContext* context = [(CCEAGLView*)(_application->getView()) getContext];
  189. if (context != [EAGLContext currentContext])
  190. {
  191. glFlush();
  192. }
  193. [EAGLContext setCurrentContext: context];
  194. prevTime = std::chrono::steady_clock::now();
  195. bool downsampleEnabled = _application->isDownsampleEnabled();
  196. if (downsampleEnabled)
  197. _application->getRenderTexture()->prepare();
  198. _scheduler->update(dt);
  199. cocos2d::EventDispatcher::dispatchTickEvent(dt);
  200. if (downsampleEnabled)
  201. _application->getRenderTexture()->draw();
  202. [(CCEAGLView*)(_application->getView()) swapBuffers];
  203. cocos2d::PoolManager::getInstance()->getCurrentPool()->clear();
  204. now = std::chrono::steady_clock::now();
  205. dt = std::chrono::duration_cast<std::chrono::microseconds>(now - prevTime).count() / 1000000.f;
  206. }
  207. }
  208. @end
  209. NS_CC_BEGIN
  210. Application* Application::_instance = nullptr;
  211. std::shared_ptr<Scheduler> Application::_scheduler = nullptr;
  212. Application::Application(const std::string& name, int width, int height)
  213. {
  214. Application::_instance = this;
  215. _scheduler = std::make_shared<Scheduler>();
  216. createView(name, width, height);
  217. Configuration::getInstance();
  218. glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_mainFBO);
  219. _renderTexture = new RenderTexture(width, height);
  220. se::ScriptEngine::getInstance();
  221. EventDispatcher::init();
  222. _delegate = [[MainLoop alloc] initWithApplication:this];
  223. updateViewSize(width, height);
  224. }
  225. Application::~Application()
  226. {
  227. #if USE_AUDIO
  228. AudioEngine::end();
  229. #endif
  230. EventDispatcher::destroy();
  231. se::ScriptEngine::destroyInstance();
  232. // stop main loop
  233. [(MainLoop*)_delegate stopMainLoop];
  234. [(MainLoop*)_delegate release];
  235. _delegate = nullptr;
  236. [(CCEAGLView*)_view release];
  237. _view = nullptr;
  238. delete _renderTexture;
  239. _renderTexture = nullptr;
  240. Application::_instance = nullptr;
  241. }
  242. const cocos2d::Vec2& Application::getViewSize() const
  243. {
  244. return _viewSize;
  245. }
  246. void Application::updateViewSize(int width, int height)
  247. {
  248. _viewSize.x = width;
  249. _viewSize.y = height;
  250. cocos2d::EventDispatcher::dispatchResizeEvent(width, height);
  251. }
  252. void Application::start()
  253. {
  254. if (_delegate)
  255. [(MainLoop*)_delegate performSelector:@selector(firstStart:) withObject:(CCEAGLView*)_view afterDelay:0];
  256. }
  257. void Application::restart()
  258. {
  259. if (_delegate) {
  260. [(MainLoop*)_delegate stopMainLoop];
  261. [(MainLoop*)_delegate performSelector:@selector(firstStart:) withObject:(CCEAGLView*)_view afterDelay:0];
  262. }
  263. }
  264. void Application::end()
  265. {
  266. delete this;
  267. exit(0);
  268. }
  269. void Application::setPreferredFramesPerSecond(int fps)
  270. {
  271. [(MainLoop*)_delegate setPreferredFPS: fps];
  272. }
  273. std::string Application::getCurrentLanguageCode() const
  274. {
  275. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  276. NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
  277. NSString *currentLanguage = [languages objectAtIndex:0];
  278. return [currentLanguage UTF8String];
  279. }
  280. bool Application::isDisplayStats() {
  281. se::AutoHandleScope hs;
  282. se::Value ret;
  283. char commandBuf[100] = "cc.debug.isDisplayStats();";
  284. se::ScriptEngine::getInstance()->evalString(commandBuf, 100, &ret);
  285. return ret.toBoolean();
  286. }
  287. void Application::setDisplayStats(bool isShow) {
  288. se::AutoHandleScope hs;
  289. char commandBuf[100] = {0};
  290. sprintf(commandBuf, "cc.debug.setDisplayStats(%s);", isShow ? "true" : "false");
  291. se::ScriptEngine::getInstance()->evalString(commandBuf);
  292. }
  293. Application::LanguageType Application::getCurrentLanguage() const
  294. {
  295. // get the current language and country config
  296. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  297. NSArray *languages = [defaults objectForKey:@"AppleLanguages"];
  298. NSString *currentLanguage = [languages objectAtIndex:0];
  299. // get the current language code.(such as English is "en", Chinese is "zh" and so on)
  300. NSDictionary* temp = [NSLocale componentsFromLocaleIdentifier:currentLanguage];
  301. NSString * languageCode = [temp objectForKey:NSLocaleLanguageCode];
  302. if ([languageCode isEqualToString:@"zh"]) return LanguageType::CHINESE;
  303. if ([languageCode isEqualToString:@"en"]) return LanguageType::ENGLISH;
  304. if ([languageCode isEqualToString:@"fr"]) return LanguageType::FRENCH;
  305. if ([languageCode isEqualToString:@"it"]) return LanguageType::ITALIAN;
  306. if ([languageCode isEqualToString:@"de"]) return LanguageType::GERMAN;
  307. if ([languageCode isEqualToString:@"es"]) return LanguageType::SPANISH;
  308. if ([languageCode isEqualToString:@"nl"]) return LanguageType::DUTCH;
  309. if ([languageCode isEqualToString:@"ru"]) return LanguageType::RUSSIAN;
  310. if ([languageCode isEqualToString:@"ko"]) return LanguageType::KOREAN;
  311. if ([languageCode isEqualToString:@"ja"]) return LanguageType::JAPANESE;
  312. if ([languageCode isEqualToString:@"hu"]) return LanguageType::HUNGARIAN;
  313. if ([languageCode isEqualToString:@"pt"]) return LanguageType::PORTUGUESE;
  314. if ([languageCode isEqualToString:@"ar"]) return LanguageType::ARABIC;
  315. if ([languageCode isEqualToString:@"nb"]) return LanguageType::NORWEGIAN;
  316. if ([languageCode isEqualToString:@"pl"]) return LanguageType::POLISH;
  317. if ([languageCode isEqualToString:@"tr"]) return LanguageType::TURKISH;
  318. if ([languageCode isEqualToString:@"uk"]) return LanguageType::UKRAINIAN;
  319. if ([languageCode isEqualToString:@"ro"]) return LanguageType::ROMANIAN;
  320. if ([languageCode isEqualToString:@"bg"]) return LanguageType::BULGARIAN;
  321. return LanguageType::ENGLISH;
  322. }
  323. Application::Platform Application::getPlatform() const
  324. {
  325. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) // idiom for iOS <= 3.2, otherwise: [UIDevice userInterfaceIdiom] is faster.
  326. return Platform::IPAD;
  327. else
  328. return Platform::IPHONE;
  329. }
  330. float Application::getScreenScale() const
  331. {
  332. return [(UIView*)_view contentScaleFactor];
  333. }
  334. GLint Application::getMainFBO() const
  335. {
  336. return _mainFBO;
  337. }
  338. bool Application::openURL(const std::string &url)
  339. {
  340. NSString* msg = [NSString stringWithCString:url.c_str() encoding:NSUTF8StringEncoding];
  341. NSURL* nsUrl = [NSURL URLWithString:msg];
  342. return [[UIApplication sharedApplication] openURL:nsUrl];
  343. }
  344. void Application::copyTextToClipboard(const std::string &text)
  345. {
  346. UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
  347. pasteboard.string = [NSString stringWithCString:text.c_str() encoding:NSUTF8StringEncoding];
  348. }
  349. bool Application::applicationDidFinishLaunching()
  350. {
  351. return true;
  352. }
  353. void Application::onPause()
  354. {
  355. }
  356. void Application::onResume()
  357. {
  358. }
  359. void Application::setMultitouch(bool value)
  360. {
  361. if (value != _multiTouch)
  362. {
  363. _multiTouch = value;
  364. if (_view)
  365. [(CCEAGLView*)_view setMultipleTouchEnabled:_multiTouch];
  366. }
  367. }
  368. void Application::onCreateView(PixelFormat& pixelformat, DepthFormat& depthFormat, int& multisamplingCount)
  369. {
  370. pixelformat = PixelFormat::RGB565;
  371. depthFormat = DepthFormat::DEPTH24_STENCIL8;
  372. multisamplingCount = 0;
  373. }
  374. namespace
  375. {
  376. GLenum depthFormatMap[] =
  377. {
  378. 0, // NONE: no depth and no stencil
  379. GL_DEPTH_COMPONENT24_OES, // DEPTH_COMPONENT16: unsupport, convert to GL_DEPTH_COMPONENT24_OES
  380. GL_DEPTH_COMPONENT24_OES, // DEPTH_COMPONENT24
  381. GL_DEPTH_COMPONENT24_OES, // DEPTH_COMPONENT32F: unsupport, convert to GL_DEPTH_COMPONENT24_OES
  382. GL_DEPTH24_STENCIL8_OES, // DEPTH24_STENCIL8
  383. GL_DEPTH24_STENCIL8_OES, // DEPTH32F_STENCIL8: unsupport, convert to GL_DEPTH24_STENCIL8_OES
  384. GL_DEPTH_STENCIL_OES // STENCIL_INDEX8
  385. };
  386. GLenum depthFormat2GLDepthFormat(cocos2d::Application::DepthFormat depthFormat)
  387. {
  388. return depthFormatMap[(int)depthFormat];
  389. }
  390. }
  391. void Application::createView(const std::string& /*name*/, int width, int height)
  392. {
  393. PixelFormat pixelFormat = PixelFormat::RGB565;
  394. DepthFormat depthFormat = DepthFormat::DEPTH24_STENCIL8;
  395. int multisamplingCount = 0;
  396. onCreateView(pixelFormat,
  397. depthFormat,
  398. multisamplingCount);
  399. CGRect bounds;
  400. bounds.origin.x = 0;
  401. bounds.origin.y = 0;
  402. bounds.size.width = width;
  403. bounds.size.height = height;
  404. //IDEA: iOS only support these pixel format?
  405. // - RGB565
  406. // - RGBA8
  407. NSString *pixelString = kEAGLColorFormatRGB565;
  408. if (PixelFormat::RGB565 != pixelFormat &&
  409. PixelFormat::RGBA8 != pixelFormat)
  410. NSLog(@"Unsupported pixel format is set, iOS only support RGB565 or RGBA8. Change to use RGB565");
  411. else if (PixelFormat::RGBA8 == pixelFormat)
  412. pixelString = kEAGLColorFormatRGBA8;
  413. // create view
  414. CCEAGLView *eaglView = [CCEAGLView viewWithFrame: bounds
  415. pixelFormat: pixelString
  416. depthFormat: depthFormat2GLDepthFormat(depthFormat)
  417. preserveBackbuffer: NO
  418. sharegroup: nil
  419. multiSampling: multisamplingCount != 0
  420. numberOfSamples: multisamplingCount];
  421. [eaglView setMultipleTouchEnabled:_multiTouch];
  422. [eaglView retain];
  423. _view = eaglView;
  424. }
  425. std::string Application::getSystemVersion()
  426. {
  427. NSString* systemVersion = [UIDevice currentDevice].systemVersion;
  428. return [systemVersion UTF8String];
  429. }
  430. NS_CC_END