ios_system.m 16 KB

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