EAGLView.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /******************************************************************************/
  2. #include "iOS.h"
  3. /******************************************************************************/
  4. Bool DontRemoveThisOrEAGLViewClassWontBeLinked;
  5. /******************************************************************************/
  6. namespace EE{
  7. /******************************************************************************/
  8. void (*ResizeAdPtr)();
  9. static const Bool iOS_9_0=(Compare(OSVerNumber(), VecI4(9, 0, 0, 0))>=0); // if iOS 9.0 available
  10. static KB_KEY KeyMap[128];
  11. static inline void SetKeyMap(Char c, KB_KEY key) {U16 u=c; if(InRange(u, KeyMap))KeyMap[u]=key;}
  12. static void InitKeyMap()
  13. {
  14. SetKeyMap('a', KB_A);
  15. SetKeyMap('A', KB_A);
  16. SetKeyMap('b', KB_B);
  17. SetKeyMap('B', KB_B);
  18. SetKeyMap('c', KB_C);
  19. SetKeyMap('C', KB_C);
  20. SetKeyMap('d', KB_D);
  21. SetKeyMap('D', KB_D);
  22. SetKeyMap('e', KB_E);
  23. SetKeyMap('E', KB_E);
  24. SetKeyMap('f', KB_F);
  25. SetKeyMap('F', KB_F);
  26. SetKeyMap('g', KB_G);
  27. SetKeyMap('G', KB_G);
  28. SetKeyMap('h', KB_H);
  29. SetKeyMap('H', KB_H);
  30. SetKeyMap('i', KB_I);
  31. SetKeyMap('I', KB_I);
  32. SetKeyMap('j', KB_J);
  33. SetKeyMap('J', KB_J);
  34. SetKeyMap('k', KB_K);
  35. SetKeyMap('K', KB_K);
  36. SetKeyMap('l', KB_L);
  37. SetKeyMap('L', KB_L);
  38. SetKeyMap('m', KB_M);
  39. SetKeyMap('M', KB_M);
  40. SetKeyMap('n', KB_N);
  41. SetKeyMap('N', KB_N);
  42. SetKeyMap('o', KB_O);
  43. SetKeyMap('O', KB_O);
  44. SetKeyMap('p', KB_P);
  45. SetKeyMap('P', KB_P);
  46. SetKeyMap('q', KB_Q);
  47. SetKeyMap('Q', KB_Q);
  48. SetKeyMap('r', KB_R);
  49. SetKeyMap('R', KB_R);
  50. SetKeyMap('s', KB_S);
  51. SetKeyMap('S', KB_S);
  52. SetKeyMap('t', KB_T);
  53. SetKeyMap('T', KB_T);
  54. SetKeyMap('u', KB_U);
  55. SetKeyMap('U', KB_U);
  56. SetKeyMap('v', KB_V);
  57. SetKeyMap('V', KB_V);
  58. SetKeyMap('w', KB_W);
  59. SetKeyMap('W', KB_W);
  60. SetKeyMap('x', KB_X);
  61. SetKeyMap('X', KB_X);
  62. SetKeyMap('y', KB_Y);
  63. SetKeyMap('Y', KB_Y);
  64. SetKeyMap('z', KB_Z);
  65. SetKeyMap('Z', KB_Z);
  66. SetKeyMap('1', KB_1);
  67. SetKeyMap('!', KB_1);
  68. SetKeyMap('2', KB_2);
  69. SetKeyMap('@', KB_2);
  70. SetKeyMap('3', KB_3);
  71. SetKeyMap('#', KB_3);
  72. SetKeyMap('4', KB_4);
  73. SetKeyMap('$', KB_4);
  74. SetKeyMap('5', KB_5);
  75. SetKeyMap('%', KB_5);
  76. SetKeyMap('6', KB_6);
  77. SetKeyMap('^', KB_6);
  78. SetKeyMap('7', KB_7);
  79. SetKeyMap('&', KB_7);
  80. SetKeyMap('8', KB_8);
  81. SetKeyMap('*', KB_8);
  82. SetKeyMap('9', KB_9);
  83. SetKeyMap('(', KB_9);
  84. SetKeyMap('0', KB_0);
  85. SetKeyMap(')', KB_0);
  86. SetKeyMap('-' , KB_SUB );
  87. SetKeyMap('_' , KB_SUB );
  88. SetKeyMap('=' , KB_EQUAL );
  89. SetKeyMap('+' , KB_EQUAL );
  90. SetKeyMap('[' , KB_LBRACKET );
  91. SetKeyMap('{' , KB_LBRACKET );
  92. SetKeyMap(']' , KB_RBRACKET );
  93. SetKeyMap('}' , KB_RBRACKET );
  94. SetKeyMap(';' , KB_SEMICOLON );
  95. SetKeyMap(':' , KB_SEMICOLON );
  96. SetKeyMap('\'', KB_APOSTROPHE);
  97. SetKeyMap('"' , KB_APOSTROPHE);
  98. SetKeyMap(',' , KB_COMMA );
  99. SetKeyMap('<' , KB_COMMA );
  100. SetKeyMap('.' , KB_DOT );
  101. SetKeyMap('>' , KB_DOT );
  102. SetKeyMap('/' , KB_SLASH );
  103. SetKeyMap('?' , KB_SLASH );
  104. SetKeyMap('\\', KB_BACKSLASH );
  105. SetKeyMap('|' , KB_BACKSLASH );
  106. SetKeyMap('`' , KB_TILDE );
  107. SetKeyMap('~' , KB_TILDE );
  108. SetKeyMap('\n', KB_ENTER);
  109. SetKeyMap('\t', KB_TAB );
  110. }
  111. /******************************************************************************/
  112. EAGLView* GetUIView()
  113. {
  114. return ViewController ? (EAGLView*)ViewController.view : null;
  115. }
  116. /******************************************************************************/
  117. } // namespace EE
  118. /******************************************************************************/
  119. @implementation EAGLView
  120. /******************************************************************************/
  121. +(Class)layerClass
  122. {
  123. return [CAEAGLLayer class];
  124. }
  125. /******************************************************************************/
  126. -(id)initWithCoder:(NSCoder*)coder
  127. {
  128. if(self=[super initWithCoder:coder])
  129. {
  130. // Get the layer
  131. CAEAGLLayer *layer=(CAEAGLLayer*)self.layer;
  132. layer.opaque=true;
  133. layer.drawableProperties=[NSDictionary dictionaryWithObjectsAndKeys:
  134. [NSNumber numberWithBool:false], kEAGLDrawablePropertyRetainedBacking, // discard framebuffer contents after drawing frame, for better performance
  135. kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, // use RGBA8 format
  136. nil];
  137. initialized=false;
  138. force_touch=false;
  139. keyboard_visible=false;
  140. display_link=nil;
  141. InitKeyMap();
  142. self.multipleTouchEnabled=true;
  143. self.contentScaleFactor=ScreenScale;
  144. // setup notifications for the keyboard
  145. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown :) name:UIKeyboardDidShowNotification object:nil];
  146. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil];
  147. }
  148. return self;
  149. }
  150. /******************************************************************************/
  151. -(void)keyboardWasShown:(NSNotification*)aNotification
  152. {
  153. Kb._visible=true;
  154. NSDictionary *info=[aNotification userInfo];
  155. CGRect rect=[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
  156. RectI recti(Round(rect.origin.x*ScreenScale), Round(rect.origin.y*ScreenScale), Round((rect.origin.x+rect.size.width)*ScreenScale), Round((rect.origin.y+rect.size.height)*ScreenScale));
  157. switch([UIApplication sharedApplication].statusBarOrientation) // don't use 'App.orientation' as it's one frame ahead/behind than this call
  158. {
  159. default : Kb._recti=recti; break;
  160. case UIInterfaceOrientationPortraitUpsideDown: Kb._recti.set(recti.min.x, D.resH()-recti.max.y, recti.max.x, D.resH()-recti.min.y); break;
  161. case UIInterfaceOrientationLandscapeLeft : Kb._recti.set(recti.min.y, recti.min.x, recti.max.y, recti.max.x); break;
  162. case UIInterfaceOrientationLandscapeRight : Kb._recti.set(recti.min.y, D.resH()-recti.max.x, recti.max.y, D.resH()-recti.min.x); break;
  163. }
  164. }
  165. -(void)keyboardWillBeHidden:(NSNotification*)aNotification {Kb._visible=false;}
  166. /******************************************************************************/
  167. -(void)detectForceTouch
  168. {
  169. force_touch=(iOS_9_0 && [[self traitCollection] forceTouchCapability]==UIForceTouchCapabilityAvailable); // API available on iOS 9.0+ ONLY
  170. }
  171. -(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
  172. {
  173. [super traitCollectionDidChange:previousTraitCollection];
  174. [self detectForceTouch];
  175. }
  176. /******************************************************************************/
  177. -(void)layoutSubviews // this is called when the layer is initialized, resized or a sub view is added (like ads)
  178. {
  179. if(App._closed)return; // do nothing if app called 'Exit'
  180. // always proceed, because issues may appear when screen is rotated while full screen ad is displayed, and then we go back to the application
  181. if(!initialized)
  182. {
  183. initialized=true;
  184. [self detectForceTouch];
  185. if(!App.create())Exit("Failed to initialize the application"); // something failed
  186. [self setUpdate];
  187. }else
  188. {
  189. D._res.zero(); D.modeSet(1, 1); // clear current res to force setting mode according to newly detected resolution, for now use dummy values, actual values will be detected later
  190. }
  191. if(ResizeAdPtr)ResizeAdPtr(); // re-position banner after having everything, call this from a pointer to function (and not directly to the advertisement codes) to avoid force linking to advertisement (which then links to google admob lib, which uses 'advertisingIdentifier' which is prohibited when ads are not displayed in iOS, also linking to admob increases the app binary size)
  192. }
  193. /******************************************************************************/
  194. -(void)update:(id)sender
  195. {
  196. if(App._close)
  197. {
  198. App.del(); // manually call shut down
  199. ExitNow(); // force exit as iOS does not offer a clean way to do it
  200. }
  201. if(App.active())
  202. {
  203. Bool kb_visible =Kb.visibleWanted();
  204. if( kb_visible!=keyboard_visible || Kb._refresh_visible){if(keyboard_visible=kb_visible)[self becomeFirstResponder];else [self resignFirstResponder]; Kb._refresh_visible=false;}
  205. App.update();
  206. }
  207. }
  208. /******************************************************************************/
  209. -(void)setUpdate
  210. {
  211. SyncLocker locker(D._lock); // thread safety because this method can be called by 'Exit'
  212. bool run =(initialized && App.active() && !App._closed); // don't run if app called 'Exit'
  213. if( run!=(display_link!=null))
  214. {
  215. if(run)
  216. {
  217. display_link=[CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
  218. //display_link.preferredFramesPerSecond=..; this is not needed because default value is 0, which is max possible frame rate - https://developer.apple.com/documentation/quartzcore/cadisplaylink/1648421-preferredframespersecond?language=objc
  219. [display_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  220. }else
  221. {
  222. [display_link invalidate];
  223. display_link=nil;
  224. }
  225. }
  226. }
  227. /******************************************************************************/
  228. -(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
  229. {
  230. for(UITouch *touch in touches)
  231. {
  232. CGPoint pos=[touch locationInView:self];
  233. Vec2 p(pos.x, pos.y); p*=ScreenScale;
  234. VecI2 pi=Round(p); p=D.windowPixelToScreen(p);
  235. Touch *t=FindTouchByHandle(touch); // find existing one
  236. if(!t) // create new one
  237. {
  238. t=&Touches.New().init(pi, p, touch, iOS_9_0 ? touch.type==UITouchTypeStylus : false); // API available on iOS 9.0+ ONLY
  239. }else
  240. {
  241. t->_deltai+=pi-t->_posi;
  242. t->_posi =pi;
  243. t->_pos =p;
  244. }
  245. Int taps =[touch tapCount]; // it starts from 1
  246. t->_first=(taps&1); // it's a first click on odd numbers, 1, 3, 5, ..
  247. t->_state=BS_ON|BS_PUSHED;
  248. if(!t->_first)t->_state|=BS_DOUBLE; // set double clicking only on even numbers, 2, 4, 6, ..
  249. t->_force=(force_touch ? touch.force : 1); // API available for 'force_touch' ONLY
  250. }
  251. }
  252. -(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
  253. {
  254. for(UITouch *touch in touches)
  255. {
  256. CGPoint pos=[touch locationInView:self];
  257. Vec2 p(pos.x, pos.y); p*=ScreenScale;
  258. VecI2 pi=Round(p); p=D.windowPixelToScreen(p);
  259. Touch *t=FindTouchByHandle(touch); // find existing one
  260. if(!t) // create new one
  261. {
  262. t=&Touches.New().init(pi, p, touch, iOS_9_0 ? touch.type==UITouchTypeStylus : false); // API available on iOS 9.0+ ONLY
  263. t->_state=BS_ON|BS_PUSHED;
  264. t->_force=1;
  265. }
  266. t->_deltai+=pi-t->_posi;
  267. t->_posi =pi;
  268. t->_pos =p;
  269. if(force_touch)t->_force=touch.force; // API available for 'force_touch' ONLY
  270. }
  271. }
  272. -(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
  273. {
  274. for(UITouch *touch in touches)if(Touch *t=FindTouchByHandle(touch))
  275. {
  276. CGPoint pos=[touch locationInView:self];
  277. Vec2 p(pos.x, pos.y); p*=ScreenScale;
  278. VecI2 pi=Round(p); p=D.windowPixelToScreen(p);
  279. t->_deltai+=pi-t->_posi;
  280. t->_posi =pi;
  281. t->_pos =p;
  282. t->_remove =true;
  283. if(t->_state&BS_ON) // check for state in case it was manually eaten
  284. {
  285. t->_state|= BS_RELEASED;
  286. t->_state&=~BS_ON;
  287. }
  288. }
  289. }
  290. -(void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
  291. {
  292. [self touchesEnded:touches withEvent:event];
  293. }
  294. /******************************************************************************/
  295. -(void)insertText:(NSString*)text
  296. {
  297. Str s=text; FREPA(s)
  298. {
  299. Char c=s[i]; Kb.queue(c, -1);
  300. //U16 u=c ; if(InRange(u, KeyMap)){KB_KEY k=KeyMap[u]; Kb.push(k); Kb.release(k);}
  301. }
  302. }
  303. -(void)deleteBackward
  304. {
  305. Kb.push (KB_BACK, -1);
  306. Kb.release(KB_BACK);
  307. }
  308. -(BOOL)hasText {return true;}
  309. -(BOOL)canBecomeFirstResponder {return true;}
  310. /******************************************************************************/
  311. @end
  312. /******************************************************************************/