macos_system.m 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. #import "macos_system.h"
  2. #import <Cocoa/Cocoa.h>
  3. #include <iron_gpu.h>
  4. #include <iron_math.h>
  5. #include <iron_system.h>
  6. #include <iron_video.h>
  7. #include <mach/mach_time.h>
  8. #include <objc/runtime.h>
  9. #include <stdbool.h>
  10. struct WindowData {
  11. id handle;
  12. id view;
  13. bool fullscreen;
  14. void (*resizeCallback)(int width, int height, void *data);
  15. void *resizeCallbackData;
  16. bool (*closeCallback)(void *data);
  17. void *closeCallbackData;
  18. };
  19. static struct WindowData windows[1] = {0};
  20. static bool controlKeyMouseButton = false;
  21. static int mouseX, mouseY;
  22. static bool keyboardShown = false;
  23. static const char *videoFormats[] = {"ogv", NULL};
  24. static NSApplication *myapp;
  25. static NSWindow *window;
  26. static BasicMTKView *view;
  27. static char language[3];
  28. static int current_cursor_index = 0;
  29. @implementation BasicMTKView
  30. static bool shift = false;
  31. static bool ctrl = false;
  32. static bool alt = false;
  33. static bool cmd = false;
  34. - (void)flagsChanged:(NSEvent *)theEvent {
  35. if (shift) {
  36. iron_internal_keyboard_trigger_key_up(IRON_KEY_SHIFT);
  37. shift = false;
  38. }
  39. if (ctrl) {
  40. iron_internal_keyboard_trigger_key_up(IRON_KEY_CONTROL);
  41. ctrl = false;
  42. }
  43. if (alt) {
  44. iron_internal_keyboard_trigger_key_up(IRON_KEY_ALT);
  45. alt = false;
  46. }
  47. if (cmd) {
  48. iron_internal_keyboard_trigger_key_up(IRON_KEY_META);
  49. cmd = false;
  50. }
  51. if ([theEvent modifierFlags] & NSShiftKeyMask) {
  52. iron_internal_keyboard_trigger_key_down(IRON_KEY_SHIFT);
  53. shift = true;
  54. }
  55. if ([theEvent modifierFlags] & NSControlKeyMask) {
  56. iron_internal_keyboard_trigger_key_down(IRON_KEY_CONTROL);
  57. ctrl = true;
  58. }
  59. if ([theEvent modifierFlags] & NSAlternateKeyMask) {
  60. iron_internal_keyboard_trigger_key_down(IRON_KEY_ALT);
  61. alt = true;
  62. }
  63. if ([theEvent modifierFlags] & NSCommandKeyMask) {
  64. iron_internal_keyboard_trigger_key_down(IRON_KEY_META);
  65. cmd = true;
  66. }
  67. }
  68. - (void)keyDown:(NSEvent *)theEvent {
  69. if ([theEvent isARepeat])
  70. return;
  71. NSString *characters = [theEvent charactersIgnoringModifiers];
  72. if ([characters length]) {
  73. unichar ch = [characters characterAtIndex:0];
  74. switch (ch) { // keys that exist in keydown and keypress events
  75. case 59:
  76. iron_internal_keyboard_trigger_key_down(IRON_KEY_SEMICOLON);
  77. break;
  78. case 91:
  79. iron_internal_keyboard_trigger_key_down(IRON_KEY_OPEN_BRACKET);
  80. break;
  81. case 93:
  82. iron_internal_keyboard_trigger_key_down(IRON_KEY_CLOSE_BRACKET);
  83. break;
  84. case 39:
  85. iron_internal_keyboard_trigger_key_down(IRON_KEY_QUOTE);
  86. break;
  87. case 92:
  88. iron_internal_keyboard_trigger_key_down(IRON_KEY_BACK_SLASH);
  89. break;
  90. case 44:
  91. iron_internal_keyboard_trigger_key_down(IRON_KEY_COMMA);
  92. break;
  93. case 46:
  94. iron_internal_keyboard_trigger_key_down(IRON_KEY_PERIOD);
  95. break;
  96. case 47:
  97. iron_internal_keyboard_trigger_key_down(IRON_KEY_SLASH);
  98. break;
  99. case 96:
  100. iron_internal_keyboard_trigger_key_down(IRON_KEY_BACK_QUOTE);
  101. break;
  102. case 32:
  103. iron_internal_keyboard_trigger_key_down(IRON_KEY_SPACE);
  104. break;
  105. case 34:
  106. iron_internal_keyboard_trigger_key_down(IRON_KEY_DOUBLE_QUOTE);
  107. break;
  108. case 40:
  109. iron_internal_keyboard_trigger_key_down(IRON_KEY_OPEN_PAREN);
  110. break;
  111. case 41:
  112. iron_internal_keyboard_trigger_key_down(IRON_KEY_CLOSE_PAREN);
  113. break;
  114. case 42:
  115. iron_internal_keyboard_trigger_key_down(IRON_KEY_ASTERISK);
  116. break;
  117. case 43:
  118. iron_internal_keyboard_trigger_key_down(IRON_KEY_PLUS);
  119. break;
  120. case 45:
  121. iron_internal_keyboard_trigger_key_down(IRON_KEY_HYPHEN_MINUS);
  122. break;
  123. case 61:
  124. iron_internal_keyboard_trigger_key_down(IRON_KEY_EQUALS);
  125. break;
  126. case 95:
  127. iron_internal_keyboard_trigger_key_down(IRON_KEY_UNDERSCORE);
  128. break;
  129. }
  130. switch (ch) {
  131. case NSRightArrowFunctionKey:
  132. iron_internal_keyboard_trigger_key_down(IRON_KEY_RIGHT);
  133. break;
  134. case NSLeftArrowFunctionKey:
  135. iron_internal_keyboard_trigger_key_down(IRON_KEY_LEFT);
  136. break;
  137. case NSUpArrowFunctionKey:
  138. iron_internal_keyboard_trigger_key_down(IRON_KEY_UP);
  139. break;
  140. case NSDownArrowFunctionKey:
  141. iron_internal_keyboard_trigger_key_down(IRON_KEY_DOWN);
  142. break;
  143. case 27:
  144. iron_internal_keyboard_trigger_key_down(IRON_KEY_ESCAPE);
  145. break;
  146. case NSEnterCharacter:
  147. case NSNewlineCharacter:
  148. case NSCarriageReturnCharacter:
  149. iron_internal_keyboard_trigger_key_down(IRON_KEY_RETURN);
  150. iron_internal_keyboard_trigger_key_press('\n');
  151. break;
  152. case 0x7f:
  153. iron_internal_keyboard_trigger_key_down(IRON_KEY_BACKSPACE);
  154. iron_internal_keyboard_trigger_key_press('\x08');
  155. break;
  156. case 9:
  157. iron_internal_keyboard_trigger_key_down(IRON_KEY_TAB);
  158. iron_internal_keyboard_trigger_key_press('\t');
  159. break;
  160. default:
  161. if (ch == 'x' && [theEvent modifierFlags] & NSCommandKeyMask) {
  162. char *text = iron_internal_cut_callback();
  163. if (text != NULL) {
  164. NSPasteboard *board = [NSPasteboard generalPasteboard];
  165. [board clearContents];
  166. [board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType];
  167. }
  168. }
  169. if (ch == 'c' && [theEvent modifierFlags] & NSCommandKeyMask) {
  170. char *text = iron_internal_copy_callback();
  171. if (text != NULL) {
  172. iron_copy_to_clipboard(text);
  173. }
  174. }
  175. if (ch == 'v' && [theEvent modifierFlags] & NSCommandKeyMask) {
  176. NSPasteboard *board = [NSPasteboard generalPasteboard];
  177. NSString *data = [board stringForType:NSStringPboardType];
  178. if (data != nil) {
  179. char charData[4096];
  180. strcpy(charData, [data UTF8String]);
  181. iron_internal_paste_callback(charData);
  182. }
  183. }
  184. if (ch >= L'a' && ch <= L'z') {
  185. iron_internal_keyboard_trigger_key_down(ch - L'a' + IRON_KEY_A);
  186. }
  187. else if (ch >= L'A' && ch <= L'Z') {
  188. iron_internal_keyboard_trigger_key_down(ch - L'A' + IRON_KEY_A);
  189. }
  190. else if (ch >= L'0' && ch <= L'9') {
  191. iron_internal_keyboard_trigger_key_down(ch - L'0' + IRON_KEY_0);
  192. }
  193. iron_internal_keyboard_trigger_key_press(ch);
  194. break;
  195. }
  196. }
  197. }
  198. - (void)keyUp:(NSEvent *)theEvent {
  199. NSString *characters = [theEvent charactersIgnoringModifiers];
  200. if ([characters length]) {
  201. unichar ch = [characters characterAtIndex:0];
  202. switch (ch) {
  203. case 59:
  204. iron_internal_keyboard_trigger_key_up(IRON_KEY_SEMICOLON);
  205. break;
  206. case 91:
  207. iron_internal_keyboard_trigger_key_up(IRON_KEY_OPEN_BRACKET);
  208. break;
  209. case 93:
  210. iron_internal_keyboard_trigger_key_up(IRON_KEY_CLOSE_BRACKET);
  211. break;
  212. case 39:
  213. iron_internal_keyboard_trigger_key_up(IRON_KEY_QUOTE);
  214. break;
  215. case 92:
  216. iron_internal_keyboard_trigger_key_up(IRON_KEY_BACK_SLASH);
  217. break;
  218. case 44:
  219. iron_internal_keyboard_trigger_key_up(IRON_KEY_COMMA);
  220. break;
  221. case 46:
  222. iron_internal_keyboard_trigger_key_up(IRON_KEY_PERIOD);
  223. break;
  224. case 47:
  225. iron_internal_keyboard_trigger_key_up(IRON_KEY_SLASH);
  226. break;
  227. case 96:
  228. iron_internal_keyboard_trigger_key_up(IRON_KEY_BACK_QUOTE);
  229. break;
  230. case 45:
  231. iron_internal_keyboard_trigger_key_up(IRON_KEY_HYPHEN_MINUS);
  232. break;
  233. case 61:
  234. iron_internal_keyboard_trigger_key_up(IRON_KEY_EQUALS);
  235. break;
  236. case NSRightArrowFunctionKey:
  237. iron_internal_keyboard_trigger_key_up(IRON_KEY_RIGHT);
  238. break;
  239. case NSLeftArrowFunctionKey:
  240. iron_internal_keyboard_trigger_key_up(IRON_KEY_LEFT);
  241. break;
  242. case NSUpArrowFunctionKey:
  243. iron_internal_keyboard_trigger_key_up(IRON_KEY_UP);
  244. break;
  245. case NSDownArrowFunctionKey:
  246. iron_internal_keyboard_trigger_key_up(IRON_KEY_DOWN);
  247. break;
  248. case 27:
  249. iron_internal_keyboard_trigger_key_up(IRON_KEY_ESCAPE);
  250. break;
  251. case NSEnterCharacter:
  252. case NSNewlineCharacter:
  253. case NSCarriageReturnCharacter:
  254. iron_internal_keyboard_trigger_key_up(IRON_KEY_RETURN);
  255. break;
  256. case 0x7f:
  257. iron_internal_keyboard_trigger_key_up(IRON_KEY_BACKSPACE);
  258. break;
  259. case 9:
  260. iron_internal_keyboard_trigger_key_up(IRON_KEY_TAB);
  261. break;
  262. case 32:
  263. iron_internal_keyboard_trigger_key_up(IRON_KEY_SPACE);
  264. break;
  265. case 34:
  266. iron_internal_keyboard_trigger_key_up(IRON_KEY_DOUBLE_QUOTE);
  267. break;
  268. case 40:
  269. iron_internal_keyboard_trigger_key_up(IRON_KEY_OPEN_PAREN);
  270. break;
  271. case 41:
  272. iron_internal_keyboard_trigger_key_up(IRON_KEY_CLOSE_PAREN);
  273. break;
  274. case 42:
  275. iron_internal_keyboard_trigger_key_up(IRON_KEY_ASTERISK);
  276. break;
  277. case 43:
  278. iron_internal_keyboard_trigger_key_up(IRON_KEY_PLUS);
  279. break;
  280. case 95:
  281. iron_internal_keyboard_trigger_key_up(IRON_KEY_UNDERSCORE);
  282. break;
  283. default:
  284. if (ch >= L'a' && ch <= L'z') {
  285. iron_internal_keyboard_trigger_key_up(ch - L'a' + IRON_KEY_A);
  286. }
  287. else if (ch >= L'A' && ch <= L'Z') {
  288. iron_internal_keyboard_trigger_key_up(ch - L'A' + IRON_KEY_A);
  289. }
  290. else if (ch >= L'0' && ch <= L'9') {
  291. iron_internal_keyboard_trigger_key_up(ch - L'0' + IRON_KEY_0);
  292. }
  293. break;
  294. }
  295. }
  296. }
  297. static int getMouseX(NSEvent *event) {
  298. NSWindow *window = [[NSApplication sharedApplication] mainWindow];
  299. float scale = [window backingScaleFactor];
  300. return (int)([event locationInWindow].x * scale);
  301. }
  302. static int getMouseY(NSEvent *event) {
  303. NSWindow *window = [[NSApplication sharedApplication] mainWindow];
  304. float scale = [window backingScaleFactor];
  305. return (int)(iron_window_height() - [event locationInWindow].y * scale);
  306. }
  307. - (void)mouseDown:(NSEvent *)theEvent {
  308. if ([theEvent modifierFlags] & NSControlKeyMask) {
  309. controlKeyMouseButton = true;
  310. iron_internal_mouse_trigger_press(1, getMouseX(theEvent), getMouseY(theEvent));
  311. }
  312. else {
  313. controlKeyMouseButton = false;
  314. iron_internal_mouse_trigger_press(0, getMouseX(theEvent), getMouseY(theEvent));
  315. }
  316. if ([theEvent subtype] == NSTabletPointEventSubtype) {
  317. iron_internal_pen_trigger_press(getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure);
  318. }
  319. }
  320. - (void)mouseUp:(NSEvent *)theEvent {
  321. if (controlKeyMouseButton) {
  322. iron_internal_mouse_trigger_release(1, getMouseX(theEvent), getMouseY(theEvent));
  323. }
  324. else {
  325. iron_internal_mouse_trigger_release(0, getMouseX(theEvent), getMouseY(theEvent));
  326. }
  327. controlKeyMouseButton = false;
  328. if ([theEvent subtype] == NSTabletPointEventSubtype) {
  329. iron_internal_pen_trigger_release(getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure);
  330. }
  331. }
  332. - (void)mouseMoved:(NSEvent *)theEvent {
  333. iron_internal_mouse_trigger_move(getMouseX(theEvent), getMouseY(theEvent));
  334. }
  335. - (void)mouseDragged:(NSEvent *)theEvent {
  336. iron_internal_mouse_trigger_move(getMouseX(theEvent), getMouseY(theEvent));
  337. if ([theEvent subtype] == NSTabletPointEventSubtype) {
  338. iron_internal_pen_trigger_move(getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure);
  339. }
  340. }
  341. - (void)rightMouseDown:(NSEvent *)theEvent {
  342. iron_internal_mouse_trigger_press(1, getMouseX(theEvent), getMouseY(theEvent));
  343. }
  344. - (void)rightMouseUp:(NSEvent *)theEvent {
  345. iron_internal_mouse_trigger_release(1, getMouseX(theEvent), getMouseY(theEvent));
  346. }
  347. - (void)rightMouseDragged:(NSEvent *)theEvent {
  348. iron_internal_mouse_trigger_move(getMouseX(theEvent), getMouseY(theEvent));
  349. }
  350. - (void)otherMouseDown:(NSEvent *)theEvent {
  351. iron_internal_mouse_trigger_press(2, getMouseX(theEvent), getMouseY(theEvent));
  352. }
  353. - (void)otherMouseUp:(NSEvent *)theEvent {
  354. iron_internal_mouse_trigger_release(2, getMouseX(theEvent), getMouseY(theEvent));
  355. }
  356. - (void)otherMouseDragged:(NSEvent *)theEvent {
  357. iron_internal_mouse_trigger_move(getMouseX(theEvent), getMouseY(theEvent));
  358. }
  359. - (void)scrollWheel:(NSEvent *)theEvent {
  360. float delta = [theEvent deltaY];
  361. iron_internal_mouse_trigger_scroll(-delta);
  362. }
  363. - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  364. NSPasteboard *pboard = [sender draggingPasteboard];
  365. NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
  366. if ([[pboard types] containsObject:NSURLPboardType]) {
  367. if (sourceDragMask & NSDragOperationLink) {
  368. return NSDragOperationLink;
  369. }
  370. }
  371. return NSDragOperationNone;
  372. }
  373. - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  374. NSPasteboard *pboard = [sender draggingPasteboard];
  375. if ([[pboard types] containsObject:NSURLPboardType]) {
  376. NSArray *urls = [pboard readObjectsForClasses:@[ [NSURL class] ] options:nil];
  377. for (NSURL *fileURL in urls) {
  378. const char *filePath = [fileURL.path cStringUsingEncoding:NSUTF8StringEncoding];
  379. iron_internal_drop_files_callback(filePath);
  380. }
  381. }
  382. return YES;
  383. }
  384. - (id)initWithFrame:(NSRect)frameRect {
  385. self = [super initWithFrame:frameRect];
  386. device = MTLCreateSystemDefaultDevice();
  387. commandQueue = [device newCommandQueue];
  388. library = [device newDefaultLibrary];
  389. CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
  390. metalLayer.device = device;
  391. metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
  392. metalLayer.framebufferOnly = YES;
  393. metalLayer.opaque = YES;
  394. metalLayer.backgroundColor = nil;
  395. return self;
  396. }
  397. - (BOOL)acceptsFirstResponder {
  398. return YES;
  399. }
  400. - (BOOL)becomeFirstResponder {
  401. return YES;
  402. }
  403. - (BOOL)resignFirstResponder {
  404. return YES;
  405. }
  406. - (void)resize:(NSSize)size {
  407. [self setFrameSize:size];
  408. }
  409. - (CAMetalLayer *)metalLayer {
  410. return (CAMetalLayer *)self.layer;
  411. }
  412. - (id<MTLDevice>)metalDevice {
  413. return device;
  414. }
  415. - (id<MTLCommandQueue>)metalQueue {
  416. return commandQueue;
  417. }
  418. @end
  419. void iron_copy_to_clipboard(const char *text) {
  420. NSPasteboard *board = [NSPasteboard generalPasteboard];
  421. [board clearContents];
  422. [board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType];
  423. }
  424. int iron_count_displays(void) {
  425. NSArray *screens = [NSScreen screens];
  426. return (int)[screens count];
  427. }
  428. int iron_primary_display(void) {
  429. NSArray *screens = [NSScreen screens];
  430. NSScreen *mainScreen = [NSScreen mainScreen];
  431. int max_displays = 8;
  432. for (int i = 0; i < max_displays; ++i) {
  433. if (mainScreen == screens[i]) {
  434. return i;
  435. }
  436. }
  437. return -1;
  438. }
  439. void iron_display_init(void) {}
  440. iron_display_mode_t iron_display_current_mode(int display) {
  441. NSArray *screens = [NSScreen screens];
  442. NSScreen *screen = screens[display];
  443. NSRect screenRect = [screen frame];
  444. iron_display_mode_t dm;
  445. dm.width = screenRect.size.width;
  446. dm.height = screenRect.size.height;
  447. dm.frequency = 60;
  448. dm.bits_per_pixel = 32;
  449. NSDictionary *description = [screen deviceDescription];
  450. NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
  451. NSNumber *screenNumber = [description objectForKey:@"NSScreenNumber"];
  452. CGSize displayPhysicalSize = CGDisplayScreenSize([screenNumber unsignedIntValue]); // in millimeters
  453. double ppi = displayPixelSize.width / (displayPhysicalSize.width * 0.039370); // Convert MM to INCH
  454. dm.pixels_per_inch = round(ppi);
  455. return dm;
  456. }
  457. bool iron_mouse_can_lock(void) {
  458. return true;
  459. }
  460. void iron_mouse_show(void) {
  461. CGDisplayShowCursor(kCGDirectMainDisplay);
  462. }
  463. void iron_mouse_hide(void) {
  464. CGDisplayHideCursor(kCGDirectMainDisplay);
  465. }
  466. void iron_mouse_set_position(int x, int y) {
  467. NSWindow *window = windows[0].handle;
  468. float scale = [window backingScaleFactor];
  469. NSRect rect = [[window contentView] bounds];
  470. NSPoint windowpoint = NSMakePoint(x / scale, rect.size.height - y / scale);
  471. NSPoint screenpoint = [window convertPointToScreen:windowpoint];
  472. CGPoint cgpoint = CGPointMake(screenpoint.x, [[NSScreen mainScreen] frame].size.height - screenpoint.y);
  473. CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
  474. CGAssociateMouseAndMouseCursorPosition(true);
  475. }
  476. void iron_mouse_get_position(int *x, int *y) {
  477. NSWindow *window = windows[0].handle;
  478. float scale = [window backingScaleFactor];
  479. NSRect rect = [[window contentView] bounds];
  480. NSPoint point = [window mouseLocationOutsideOfEventStream];
  481. *x = (int)(point.x * scale);
  482. *y = (int)((rect.size.height - point.y) * scale);
  483. }
  484. void iron_mouse_set_cursor(iron_cursor_t cursor_index) {
  485. if (current_cursor_index == cursor_index) {
  486. return;
  487. }
  488. current_cursor_index = cursor_index;
  489. if (cursor_index == IRON_CURSOR_HAND) {
  490. [[NSCursor pointingHandCursor] set];
  491. }
  492. else if (cursor_index == IRON_CURSOR_IBEAM) {
  493. [[NSCursor IBeamCursor] set];
  494. }
  495. else if (cursor_index == IRON_CURSOR_SIZEWE) {
  496. [[NSCursor resizeLeftRightCursor] set];
  497. }
  498. else if (cursor_index == IRON_CURSOR_SIZENS) {
  499. [[NSCursor resizeUpDownCursor] set];
  500. }
  501. else {
  502. [[NSCursor arrowCursor] set];
  503. }
  504. }
  505. void iron_keyboard_show(void) {
  506. keyboardShown = true;
  507. }
  508. void iron_keyboard_hide(void) {
  509. keyboardShown = false;
  510. }
  511. bool iron_keyboard_active(void) {
  512. return keyboardShown;
  513. }
  514. const char *iron_system_id(void) {
  515. return "macOS";
  516. }
  517. const char **iron_video_formats(void) {
  518. return videoFormats;
  519. }
  520. void iron_set_keep_screen_on(bool on) {}
  521. double iron_frequency(void) {
  522. mach_timebase_info_data_t info;
  523. mach_timebase_info(&info);
  524. return (double)info.denom / (double)info.numer / 1e-9;
  525. }
  526. uint64_t iron_timestamp(void) {
  527. return mach_absolute_time();
  528. }
  529. bool with_autoreleasepool(bool (*f)(void)) {
  530. @autoreleasepool {
  531. return f();
  532. }
  533. }
  534. const char *iron_get_resource_path(void) {
  535. return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:NSUTF8StringEncoding];
  536. }
  537. @interface IronApplication : NSApplication {
  538. }
  539. - (void)terminate:(id)sender;
  540. @end
  541. @interface IronAppDelegate : NSObject <NSWindowDelegate> {
  542. }
  543. - (void)windowWillClose:(NSNotification *)notification;
  544. - (void)windowDidResize:(NSNotification *)notification;
  545. - (void)windowWillMiniaturize:(NSNotification *)notification;
  546. - (void)windowDidDeminiaturize:(NSNotification *)notification;
  547. - (void)windowDidResignMain:(NSNotification *)notification;
  548. - (void)windowDidBecomeMain:(NSNotification *)notification;
  549. @end
  550. static IronAppDelegate *delegate;
  551. CAMetalLayer *get_metal_layer(void) {
  552. return [view metalLayer];
  553. }
  554. id get_metal_device(void) {
  555. return [view metalDevice];
  556. }
  557. id get_metal_queue(void) {
  558. return [view metalQueue];
  559. }
  560. bool iron_internal_handle_messages(void) {
  561. NSEvent *event = [myapp nextEventMatchingMask:NSAnyEventMask
  562. untilDate:[NSDate distantPast]
  563. inMode:NSDefaultRunLoopMode
  564. dequeue:YES]; // distantPast: non-blocking
  565. if (event != nil) {
  566. [myapp sendEvent:event];
  567. [myapp updateWindows];
  568. }
  569. // Sleep for a frame to limit the calls when the window is not visible.
  570. if (!window.visible) {
  571. [NSThread sleepForTimeInterval:1.0 / 60];
  572. }
  573. return true;
  574. }
  575. static void createWindow(iron_window_options_t *options) {
  576. int width = options->width / [[NSScreen mainScreen] backingScaleFactor];
  577. int height = options->height / [[NSScreen mainScreen] backingScaleFactor];
  578. int styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable;
  579. if ((options->features & IRON_WINDOW_FEATURE_RESIZEABLE) || (options->features & IRON_WINDOW_FEATURE_MAXIMIZABLE)) {
  580. styleMask |= NSWindowStyleMaskResizable;
  581. }
  582. if (options->features & IRON_WINDOW_FEATURE_MINIMIZABLE) {
  583. styleMask |= NSWindowStyleMaskMiniaturizable;
  584. }
  585. view = [[BasicMTKView alloc] initWithFrame:NSMakeRect(0, 0, width, height)];
  586. [view registerForDraggedTypes:[NSArray arrayWithObjects:NSURLPboardType, nil]];
  587. window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height) styleMask:styleMask backing:NSBackingStoreBuffered defer:TRUE];
  588. delegate = [IronAppDelegate alloc];
  589. [window setDelegate:delegate];
  590. [window setTitle:[NSString stringWithCString:options->title encoding:NSUTF8StringEncoding]];
  591. [window setAcceptsMouseMovedEvents:YES];
  592. [[window contentView] addSubview:view];
  593. [window center];
  594. windows[0].handle = window;
  595. windows[0].view = view;
  596. [window makeKeyAndOrderFront:nil];
  597. if (options->mode == IRON_WINDOW_MODE_FULLSCREEN) {
  598. [window toggleFullScreen:nil];
  599. windows[0].fullscreen = true;
  600. }
  601. }
  602. void iron_window_change_window_mode(iron_window_mode_t mode) {
  603. switch (mode) {
  604. case IRON_WINDOW_MODE_WINDOW:
  605. if (windows[0].fullscreen) {
  606. [window toggleFullScreen:nil];
  607. windows[0].fullscreen = false;
  608. }
  609. break;
  610. case IRON_WINDOW_MODE_FULLSCREEN:
  611. if (!windows[0].fullscreen) {
  612. [window toggleFullScreen:nil];
  613. windows[0].fullscreen = true;
  614. }
  615. break;
  616. }
  617. }
  618. void iron_window_set_close_callback(bool (*callback)(void *), void *data) {
  619. windows[0].closeCallback = callback;
  620. windows[0].closeCallbackData = data;
  621. }
  622. static void add_menubar(void) {
  623. NSString *appName = [[NSProcessInfo processInfo] processName];
  624. NSMenu *appMenu = [NSMenu new];
  625. NSString *quitTitle = [@"Quit " stringByAppendingString:appName];
  626. NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"];
  627. [appMenu addItem:quitMenuItem];
  628. NSMenuItem *appMenuItem = [NSMenuItem new];
  629. [appMenuItem setSubmenu:appMenu];
  630. NSMenu *menubar = [NSMenu new];
  631. [menubar addItem:appMenuItem];
  632. [NSApp setMainMenu:menubar];
  633. }
  634. void iron_init(iron_window_options_t *win) {
  635. @autoreleasepool {
  636. myapp = [IronApplication sharedApplication];
  637. [myapp finishLaunching];
  638. [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
  639. NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
  640. add_menubar();
  641. #ifdef WITH_GAMEPAD
  642. hidManager = (struct HIDManager *)malloc(sizeof(struct HIDManager));
  643. HIDManager_init(hidManager);
  644. #endif
  645. }
  646. createWindow(win);
  647. gpu_init(win->depth_bits, true);
  648. }
  649. int iron_window_width() {
  650. NSWindow *window = windows[0].handle;
  651. float scale = [window backingScaleFactor];
  652. return [[window contentView] frame].size.width * scale;
  653. }
  654. int iron_window_height() {
  655. NSWindow *window = windows[0].handle;
  656. float scale = [window backingScaleFactor];
  657. return [[window contentView] frame].size.height * scale;
  658. }
  659. void iron_load_url(const char *url) {
  660. [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]];
  661. }
  662. const char *iron_language(void) {
  663. NSString *nsstr = [[NSLocale preferredLanguages] objectAtIndex:0];
  664. const char *lang = [nsstr UTF8String];
  665. language[0] = lang[0];
  666. language[1] = lang[1];
  667. language[2] = 0;
  668. return language;
  669. }
  670. void iron_internal_shutdown(void) {}
  671. const char *iron_internal_save_path(void) {
  672. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  673. NSString *resolvedPath = [paths objectAtIndex:0];
  674. NSString *appName = [NSString stringWithUTF8String:iron_application_name()];
  675. resolvedPath = [resolvedPath stringByAppendingPathComponent:appName];
  676. NSFileManager *fileMgr = [[NSFileManager alloc] init];
  677. NSError *error;
  678. [fileMgr createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];
  679. resolvedPath = [resolvedPath stringByAppendingString:@"/"];
  680. return [resolvedPath cStringUsingEncoding:NSUTF8StringEncoding];
  681. }
  682. #ifndef IRON_NO_MAIN
  683. int main(int argc, char **argv) {
  684. return kickstart(argc, argv);
  685. }
  686. #endif
  687. @implementation IronApplication
  688. - (void)terminate:(id)sender {
  689. iron_stop();
  690. }
  691. @end
  692. @implementation IronAppDelegate
  693. - (BOOL)windowShouldClose:(NSWindow *)sender {
  694. if (windows[0].closeCallback != NULL) {
  695. if (windows[0].closeCallback(windows[0].closeCallbackData)) {
  696. return YES;
  697. }
  698. else {
  699. return NO;
  700. }
  701. }
  702. return YES;
  703. }
  704. - (void)windowWillClose:(NSNotification *)notification {
  705. iron_stop();
  706. }
  707. void iron_internal_call_resize_callback(int width, int height) {
  708. if (windows[0].resizeCallback != NULL) {
  709. windows[0].resizeCallback(width, height, windows[0].resizeCallbackData);
  710. }
  711. }
  712. - (void)windowDidResize:(NSNotification *)notification {
  713. NSWindow *window = [notification object];
  714. NSSize size = [[window contentView] frame].size;
  715. [view resize:size];
  716. float scale = [window backingScaleFactor];
  717. int w = size.width * scale;
  718. int h = size.height * scale;
  719. gpu_resize(w, h);
  720. iron_internal_call_resize_callback(w, h);
  721. }
  722. - (void)windowWillMiniaturize:(NSNotification *)notification {
  723. iron_internal_background_callback();
  724. }
  725. - (void)windowDidDeminiaturize:(NSNotification *)notification {
  726. iron_internal_foreground_callback();
  727. }
  728. - (void)windowDidResignMain:(NSNotification *)notification {
  729. iron_internal_pause_callback();
  730. }
  731. - (void)windowDidBecomeMain:(NSNotification *)notification {
  732. iron_internal_resume_callback();
  733. }
  734. @end
  735. int iron_window_x() {
  736. return 0;
  737. }
  738. int iron_window_y() {
  739. return 0;
  740. }
  741. void iron_window_resize(int width, int height) {}
  742. void iron_window_move(int x, int y) {}
  743. void iron_window_change_mode(iron_window_mode_t mode) {}
  744. void iron_window_destroy() {}
  745. void iron_window_show() {}
  746. void iron_window_hide() {}
  747. void iron_window_create(iron_window_options_t *win) {}
  748. void iron_window_set_title(const char *title) {
  749. NSWindow *window = windows[0].handle;
  750. [window setTitle:[NSString stringWithUTF8String:title]];
  751. }
  752. void iron_window_set_resize_callback(void (*callback)(int x, int y, void *data), void *data) {
  753. windows[0].resizeCallback = callback;
  754. windows[0].resizeCallbackData = data;
  755. }
  756. iron_window_mode_t iron_window_get_mode() {
  757. return IRON_WINDOW_MODE_WINDOW;
  758. }
  759. int iron_window_display() {
  760. return 0;
  761. }
  762. #ifdef WITH_GAMEPAD
  763. static struct HIDManager *hidManager;
  764. bool iron_gamepad_connected(int num) {
  765. return true;
  766. }
  767. void iron_gamepad_rumble(int gamepad, float left, float right) {}
  768. static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef);
  769. static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender);
  770. static void reset(struct HIDGamepad *gamepad);
  771. static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements);
  772. static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex);
  773. static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex);
  774. static void cstringFromCFStringRef(CFStringRef string, char *cstr, size_t clen) {
  775. cstr[0] = '\0';
  776. if (string != NULL) {
  777. char temp[256];
  778. if (CFStringGetCString(string, temp, 256, kCFStringEncodingUTF8)) {
  779. temp[iron_mini(255, (int)(clen - 1))] = '\0';
  780. strncpy(cstr, temp, clen);
  781. }
  782. }
  783. }
  784. void HIDGamepad_init(struct HIDGamepad *gamepad) {
  785. reset(gamepad);
  786. }
  787. void HIDGamepad_destroy(struct HIDGamepad *gamepad) {
  788. HIDGamepad_unbind(gamepad);
  789. }
  790. void HIDGamepad_bind(struct HIDGamepad *gamepad, IOHIDDeviceRef inDeviceRef, int inPadIndex) {
  791. gamepad->hidDeviceRef = inDeviceRef;
  792. gamepad->padIndex = inPadIndex;
  793. IOHIDDeviceOpen(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
  794. IOHIDDeviceRegisterInputValueCallback(gamepad->hidDeviceRef, inputValueCallback, gamepad);
  795. IOHIDDeviceScheduleWithRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  796. gamepad->hidQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, gamepad->hidDeviceRef, 32, kIOHIDOptionsTypeNone);
  797. if (CFGetTypeID(gamepad->hidQueueRef) == IOHIDQueueGetTypeID()) {
  798. IOHIDQueueStart(gamepad->hidQueueRef);
  799. IOHIDQueueRegisterValueAvailableCallback(gamepad->hidQueueRef, valueAvailableCallback, gamepad);
  800. IOHIDQueueScheduleWithRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  801. }
  802. CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(gamepad->hidDeviceRef, NULL, kIOHIDOptionsTypeNone);
  803. initDeviceElements(gamepad, elementCFArrayRef);
  804. {
  805. CFNumberRef vendorIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDVendorIDKey));
  806. CFNumberGetValue(vendorIdRef, kCFNumberIntType, &gamepad->hidDeviceVendorID);
  807. CFNumberRef productIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductIDKey));
  808. CFNumberGetValue(productIdRef, kCFNumberIntType, &gamepad->hidDeviceProductID);
  809. CFStringRef vendorRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDManufacturerKey));
  810. cstringFromCFStringRef(vendorRef, gamepad->hidDeviceVendor, sizeof(gamepad->hidDeviceVendor));
  811. CFStringRef productRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductKey));
  812. cstringFromCFStringRef(productRef, gamepad->hidDeviceProduct, sizeof(gamepad->hidDeviceProduct));
  813. }
  814. }
  815. static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements) {
  816. for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) {
  817. IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
  818. IOHIDElementType elemType = IOHIDElementGetType(elementRef);
  819. IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
  820. uint32_t usagePage = IOHIDElementGetUsagePage(elementRef);
  821. uint32_t usage = IOHIDElementGetUsage(elementRef);
  822. // Match up items
  823. switch (usagePage) {
  824. case kHIDPage_GenericDesktop:
  825. switch (usage) {
  826. case kHIDUsage_GD_X: // Left stick X
  827. gamepad->axis[0] = cookie;
  828. break;
  829. case kHIDUsage_GD_Y: // Left stick Y
  830. gamepad->axis[1] = cookie;
  831. break;
  832. case kHIDUsage_GD_Z: // Left trigger
  833. gamepad->axis[4] = cookie;
  834. break;
  835. case kHIDUsage_GD_Rx: // Right stick X
  836. gamepad->axis[2] = cookie;
  837. break;
  838. case kHIDUsage_GD_Ry: // Right stick Y
  839. gamepad->axis[3] = cookie;
  840. break;
  841. case kHIDUsage_GD_Rz: // Right trigger
  842. gamepad->axis[5] = cookie;
  843. break;
  844. case kHIDUsage_GD_Hatswitch:
  845. break;
  846. default:
  847. break;
  848. }
  849. break;
  850. case kHIDPage_Button:
  851. if ((usage >= 1) && (usage <= 15)) {
  852. // Button 1-11
  853. gamepad->buttons[usage - 1] = cookie;
  854. }
  855. break;
  856. default:
  857. break;
  858. }
  859. if (elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis) {
  860. if (!IOHIDQueueContainsElement(gamepad->hidQueueRef, elementRef))
  861. IOHIDQueueAddElement(gamepad->hidQueueRef, elementRef);
  862. }
  863. }
  864. }
  865. void HIDGamepad_unbind(struct HIDGamepad *gamepad) {
  866. if (gamepad->hidQueueRef) {
  867. IOHIDQueueStop(gamepad->hidQueueRef);
  868. IOHIDQueueUnscheduleFromRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  869. }
  870. if (gamepad->hidDeviceRef) {
  871. IOHIDDeviceUnscheduleFromRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  872. IOHIDDeviceClose(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice);
  873. }
  874. reset(gamepad);
  875. }
  876. static void reset(struct HIDGamepad *gamepad) {
  877. gamepad->padIndex = -1;
  878. gamepad->hidDeviceRef = NULL;
  879. gamepad->hidQueueRef = NULL;
  880. gamepad->hidDeviceVendor[0] = '\0';
  881. gamepad->hidDeviceProduct[0] = '\0';
  882. gamepad->hidDeviceVendorID = 0;
  883. gamepad->hidDeviceProductID = 0;
  884. memset(gamepad->axis, 0, sizeof(gamepad->axis));
  885. memset(gamepad->buttons, 0, sizeof(gamepad->buttons));
  886. }
  887. static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex) {
  888. double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
  889. double min = IOHIDElementGetLogicalMin(elementRef);
  890. double max = IOHIDElementGetLogicalMax(elementRef);
  891. double normalize = (rawValue - min) / (max - min);
  892. iron_internal_gamepad_trigger_button(gamepad->padIndex, buttonIndex, normalize);
  893. }
  894. static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex) {
  895. double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical);
  896. double min = IOHIDElementGetPhysicalMin(elementRef);
  897. double max = IOHIDElementGetPhysicalMax(elementRef);
  898. double normalize = normalize = (((rawValue - min) / (max - min)) * 2) - 1;
  899. if (axisIndex % 2 == 1) {
  900. normalize = -normalize;
  901. }
  902. iron_internal_gamepad_trigger_axis(gamepad->padIndex, axisIndex, normalize);
  903. }
  904. static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef) {}
  905. static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender) {
  906. struct HIDGamepad *pad = (struct HIDGamepad *)inContext;
  907. do {
  908. IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout((IOHIDQueueRef)inSender, 0.);
  909. if (!valueRef) {
  910. break;
  911. }
  912. IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef);
  913. IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef);
  914. for (int i = 0, c = sizeof(pad->buttons); i < c; ++i) {
  915. if (cookie == pad->buttons[i]) {
  916. buttonChanged(pad, elementRef, valueRef, i);
  917. break;
  918. }
  919. }
  920. for (int i = 0, c = sizeof(pad->axis); i < c; ++i) {
  921. if (cookie == pad->axis[i]) {
  922. axisChanged(pad, elementRef, valueRef, i);
  923. break;
  924. }
  925. }
  926. CFRelease(valueRef);
  927. } while (1);
  928. }
  929. const char *iron_gamepad_vendor(int gamepad) {
  930. return "unknown";
  931. }
  932. const char *iron_gamepad_product_name(int gamepad) {
  933. return "unknown";
  934. }
  935. static int initHIDManager(struct HIDManager *manager);
  936. static bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef);
  937. static CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage);
  938. static void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
  939. static void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef);
  940. void HIDManager_init(struct HIDManager *manager) {
  941. manager->managerRef = 0x0;
  942. initHIDManager(manager);
  943. }
  944. void HIDManager_destroy(struct HIDManager *manager) {
  945. if (manager->managerRef) {
  946. IOHIDManagerUnscheduleFromRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  947. IOHIDManagerClose(manager->managerRef, kIOHIDOptionsTypeNone);
  948. }
  949. }
  950. static int initHIDManager(struct HIDManager *manager) {
  951. manager->managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
  952. if (CFGetTypeID(manager->managerRef) == IOHIDManagerGetTypeID()) {
  953. CFMutableArrayRef matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
  954. if (matchingCFArrayRef) {
  955. CFDictionaryRef matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
  956. addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
  957. matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
  958. addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef);
  959. }
  960. else {
  961. iron_error("%s: CFArrayCreateMutable failed.", __PRETTY_FUNCTION__);
  962. return -1;
  963. }
  964. IOHIDManagerSetDeviceMatchingMultiple(manager->managerRef, matchingCFArrayRef);
  965. CFRelease(matchingCFArrayRef);
  966. IOHIDManagerOpen(manager->managerRef, kIOHIDOptionsTypeNone);
  967. IOHIDManagerRegisterDeviceMatchingCallback(manager->managerRef, deviceConnected, manager);
  968. IOHIDManagerRegisterDeviceRemovalCallback(manager->managerRef, deviceRemoved, manager);
  969. IOHIDManagerScheduleWithRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  970. return 0;
  971. }
  972. return -1;
  973. }
  974. bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef) {
  975. if (matchingCFDictRef) {
  976. CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef);
  977. CFRelease(matchingCFDictRef);
  978. return true;
  979. }
  980. return false;
  981. }
  982. CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage) {
  983. CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  984. if (result) {
  985. if (inUsagePage) {
  986. CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage);
  987. if (pageCFNumberRef) {
  988. CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsagePageKey), pageCFNumberRef);
  989. CFRelease(pageCFNumberRef);
  990. if (inUsage) {
  991. CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage);
  992. if (usageCFNumberRef) {
  993. CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef);
  994. CFRelease(usageCFNumberRef);
  995. }
  996. }
  997. }
  998. }
  999. }
  1000. return result;
  1001. }
  1002. void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
  1003. struct HIDManager *manager = (struct HIDManager *)inContext;
  1004. struct HIDManagerDeviceRecord *device = &manager->devices[0];
  1005. for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
  1006. if (!device->connected) {
  1007. device->connected = true;
  1008. device->device = inIOHIDDeviceRef;
  1009. HIDGamepad_bind(&device->pad, inIOHIDDeviceRef, i);
  1010. break;
  1011. }
  1012. }
  1013. }
  1014. void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) {
  1015. struct HIDManager *manager = (struct HIDManager *)inContext;
  1016. struct HIDManagerDeviceRecord *device = &manager->devices[0];
  1017. for (int i = 0; i < IRON_MAX_HID_DEVICES; ++i, ++device) {
  1018. if (device->connected && device->device == inIOHIDDeviceRef) {
  1019. device->connected = false;
  1020. device->device = NULL;
  1021. HIDGamepad_unbind(&device->pad);
  1022. break;
  1023. }
  1024. }
  1025. }
  1026. #endif
  1027. extern void (*iron_save_and_quit)(bool);
  1028. bool _save_and_quit_callback_internal() {
  1029. bool save = false;
  1030. NSString *title = [window title];
  1031. bool dirty = [title rangeOfString:@"* - ArmorPaint"].location != NSNotFound;
  1032. if (dirty) {
  1033. NSAlert *alert = [[NSAlert alloc] init];
  1034. [alert setMessageText:@"Project has been modified, save changes?"];
  1035. [alert addButtonWithTitle:@"Yes"];
  1036. [alert addButtonWithTitle:@"Cancel"];
  1037. [alert addButtonWithTitle:@"No"];
  1038. [alert setAlertStyle:NSAlertStyleWarning];
  1039. NSInteger res = [alert runModal];
  1040. if (res == NSAlertFirstButtonReturn) {
  1041. save = true;
  1042. }
  1043. else if (res == NSAlertThirdButtonReturn) {
  1044. save = false;
  1045. }
  1046. else { // Cancel
  1047. return false;
  1048. }
  1049. }
  1050. iron_save_and_quit(save);
  1051. return false;
  1052. }
  1053. volatile int iron_exec_async_done = 1;
  1054. void iron_exec_async(const char *path, char *argv[]) {
  1055. iron_exec_async_done = 0;
  1056. NSTask *task = [[NSTask alloc] init];
  1057. [task setLaunchPath:[NSString stringWithUTF8String:path]];
  1058. NSMutableArray *args = [NSMutableArray array];
  1059. int i = 1;
  1060. while (argv[i] != NULL) {
  1061. [args addObject:[NSString stringWithUTF8String:argv[i]]];
  1062. i++;
  1063. }
  1064. [task setArguments:args];
  1065. task.terminationHandler = ^(NSTask *t){
  1066. iron_exec_async_done = 1;
  1067. };
  1068. [task launch];
  1069. }