ios_system.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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(iron_cursor_t 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. NSURL *nsURL = [NSURL URLWithString:[NSString stringWithUTF8String:url]];
  125. [[UIApplication sharedApplication] openURL:nsURL options:@{} completionHandler:nil];
  126. }
  127. const char *iron_language(void) {
  128. NSString *nsstr = [[NSLocale preferredLanguages] objectAtIndex:0];
  129. const char *lang = [nsstr UTF8String];
  130. language[0] = lang[0];
  131. language[1] = lang[1];
  132. language[2] = 0;
  133. return language;
  134. }
  135. void iron_internal_shutdown(void) {}
  136. void iron_init(iron_window_options_t *win) {
  137. gpu_init(win->depth_bits, true);
  138. }
  139. const char *iron_system_id(void) {
  140. const char *name = [[[UIDevice currentDevice] name] UTF8String];
  141. const char *vendorId = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] UTF8String];
  142. strcpy(sysid, name);
  143. strcat(sysid, "-");
  144. strcat(sysid, vendorId);
  145. return sysid;
  146. }
  147. const char *iron_internal_save_path(void) {
  148. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  149. NSString *resolvedPath = [paths objectAtIndex:0];
  150. NSString *appName = [NSString stringWithUTF8String:iron_application_name()];
  151. resolvedPath = [resolvedPath stringByAppendingPathComponent:appName];
  152. NSFileManager *fileMgr = [[NSFileManager alloc] init];
  153. NSError *error;
  154. [fileMgr createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];
  155. resolvedPath = [resolvedPath stringByAppendingString:@"/"];
  156. return [resolvedPath cStringUsingEncoding:1];
  157. }
  158. const char **iron_video_formats(void) {
  159. return video_formats;
  160. }
  161. double iron_frequency(void) {
  162. mach_timebase_info_data_t info;
  163. mach_timebase_info(&info);
  164. return (double)info.denom / (double)info.numer / 1e-9;
  165. }
  166. uint64_t iron_timestamp(void) {
  167. uint64_t time = mach_absolute_time();
  168. return time;
  169. }
  170. int main(int argc, char *argv[]) {
  171. int res = 0;
  172. @autoreleasepool {
  173. [IronSceneDelegate description]; // otherwise removed by the linker
  174. res = UIApplicationMain(argc, argv, nil, nil);
  175. }
  176. return res;
  177. }
  178. int iron_window_x() {
  179. return 0;
  180. }
  181. int iron_window_y() {
  182. return 0;
  183. }
  184. void iron_window_resize(int width, int height) {}
  185. void iron_window_move(int x, int y) {}
  186. void iron_window_change_mode(iron_window_mode_t mode) {}
  187. void iron_window_destroy() {}
  188. void iron_window_show() {}
  189. void iron_window_hide() {}
  190. void iron_window_set_title(const char *title) {}
  191. void iron_window_create(iron_window_options_t *win) {}
  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:mobile_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. wchar_t *wpath = (wchar_t *)[filePath cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
  424. iron_internal_drop_files_callback(wpath);
  425. }
  426. - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
  427. // wchar_t *filePath = (wchar_t *)[url.path cStringUsingEncoding:NSUTF32LittleEndianStringEncoding];
  428. // CFURLRef cfurl = (__bridge CFURLRef)url;
  429. // CFURLStartAccessingSecurityScopedResource(cfurl);
  430. // iron_internal_drop_files_callback(filePath);
  431. // CFURLStopAccessingSecurityScopedResource(cfurl);
  432. importFile(url);
  433. }
  434. - (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id<UIDropSession>)session {
  435. CGPoint point = [session locationInView:self.view];
  436. float x = point.x * my_view.contentScaleFactor;
  437. float y = point.y * my_view.contentScaleFactor;
  438. iron_internal_mouse_trigger_move(x, y);
  439. iron_internal_surface_trigger_move(0, x, y);
  440. for (UIDragItem *item in session.items) {
  441. [item.itemProvider loadInPlaceFileRepresentationForTypeIdentifier:item.itemProvider.registeredTypeIdentifiers[0] completionHandler:^(NSURL * _Nullable url, BOOL isInPlace, NSError * _Nullable error) {
  442. importFile(url);
  443. }];
  444. }
  445. }
  446. - (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id<UIDropSession>)session {
  447. return [[UIDropProposal alloc] initWithDropOperation: UIDropOperationCopy];
  448. }
  449. - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
  450. return UIRectEdgeAll;
  451. }
  452. @end
  453. @implementation IronSceneDelegate
  454. - (void)mainLoop {
  455. @autoreleasepool {
  456. kickstart(0, NULL);
  457. }
  458. }
  459. - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
  460. #ifdef IRON_A2
  461. AVAudioSession *sessionInstance = [AVAudioSession sharedInstance];
  462. NSError *error;
  463. NSString *category = AVAudioSessionCategoryAmbient;
  464. [sessionInstance setCategory:category error:&error];
  465. #endif
  466. UIWindowScene *windowScene = (UIWindowScene *)scene;
  467. self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
  468. self.window.frame = windowScene.coordinateSpace.bounds;
  469. [self.window setBackgroundColor:[UIColor blackColor]];
  470. my_view_controller = [[MyViewController alloc] init];
  471. my_view_controller.view.multipleTouchEnabled = YES;
  472. [self.window setRootViewController:my_view_controller];
  473. [self.window makeKeyAndVisible];
  474. [my_view_controller setVisible:YES];
  475. [self performSelectorOnMainThread:@selector(mainLoop) withObject:nil waitUntilDone:NO];
  476. }
  477. - (void)sceneDidDisconnect:(UIScene *)scene {
  478. iron_internal_shutdown_callback();
  479. }
  480. - (void)sceneDidBecomeActive:(UIScene *)scene {
  481. iron_internal_resume_callback();
  482. }
  483. - (void)sceneWillResignActive:(UIScene *)scene {
  484. iron_internal_pause_callback();
  485. }
  486. - (void)sceneWillEnterForeground:(UIScene *)scene {
  487. iron_internal_foreground_callback();
  488. }
  489. - (void)sceneDidEnterBackground:(UIScene *)scene {
  490. iron_internal_background_callback();
  491. }
  492. @end
  493. #ifdef WITH_GAMEPAD
  494. const char *iron_gamepad_vendor(int gamepad) {
  495. return "nobody";
  496. }
  497. const char *iron_gamepad_product_name(int gamepad) {
  498. return "none";
  499. }
  500. bool iron_gamepad_connected(int num) {
  501. return true;
  502. }
  503. void iron_gamepad_rumble(int gamepad, float left, float right) {}
  504. #endif