os_ios.m 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. // +build darwin,ios
  3. @import UIKit;
  4. #include <stdint.h>
  5. #include "_cgo_export.h"
  6. #include "framework_ios.h"
  7. __attribute__ ((visibility ("hidden"))) Class gio_layerClass(void);
  8. @interface GioView: UIView <UIKeyInput>
  9. @property uintptr_t handle;
  10. @end
  11. @implementation GioViewController
  12. CGFloat _keyboardHeight;
  13. - (void)loadView {
  14. gio_runMain();
  15. CGRect zeroFrame = CGRectMake(0, 0, 0, 0);
  16. self.view = [[UIView alloc] initWithFrame:zeroFrame];
  17. self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
  18. UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame];
  19. [self.view addSubview: drawView];
  20. #if !TARGET_OS_TV
  21. drawView.multipleTouchEnabled = YES;
  22. #endif
  23. drawView.preservesSuperviewLayoutMargins = YES;
  24. drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
  25. onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self);
  26. #if !TARGET_OS_TV
  27. [[NSNotificationCenter defaultCenter] addObserver:self
  28. selector:@selector(keyboardWillChange:)
  29. name:UIKeyboardWillShowNotification
  30. object:nil];
  31. [[NSNotificationCenter defaultCenter] addObserver:self
  32. selector:@selector(keyboardWillChange:)
  33. name:UIKeyboardWillChangeFrameNotification
  34. object:nil];
  35. [[NSNotificationCenter defaultCenter] addObserver:self
  36. selector:@selector(keyboardWillHide:)
  37. name:UIKeyboardWillHideNotification
  38. object:nil];
  39. #endif
  40. [[NSNotificationCenter defaultCenter] addObserver: self
  41. selector: @selector(applicationDidEnterBackground:)
  42. name: UIApplicationDidEnterBackgroundNotification
  43. object: nil];
  44. [[NSNotificationCenter defaultCenter] addObserver: self
  45. selector: @selector(applicationWillEnterForeground:)
  46. name: UIApplicationWillEnterForegroundNotification
  47. object: nil];
  48. }
  49. - (void)applicationWillEnterForeground:(UIApplication *)application {
  50. GioView *view = (GioView *)self.view.subviews[0];
  51. if (view != nil) {
  52. onStart(view.handle);
  53. }
  54. }
  55. - (void)applicationDidEnterBackground:(UIApplication *)application {
  56. GioView *view = (GioView *)self.view.subviews[0];
  57. if (view != nil) {
  58. onStop(view.handle);
  59. }
  60. }
  61. - (void)viewDidDisappear:(BOOL)animated {
  62. [super viewDidDisappear:animated];
  63. GioView *view = (GioView *)self.view.subviews[0];
  64. onDestroy(view.handle);
  65. }
  66. - (void)viewDidLayoutSubviews {
  67. [super viewDidLayoutSubviews];
  68. GioView *view = (GioView *)self.view.subviews[0];
  69. CGRect frame = self.view.bounds;
  70. // Adjust view bounds to make room for the keyboard.
  71. frame.size.height -= _keyboardHeight;
  72. view.frame = frame;
  73. gio_onDraw(view.handle);
  74. }
  75. - (void)didReceiveMemoryWarning {
  76. onLowMemory();
  77. [super didReceiveMemoryWarning];
  78. }
  79. #if !TARGET_OS_TV
  80. - (void)keyboardWillChange:(NSNotification *)note {
  81. NSDictionary *userInfo = note.userInfo;
  82. CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
  83. _keyboardHeight = f.size.height;
  84. [self.view setNeedsLayout];
  85. }
  86. - (void)keyboardWillHide:(NSNotification *)note {
  87. _keyboardHeight = 0.0;
  88. [self.view setNeedsLayout];
  89. }
  90. #endif
  91. @end
  92. static void handleTouches(int last, GioView *view, NSSet<UITouch *> *touches, UIEvent *event) {
  93. CGFloat scale = view.contentScaleFactor;
  94. NSUInteger i = 0;
  95. NSUInteger n = [touches count];
  96. for (UITouch *touch in touches) {
  97. CFTypeRef touchRef = (__bridge CFTypeRef)touch;
  98. i++;
  99. NSArray<UITouch *> *coalescedTouches = [event coalescedTouchesForTouch:touch];
  100. NSUInteger j = 0;
  101. NSUInteger m = [coalescedTouches count];
  102. for (UITouch *coalescedTouch in [event coalescedTouchesForTouch:touch]) {
  103. CGPoint loc = [coalescedTouch locationInView:view];
  104. j++;
  105. int lastTouch = last && i == n && j == m;
  106. onTouch(view.handle, lastTouch, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]);
  107. }
  108. }
  109. }
  110. @implementation GioView
  111. NSArray<UIKeyCommand *> *_keyCommands;
  112. + (void)onFrameCallback:(CADisplayLink *)link {
  113. gio_onFrameCallback((__bridge CFTypeRef)link);
  114. }
  115. + (Class)layerClass {
  116. return gio_layerClass();
  117. }
  118. - (void)willMoveToWindow:(UIWindow *)newWindow {
  119. if (self.window != nil) {
  120. [[NSNotificationCenter defaultCenter] removeObserver:self
  121. name:UIWindowDidBecomeKeyNotification
  122. object:self.window];
  123. [[NSNotificationCenter defaultCenter] removeObserver:self
  124. name:UIWindowDidResignKeyNotification
  125. object:self.window];
  126. }
  127. self.contentScaleFactor = newWindow.screen.nativeScale;
  128. [[NSNotificationCenter defaultCenter] addObserver:self
  129. selector:@selector(onWindowDidBecomeKey:)
  130. name:UIWindowDidBecomeKeyNotification
  131. object:newWindow];
  132. [[NSNotificationCenter defaultCenter] addObserver:self
  133. selector:@selector(onWindowDidResignKey:)
  134. name:UIWindowDidResignKeyNotification
  135. object:newWindow];
  136. }
  137. - (void)onWindowDidBecomeKey:(NSNotification *)note {
  138. if (self.isFirstResponder) {
  139. onFocus(self.handle, YES);
  140. }
  141. }
  142. - (void)onWindowDidResignKey:(NSNotification *)note {
  143. if (self.isFirstResponder) {
  144. onFocus(self.handle, NO);
  145. }
  146. }
  147. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  148. handleTouches(0, self, touches, event);
  149. }
  150. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  151. handleTouches(0, self, touches, event);
  152. }
  153. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  154. handleTouches(1, self, touches, event);
  155. }
  156. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  157. handleTouches(1, self, touches, event);
  158. }
  159. - (void)insertText:(NSString *)text {
  160. onText(self.handle, (__bridge CFTypeRef)text);
  161. }
  162. - (BOOL)canBecomeFirstResponder {
  163. return YES;
  164. }
  165. - (BOOL)hasText {
  166. return YES;
  167. }
  168. - (void)deleteBackward {
  169. onDeleteBackward(self.handle);
  170. }
  171. - (void)onUpArrow {
  172. onUpArrow(self.handle);
  173. }
  174. - (void)onDownArrow {
  175. onDownArrow(self.handle);
  176. }
  177. - (void)onLeftArrow {
  178. onLeftArrow(self.handle);
  179. }
  180. - (void)onRightArrow {
  181. onRightArrow(self.handle);
  182. }
  183. - (NSArray<UIKeyCommand *> *)keyCommands {
  184. if (_keyCommands == nil) {
  185. _keyCommands = @[
  186. [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
  187. modifierFlags:0
  188. action:@selector(onUpArrow)],
  189. [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow
  190. modifierFlags:0
  191. action:@selector(onDownArrow)],
  192. [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
  193. modifierFlags:0
  194. action:@selector(onLeftArrow)],
  195. [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
  196. modifierFlags:0
  197. action:@selector(onRightArrow)]
  198. ];
  199. }
  200. return _keyCommands;
  201. }
  202. @end
  203. CFTypeRef gio_createDisplayLink(void) {
  204. CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)];
  205. dl.paused = YES;
  206. NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
  207. [dl addToRunLoop:runLoop forMode:[runLoop currentMode]];
  208. return (__bridge_retained CFTypeRef)dl;
  209. }
  210. int gio_startDisplayLink(CFTypeRef dlref) {
  211. CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
  212. dl.paused = NO;
  213. return 0;
  214. }
  215. int gio_stopDisplayLink(CFTypeRef dlref) {
  216. CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
  217. dl.paused = YES;
  218. return 0;
  219. }
  220. void gio_releaseDisplayLink(CFTypeRef dlref) {
  221. CADisplayLink *dl = (__bridge CADisplayLink *)dlref;
  222. [dl invalidate];
  223. CFRelease(dlref);
  224. }
  225. void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) {
  226. // Nothing to do on iOS.
  227. }
  228. void gio_hideCursor() {
  229. // Not supported.
  230. }
  231. void gio_showCursor() {
  232. // Not supported.
  233. }
  234. void gio_setCursor(NSUInteger curID) {
  235. // Not supported.
  236. }
  237. void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
  238. GioView *v = (__bridge GioView *)viewRef;
  239. v.handle = handle;
  240. }
  241. @interface _gioAppDelegate : UIResponder <UIApplicationDelegate>
  242. @property (strong, nonatomic) UIWindow *window;
  243. @end
  244. @implementation _gioAppDelegate
  245. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  246. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  247. GioViewController *controller = [[GioViewController alloc] initWithNibName:nil bundle:nil];
  248. self.window.rootViewController = controller;
  249. [self.window makeKeyAndVisible];
  250. return YES;
  251. }
  252. @end
  253. int gio_applicationMain(int argc, char *argv[]) {
  254. @autoreleasepool {
  255. return UIApplicationMain(argc, argv, nil, NSStringFromClass([_gioAppDelegate class]));
  256. }
  257. }