ios_system.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. #include "ios_system.h"
  2. #import <Foundation/Foundation.h>
  3. #import <UIKit/UIKit.h>
  4. #include <iron_gpu.h>
  5. #include <iron_math.h>
  6. #include <iron_system.h>
  7. #include <mach/mach_time.h>
  8. #include <objc/runtime.h>
  9. #include <wchar.h>
  10. #define MAX_TOUCH_COUNT 10
  11. static char ios_title[1024];
  12. static void *touches[MAX_TOUCH_COUNT];
  13. static int backing_width;
  14. static int backing_height;
  15. static bool shift_down = false;
  16. static bool visible;
  17. static MyView *my_view;
  18. static MyViewController *my_view_controller;
  19. static bool keyboard_shown = false;
  20. static char language[3];
  21. static char sysid[512];
  22. static const char *video_formats[] = {"mp4", NULL};
  23. static void (*resize_callback)(int x, int y, void *data) = NULL;
  24. static void *resize_callback_data = NULL;
  25. void iron_internal_call_resize_callback(int width, int height) {
  26. if (resize_callback != NULL) {
  27. resize_callback(width, height, resize_callback_data);
  28. }
  29. }
  30. static int get_touch_index(void *touch) {
  31. for (int i = 0; i < MAX_TOUCH_COUNT; ++i) {
  32. if (touches[i] == touch) {
  33. return i;
  34. }
  35. }
  36. return -1;
  37. }
  38. static int add_touch(void *touch) {
  39. for (int i = 0; i < MAX_TOUCH_COUNT; ++i) {
  40. if (touches[i] == NULL) {
  41. touches[i] = touch;
  42. return i;
  43. }
  44. }
  45. return -1;
  46. }
  47. static int remove_touch(void *touch) {
  48. for (int i = 0; i < MAX_TOUCH_COUNT; ++i) {
  49. if (touches[i] == touch) {
  50. touches[i] = NULL;
  51. return i;
  52. }
  53. }
  54. return -1;
  55. }
  56. int iron_window_width() {
  57. return backing_width;
  58. }
  59. int iron_window_height() {
  60. return backing_height;
  61. }
  62. CAMetalLayer *get_metal_layer(void) {
  63. return [my_view metal_layer];
  64. }
  65. id get_metal_device(void) {
  66. return [my_view metal_device];
  67. }
  68. id get_metal_queue(void) {
  69. return [my_view metal_queue];
  70. }
  71. void iron_display_init(void) {}
  72. iron_display_mode_t iron_display_current_mode(int display) {
  73. iron_display_mode_t dm;
  74. dm.width = iron_window_width();
  75. dm.height = iron_window_height();
  76. dm.frequency = (int)[[UIScreen mainScreen] maximumFramesPerSecond];
  77. dm.bits_per_pixel = 32;
  78. return dm;
  79. }
  80. int iron_count_displays(void) {
  81. return 1;
  82. }
  83. int iron_primary_display(void) {
  84. return 0;
  85. }
  86. bool iron_mouse_can_lock(void) {
  87. return false;
  88. }
  89. void iron_mouse_show(void) {}
  90. void iron_mouse_hide(void) {}
  91. void iron_mouse_set_position(int x, int y) {}
  92. void iron_mouse_get_position(int *x, int *y) {}
  93. void iron_mouse_set_cursor(iron_cursor_t cursor_index) {}
  94. bool with_autoreleasepool(bool (*f)(void)) {
  95. @autoreleasepool {
  96. return f();
  97. }
  98. }
  99. const char *iron_get_resource_path(void) {
  100. return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:1];
  101. }
  102. bool iron_internal_handle_messages(void) {
  103. SInt32 result;
  104. do {
  105. result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE);
  106. } while (result == kCFRunLoopRunHandledSource);
  107. return true;
  108. }
  109. void iron_set_keep_screen_on(bool on) {}
  110. void iron_keyboard_show(void) {
  111. keyboard_shown = true;
  112. [my_view show_keyboard];
  113. }
  114. void iron_keyboard_hide(void) {
  115. keyboard_shown = false;
  116. [my_view hide_keyboard];
  117. }
  118. bool iron_keyboard_active(void) {
  119. return keyboard_shown;
  120. }
  121. void iron_load_url(const char *url) {
  122. NSURL *nsURL = [NSURL URLWithString:[NSString stringWithUTF8String:url]];
  123. [[UIApplication sharedApplication] openURL:nsURL options:@{} completionHandler:nil];
  124. }
  125. const char *iron_language(void) {
  126. NSString *nsstr = [[NSLocale preferredLanguages] objectAtIndex:0];
  127. const char *lang = [nsstr UTF8String];
  128. language[0] = lang[0];
  129. language[1] = lang[1];
  130. language[2] = 0;
  131. return language;
  132. }
  133. void iron_internal_shutdown(void) {}
  134. void iron_init(iron_window_options_t *win) {
  135. gpu_init(win->depth_bits, true);
  136. }
  137. const char *iron_system_id(void) {
  138. const char *name = [[[UIDevice currentDevice] name] UTF8String];
  139. const char *vendorId = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] UTF8String];
  140. strcpy(sysid, name);
  141. strcat(sysid, "-");
  142. strcat(sysid, vendorId);
  143. return sysid;
  144. }
  145. const char *iron_internal_save_path(void) {
  146. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  147. NSString *resolvedPath = [paths objectAtIndex:0];
  148. NSString *appName = [NSString stringWithUTF8String:iron_application_name()];
  149. resolvedPath = [resolvedPath stringByAppendingPathComponent:appName];
  150. NSFileManager *fileMgr = [[NSFileManager alloc] init];
  151. NSError *error;
  152. [fileMgr createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];
  153. resolvedPath = [resolvedPath stringByAppendingString:@"/"];
  154. return [resolvedPath cStringUsingEncoding:1];
  155. }
  156. const char **iron_video_formats(void) {
  157. return video_formats;
  158. }
  159. double iron_frequency(void) {
  160. mach_timebase_info_data_t info;
  161. mach_timebase_info(&info);
  162. return (double)info.denom / (double)info.numer / 1e-9;
  163. }
  164. uint64_t iron_timestamp(void) {
  165. uint64_t time = mach_absolute_time();
  166. return time;
  167. }
  168. int main(int argc, char *argv[]) {
  169. int res = 0;
  170. @autoreleasepool {
  171. [IronSceneDelegate description]; // otherwise removed by the linker
  172. res = UIApplicationMain(argc, argv, nil, nil);
  173. }
  174. return res;
  175. }
  176. int iron_window_x() {
  177. return 0;
  178. }
  179. int iron_window_y() {
  180. return 0;
  181. }
  182. void iron_window_resize(int width, int height) {}
  183. void iron_window_move(int x, int y) {}
  184. void iron_window_change_mode(iron_window_mode_t mode) {}
  185. void iron_window_destroy() {}
  186. void iron_window_show() {}
  187. void iron_window_hide() {}
  188. void iron_window_create(iron_window_options_t *win) {}
  189. void iron_window_set_title(const char *title) {
  190. strcpy(ios_title, title);
  191. }
  192. void iron_window_set_resize_callback(void (*callback)(int x, int y, void *data), void *data) {
  193. resize_callback = callback;
  194. resize_callback_data = data;
  195. }
  196. void iron_window_set_close_callback(bool (*callback)(void *), void *data) {}
  197. iron_window_mode_t iron_window_get_mode() {
  198. return IRON_WINDOW_MODE_FULLSCREEN;
  199. }
  200. int iron_window_display() {
  201. return 0;
  202. }
  203. @implementation MyView
  204. + (Class)layerClass {
  205. return [CAMetalLayer class];
  206. }
  207. - (void)hoverGesture:(UIHoverGestureRecognizer *)recognizer {
  208. CGPoint point = [recognizer locationInView:self];
  209. float x = point.x * self.contentScaleFactor;
  210. float y = point.y * self.contentScaleFactor;
  211. iron_internal_pen_trigger_move(x, y, 0.0); // Pencil hover
  212. }
  213. - (id)initWithFrame:(CGRect)frame {
  214. self = [super initWithFrame:(CGRect)frame];
  215. self.contentScaleFactor = [UIScreen mainScreen].scale;
  216. backing_width = frame.size.width * self.contentScaleFactor;
  217. backing_height = frame.size.height * self.contentScaleFactor;
  218. for (int i = 0; i < MAX_TOUCH_COUNT; ++i) {
  219. touches[i] = NULL;
  220. }
  221. device = MTLCreateSystemDefaultDevice();
  222. queue = [device newCommandQueue];
  223. library = [device newDefaultLibrary];
  224. CAMetalLayer *layer = (CAMetalLayer *)self.layer;
  225. layer.device = device;
  226. layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
  227. layer.framebufferOnly = YES;
  228. layer.opaque = YES;
  229. layer.backgroundColor = nil;
  230. [self addGestureRecognizer:[[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hoverGesture:)]];
  231. return self;
  232. }
  233. - (void)layoutSubviews {
  234. backing_width = self.frame.size.width * self.contentScaleFactor;
  235. backing_height = self.frame.size.height * self.contentScaleFactor;
  236. gpu_resize(backing_width, backing_height);
  237. iron_internal_call_resize_callback(backing_width, backing_height);
  238. }
  239. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  240. for (UITouch *touch in touches) {
  241. int index = get_touch_index((__bridge void *)touch);
  242. if (index == -1) {
  243. index = add_touch((__bridge void *)touch);
  244. }
  245. if (index >= 0) {
  246. CGPoint point = [touch locationInView:self];
  247. float x = point.x * self.contentScaleFactor;
  248. float y = point.y * self.contentScaleFactor;
  249. if (index == 0) {
  250. iron_internal_mouse_trigger_press(event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y);
  251. }
  252. iron_internal_surface_trigger_touch_start(index, x, y);
  253. if (touch.type == UITouchTypePencil) {
  254. iron_internal_pen_trigger_press(x, y, 0.0);
  255. }
  256. }
  257. }
  258. }
  259. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  260. for (UITouch *touch in touches) {
  261. int index = get_touch_index((__bridge void *)touch);
  262. if (index >= 0) {
  263. CGPoint point = [touch locationInView:self];
  264. float x = point.x * self.contentScaleFactor;
  265. float y = point.y * self.contentScaleFactor;
  266. if (index == 0) {
  267. iron_internal_mouse_trigger_move(x, y);
  268. }
  269. iron_internal_surface_trigger_move(index, x, y);
  270. }
  271. }
  272. }
  273. - (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches {
  274. for (UITouch *touch in touches) {
  275. if (touch.type == UITouchTypePencil) {
  276. CGPoint point = [touch locationInView:self];
  277. float x = point.x * self.contentScaleFactor;
  278. float y = point.y * self.contentScaleFactor;
  279. iron_internal_pen_trigger_move(x, y, touch.force);
  280. }
  281. }
  282. }
  283. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  284. for (UITouch *touch in touches) {
  285. int index = remove_touch((__bridge void *)touch);
  286. if (index >= 0) {
  287. CGPoint point = [touch locationInView:self];
  288. float x = point.x * self.contentScaleFactor;
  289. float y = point.y * self.contentScaleFactor;
  290. if (index == 0) {
  291. iron_internal_mouse_trigger_release(event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y);
  292. }
  293. iron_internal_surface_trigger_touch_end(index, x, y);
  294. if (touch.type == UITouchTypePencil) {
  295. iron_internal_pen_trigger_release(x, y, 0.0);
  296. }
  297. }
  298. }
  299. }
  300. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  301. for (UITouch *touch in touches) {
  302. int index = remove_touch((__bridge void *)touch);
  303. if (index >= 0) {
  304. CGPoint point = [touch locationInView:self];
  305. float x = point.x * self.contentScaleFactor;
  306. float y = point.y * self.contentScaleFactor;
  307. if (index == 0) {
  308. iron_internal_mouse_trigger_release(event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y);
  309. }
  310. iron_internal_surface_trigger_touch_end(index, x, y);
  311. if (touch.type == UITouchTypePencil) {
  312. iron_internal_pen_trigger_release(x, y, 0.0);
  313. }
  314. }
  315. }
  316. }
  317. - (void)show_keyboard {
  318. [self becomeFirstResponder];
  319. }
  320. - (void)hide_keyboard {
  321. [self resignFirstResponder];
  322. }
  323. - (BOOL)hasText {
  324. return YES;
  325. }
  326. - (void)insertText:(NSString *)text {
  327. if ([text length] == 1) {
  328. unichar ch = [text characterAtIndex:[text length] - 1];
  329. if (ch == 8212) {
  330. ch = '_';
  331. }
  332. if (ch == L'\n') {
  333. iron_internal_keyboard_trigger_key_down(IRON_KEY_RETURN);
  334. iron_internal_keyboard_trigger_key_up(IRON_KEY_RETURN);
  335. return;
  336. }
  337. if (ch == L'.') {
  338. iron_internal_keyboard_trigger_key_down(IRON_KEY_PERIOD);
  339. iron_internal_keyboard_trigger_key_up(IRON_KEY_PERIOD);
  340. }
  341. else if (ch == L'%') {
  342. iron_internal_keyboard_trigger_key_down(IRON_KEY_PERCENT);
  343. iron_internal_keyboard_trigger_key_up(IRON_KEY_PERCENT);
  344. }
  345. else if (ch == L'(') {
  346. iron_internal_keyboard_trigger_key_down(IRON_KEY_OPEN_PAREN);
  347. iron_internal_keyboard_trigger_key_up(IRON_KEY_OPEN_PAREN);
  348. }
  349. else if (ch == L'&') {
  350. iron_internal_keyboard_trigger_key_down(IRON_KEY_AMPERSAND);
  351. iron_internal_keyboard_trigger_key_up(IRON_KEY_AMPERSAND);
  352. }
  353. else if (ch == L'$') {
  354. iron_internal_keyboard_trigger_key_down(IRON_KEY_DOLLAR);
  355. iron_internal_keyboard_trigger_key_up(IRON_KEY_DOLLAR);
  356. }
  357. else if (ch == L'#') {
  358. iron_internal_keyboard_trigger_key_down(IRON_KEY_HASH);
  359. iron_internal_keyboard_trigger_key_up(IRON_KEY_HASH);
  360. }
  361. else if (ch >= L'a' && ch <= L'z') {
  362. if (shift_down) {
  363. iron_internal_keyboard_trigger_key_up(IRON_KEY_SHIFT);
  364. shift_down = false;
  365. }
  366. iron_internal_keyboard_trigger_key_down(ch + IRON_KEY_A - L'a');
  367. iron_internal_keyboard_trigger_key_up(ch + IRON_KEY_A - L'a');
  368. }
  369. else {
  370. if (!shift_down) {
  371. iron_internal_keyboard_trigger_key_down(IRON_KEY_SHIFT);
  372. shift_down = true;
  373. }
  374. iron_internal_keyboard_trigger_key_down(ch + IRON_KEY_A - L'A');
  375. iron_internal_keyboard_trigger_key_up(ch + IRON_KEY_A - L'A');
  376. }
  377. iron_internal_keyboard_trigger_key_press(ch);
  378. }
  379. }
  380. - (void)deleteBackward {
  381. iron_internal_keyboard_trigger_key_down(IRON_KEY_BACKSPACE);
  382. iron_internal_keyboard_trigger_key_up(IRON_KEY_BACKSPACE);
  383. }
  384. - (BOOL)canBecomeFirstResponder {
  385. return YES;
  386. }
  387. - (void)onKeyboardHide:(NSNotification *)notification {
  388. iron_keyboard_hide();
  389. }
  390. - (CAMetalLayer *)metal_layer {
  391. return (CAMetalLayer *)self.layer;
  392. }
  393. - (id<MTLDevice>)metal_device {
  394. return device;
  395. }
  396. - (id<MTLCommandQueue>)metal_queue {
  397. return queue;
  398. }
  399. @end
  400. @implementation MyViewController
  401. - (void)loadView {
  402. visible = true;
  403. self.view = my_view = [[MyView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  404. [self.view addInteraction:[[UIDropInteraction alloc] initWithDelegate:self]];
  405. [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
  406. }
  407. - (void)setVisible:(BOOL)value {
  408. visible = value;
  409. }
  410. void importFile(NSURL *url) {
  411. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  412. NSString *folderName = [NSString stringWithUTF8String:ios_title];
  413. NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:folderName];
  414. if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
  415. [[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:NO attributes:nil error:nil];
  416. }
  417. NSString *suggestedName = url.path.lastPathComponent;
  418. filePath = [filePath stringByAppendingPathComponent:suggestedName];
  419. CFURLRef cfurl = (__bridge CFURLRef)url;
  420. CFURLStartAccessingSecurityScopedResource(cfurl);
  421. [[NSFileManager defaultManager] copyItemAtPath:url.path toPath:filePath error:nil];
  422. CFURLStopAccessingSecurityScopedResource(cfurl);
  423. const char *cpath = [filePath cStringUsingEncoding:NSUTF8StringEncoding];
  424. iron_internal_drop_files_callback(cpath);
  425. }
  426. - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {
  427. for (NSURL *url in urls) {
  428. importFile(url);
  429. }
  430. }
  431. - (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session {
  432. CGPoint point = [session locationInView:self.view];
  433. float x = point.x * my_view.contentScaleFactor;
  434. float y = point.y * my_view.contentScaleFactor;
  435. iron_internal_mouse_trigger_move(x, y);
  436. iron_internal_surface_trigger_move(0, x, y);
  437. for (UIDragItem *item in session.items) {
  438. [item.itemProvider loadInPlaceFileRepresentationForTypeIdentifier:item.itemProvider.registeredTypeIdentifiers[0]
  439. completionHandler:^(NSURL *_Nullable url, BOOL isInPlace, NSError *_Nullable error) {
  440. importFile(url);
  441. }];
  442. }
  443. }
  444. - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session {
  445. return [[UIDropProposal alloc] initWithDropOperation:UIDropOperationCopy];
  446. }
  447. - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
  448. return UIRectEdgeAll;
  449. }
  450. @end
  451. @implementation IronSceneDelegate
  452. - (void)mainLoop {
  453. @autoreleasepool {
  454. kickstart(0, NULL);
  455. }
  456. }
  457. - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
  458. #ifdef IRON_A2
  459. AVAudioSession *sessionInstance = [AVAudioSession sharedInstance];
  460. NSError *error;
  461. NSString *category = AVAudioSessionCategoryAmbient;
  462. [sessionInstance setCategory:category error:&error];
  463. #endif
  464. UIWindowScene *windowScene = (UIWindowScene *)scene;
  465. self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
  466. self.window.frame = windowScene.coordinateSpace.bounds;
  467. [self.window setBackgroundColor:[UIColor blackColor]];
  468. my_view_controller = [[MyViewController alloc] init];
  469. my_view_controller.view.multipleTouchEnabled = YES;
  470. [self.window setRootViewController:my_view_controller];
  471. [self.window makeKeyAndVisible];
  472. [my_view_controller setVisible:YES];
  473. [self performSelectorOnMainThread:@selector(mainLoop) withObject:nil waitUntilDone:NO];
  474. }
  475. - (void)sceneDidDisconnect:(UIScene *)scene {
  476. iron_internal_shutdown_callback();
  477. }
  478. - (void)sceneDidBecomeActive:(UIScene *)scene {
  479. iron_internal_resume_callback();
  480. }
  481. - (void)sceneWillResignActive:(UIScene *)scene {
  482. iron_internal_pause_callback();
  483. }
  484. - (void)sceneWillEnterForeground:(UIScene *)scene {
  485. iron_internal_foreground_callback();
  486. }
  487. - (void)sceneDidEnterBackground:(UIScene *)scene {
  488. iron_internal_background_callback();
  489. }
  490. @end
  491. #ifdef WITH_GAMEPAD
  492. const char *iron_gamepad_vendor(int gamepad) {
  493. return "nobody";
  494. }
  495. const char *iron_gamepad_product_name(int gamepad) {
  496. return "none";
  497. }
  498. bool iron_gamepad_connected(int num) {
  499. return true;
  500. }
  501. void iron_gamepad_rumble(int gamepad, float left, float right) {}
  502. #endif
  503. bool _save_and_quit_callback_internal() {
  504. return false;
  505. }
  506. volatile int iron_exec_async_done = 1;
  507. void iron_exec_async(const char *path, char *argv[]) {
  508. }